2

I have built a search (https://brawlins.com/oer/index.php) and I want to enable user to be able to select filters to limit the search results after their initial search. I have the filters populated in a dropdown menu on my search page (https://brawlins.com/oer/search.php?term=) but I am not sure how to filter down the results once a user clicks on them. I want the users to be able to select multiple options as well. I create a class to store the filters. Below is the code from my filter class:

    <?php
class filterContentProvider {

    private $conn;

    public function __construct($conn) {
        $this->conn = $conn;
    }

    public function getType ($conn) {

        $query = $this->conn->prepare("SELECT type, COUNT(*) as total FROM oer_search GROUP BY type");
        $query->execute();

        $filterHTML .= "<h3 class='filterTitle'>Type</h3>";  
        $filterHTML .= "<div class'dropdown'>";
        $filterHTML .= "<button class='btn btn-filter dropdown-toggle' type='button' id='dropdownMenuButton' data-toggle='dropdown' aria-haspopup='true' aria-expanded='false'>Select - Type</button>";
        $filterHTML .= "<div class='dropdown-menu scrollable-menu' aria-labelledby='dropdownMenuButton'>";

        //$filterHTML .= "<option value=''>- Type -</option>"; 

        while($row = $query->fetch(PDO::FETCH_ASSOC)) {
            $type = $row["type"];
            $total = number_format($row["total"]);
            $filterHTML .= "<a class='dropdown-item' href='search.php?type=$type'>$type ($total)</a>";
        }

        $filterHTML .= "</div>";
        $filterHTML .= "</div>"; //end of dropdown

        return $filterHTML;
    }

    public function getSubject ($conn) {

        $query = $this->conn->prepare("SELECT subject, COUNT(*) as total  FROM oer_search GROUP BY subject");
        $query->execute();

        $filterHTML .= "<h3 class='filterTitle'>Subject</h3>";  
        $filterHTML .= "<div class'dropdown'>";
        $filterHTML .= "<button class='btn btn-filter dropdown-toggle' type='button' id='dropdownMenuButton' data-toggle='dropdown' aria-haspopup='true' aria-expanded='false'>Select - Subject</button>";
        $filterHTML .= "<div class='dropdown-menu scrollable-menu' aria-labelledby='dropdownMenuButton'>";

        while($row = $query->fetch(PDO::FETCH_ASSOC)) {
            $subject = $row["subject"];
            $total = number_format($row["total"]);
            if ($subject != "") {
              $filterHTML .= "<a class='dropdown-item' href='search.php?subject=$subject'>$subject ($total)</a>";  
            }
        }

        $filterHTML .= "</div>";
        $filterHTML .= "</div>"; //end of dropdown

        return $filterHTML;
    }


    public function getLicense ($conn) {

        $query = $this->conn->prepare("SELECT license, COUNT(*) as total FROM oer_search GROUP BY license");
        $query->execute();

        $filterHTML .= "<h3 class='filterTitle'>License</h3>";  
        $filterHTML .= "<div class'dropdown'>";
        $filterHTML .= "<button class='btn btn-filter dropdown-toggle' type='button' id='dropdownMenuButton' data-toggle='dropdown' aria-haspopup='true' aria-expanded='false'>Select - License</button>";
        $filterHTML .= "<div class='dropdown-menu scrollable-menu' aria-labelledby='dropdownMenuButton'>"; 

        while($row = $query->fetch(PDO::FETCH_ASSOC)) {
            $license = $row["license"];
            $total = number_format($row["total"]);
            if ($license != "") {
              $filterHTML .= "<a class='dropdown-item' href='index-test.php?license=$license'>$license ($total)</a>";  
            }
        }

        $filterHTML .= "</div>";
        $filterHTML .= "</div>"; //end of dropdown

        return $filterHTML;
    }

    public function getReviewed ($conn) {

        $query = $this->conn->prepare("SELECT review, COUNT(*) as total FROM oer_search GROUP BY review");
        $query->execute();

        $filterHTML .= "<h3 class='filterTitle'>Reviewed</h3>";  
        $filterHTML .= "<div class'dropdown'>";
        $filterHTML .= "<button class='btn btn-filter dropdown-toggle' type='button' id='dropdownMenuButton' data-toggle='dropdown' aria-haspopup='true' aria-expanded='false'>Select - Reviewed</button>";
        $filterHTML .= "<div class='dropdown-menu scrollable-menu' aria-labelledby='dropdownMenuButton'>"; 

        while($row = $query->fetch(PDO::FETCH_ASSOC)) {
            $review = $row["review"];
            $total = number_format($row["total"]);
            if ($review != "") {
              $filterHTML .= "<a class='dropdown-item' href='index-test.php?review=$review'>$review ($total)</a>";  
            }
        }

        $filterHTML .= "</div>";
        $filterHTML .= "</div>"; //end of dropdown

        return $filterHTML;
    }

    public function getOrigin ($conn) {

        $query = $this->conn->prepare("SELECT source, COUNT(*) as total FROM oer_search GROUP BY source");
        $query->execute();

        $filterHTML .= "<h3 class='filterTitle'>Source</h3>";  
        $filterHTML .= "<div class'dropdown'>";
        $filterHTML .= "<button class='btn btn-filter dropdown-toggle' type='button' id='dropdownMenuButton' data-toggle='dropdown' aria-haspopup='true' aria-expanded='false'>Select - Source</button>";
        $filterHTML .= "<div class='dropdown-menu scrollable-menu' aria-labelledby='dropdownMenuButton'>"; 

        while($row = $query->fetch(PDO::FETCH_ASSOC)) {
            $source = $row["source"];
            $total = number_format($row["total"]);
            if ($source != "") {
              $filterHTML .= "<a class='dropdown-item' href='index-test.php?source=$source'>$source ($total)</a>";  
            }
        }

        $filterHTML .= "</div>";
        $filterHTML .= "</div>"; //end of dropdown

        return $filterHTML;
    }

}

?>

Here is the code I am using on my search.php page. Any help would be much appreciated.

<?php   
include("config.php");
include("classes/siteResultsProvider.php");
include("classes/imageResultsProvider.php");
include("classes/filterContentProvider.php");
include("classes/filterImageProvider.php");

    if(isset($_GET["term"])){
        $term = $_GET["term"];
    }

    if(isset($_GET["collection"])){
        $collection = $_GET["collection"];
    }
    else {
        $collection = "open_content";
    }

    $page = isset($_GET["page"]) ? $_GET["page"] : 1;

?>

<!DOCTYPE html>
<html lang="en">
<head>
    <title>SOAR</title>
<?php
include("header.php");
?>

    <div class="wrapper">
        <div class="header">
            <div class="headerContent">
                <div class="searchContainer">
                    <form action="search.php" method="GET">
                        <div class="searchBarContainer">
                            <input type="hidden" name="collection" value="<?php echo $collection; ?>">
                            <input class="searchBox" type="text" name="term" value="<?php echo htmlspecialchars($term, ENT_QUOTES) ?>" aria-label="search box">
                            <button class="searchButton">
                                <img src="images/search-icon.png" alt="search icon">
                            </button>
                        </div>
                    </form>
                </div>

            </div><!--end of headerContent-->

            <div class="tabsContainer">
                <ul class="tabList">
                    <li class="<?php echo $collection == 'open_content' ? 'active' : '' ?>">
                        <a href='<?php echo "search.php?term=$term&collection=open_content"; ?>'><i class="fas fa-book-open"></i> Open Content</a>
                    </li>
                    <li class="<?php echo $collection == 'images' ? 'active' : '' ?>">
                        <a href='<?php echo "search.php?term=$term&collection=images"; ?>'><i class="fas fa-images"></i> Images</a>
                    </li>
                </ul>
            </div>
        </div><!--end of header-->

<!-------------------------beginning of main section where seach results will display-------------------------->
        <?php
                if($collection == "open_content") {
                    $filters = new filterContentProvider($conn);
                }
                else {
                    $filters = new filterImageProvider($conn);
                }

           if($collection == "open_content") {

                $filterType = $filters->getType($conn);
                $filterSubject = $filters->getSubject($conn);
                $filterOrigin = $filters->getOrigin($conn);
                $fitlerLicense = $filters->getLicense($conn);
                $filterReviewed = $filters->getReviewed($conn);

                echo "<div class='filterContainer'>
                        <div class='filterContent'>
                            <div class='filter'>
                                $filterType
                            </div>
                            <div class='filter'>
                                $filterSubject
                            </div>
                            <div class='filter'>
                                $filterOrigin
                            </div>
                            <div class='filter'>
                                $fitlerLicense
                            </div>
                            <div class='filter'>
                                $filterReviewed
                            </div>
                        </div>
                     </div>"; 
            }
            else {

                $imageFilterOrigin = $filters->getImageOrigin($conn);
                $imageFilterLicense = $filters->getImageLicense($conn);

                echo "<div class='filterContainer'>
                            <div class='filterContent'>
                                <div class='filter'>
                                    $imageFilterOrigin
                                </div>
                                <div class='filter'>
                                    $imageFilterLicense
                                </div>
                            </div>
                         </div>"; 

            }

        ?>

        <div class="mainResultsSection">

            <?php

                if($collection == "open_content") {
                    $resultsProvider = new siteResultsProvider($conn);
                    $pageSize = 25;
                }
                else {
                    $resultsProvider = new imageResultsProvider($conn);
                    $pageSize = 50;
                }  

                $numResults = $resultsProvider->getNumResults($term);

                echo "<p class='resultsCount'>" . number_format($numResults) . " results found</p>";

                echo $resultsProvider->getResultsHTML($page, $pageSize, $term);
            ?>

        </div>

        <div class="paginationContainer">
            <div class="pageButtons">
                <div class="pageNumberContainer">
                    <img src="images/pageStart.png" alt="Start of page image">
                </div>

                <?php
                    $pagesToShow = 10;
                    $numPages = ceil($numResults / $pageSize);
                    $pagesLeft = min($pagesToShow, $numPages);

                    $currentPage = $page - floor($pagesToShow / 2);

                    if($currentPage < 1) {
                        $currentPage = 1;
                    }

                    if($currentPage + $pagesLeft > $numPages + 1) {
                        $currentPage = $numPages + 1  - $pagesLeft;
                    }

                    while($pagesLeft != 0 && $currentPage <= $numPages) {

                        if($currentPage == $page) {
                            echo "<div class='pageNumberContainer'>
                                <img src='images/pageSelected.png' alt='selected page page image'/>
                                <span class='pageNumber'>$currentPage</span>
                                </div>";
                        }
                        else {
                            echo "<div class='pageNumberContainer'>
                                    <a href='search.php?term=$term&collection=$collection&page=$currentPage'>
                                        <img src='images/page.png' alt='pages image'/>
                                        <span class='pageNumber'>$currentPage</span>
                                    </a>
                                </div>";
                        }

                        $currentPage++;
                        $pagesLeft--;

                    }
                ?>

                <div class="pageNumberContainer">
                    <img src="images/pageEnd.png" alt="End of page image">
                </div>
            </div>

        </div><!--end of pagination container-->
    </div><!--end of wrapper-->

    <script src="https://cdn.jsdelivr.net/gh/fancyapps/fancybox@3.5.7/dist/jquery.fancybox.min.js"></script>
    <script src="https://unpkg.com/masonry-layout@4/dist/masonry.pkgd.min.js"></script>
    <script type="text/javascript" src="js/script.js"></script>
</body>
</html>
brawlins4
  • 322
  • 8
  • 22
  • you could add another form that posts back to the same page where you hold the filter selections that runs prior to the main form. Once those are in the query string you can use them as $_GET vars to check what to filter. – Wilco Dec 17 '19 at 23:01
  • With a little bit of parameterization, you could make a single `get...` subroutine that is called 5 times. – Rick James Dec 21 '19 at 01:01
  • Hi Rick, That is where I am running into trouble. I am not sure how to do that. – brawlins4 Dec 21 '19 at 20:41
  • @brawlins4 see the test.php file in my answer – Jason Dec 23 '19 at 07:42
  • you can use Ajax if you like to use I can help you with that? – Aashif Ahamed Dec 26 '19 at 05:20
  • Thanks Aashif. Any help would be much appreciated. I have made some changes to the code for the filters. Instead of using – brawlins4 Dec 26 '19 at 21:38

1 Answers1

3

It seems like you'd want to do a couple of things to get this working.

If you want the results to be asynchronous:

  1. Add an onchange event to you select inputs. (something like onchange="addFilter('type', this.value)") See https://www.w3schools.com/jsref/event_onchange.asp
  2. Have the onchange function (addFilter) send the filters and query string back to your PHP script via an AJAX request. If you're using jQuery, the get method would work for you. https://api.jquery.com/jquery.get/
  3. Pull the new results in PHP using the $_GET parameters you've passed. (Like you are already doing in your script.) You'll want to echo out just the results--not the navigation and other elements of the page.
  4. Update the search results on the page using (something like $('#myContainer').html(results); in jQuery)
  5. Additionally, it would be good to update the URL params in the location bar How do I modify the URL without reloading the page?

Here's a really simple example:

test.php

<?php

// Look up the results here
// You'll probably want to do something more performant that this; it's just a lazy example. :)
// ...
$query = $this->conn->prepare("SELECT type, COUNT(*) as total FROM oer_search WHERE type LIKE :type AND subject LIKE :subject GROUP BY type");
$query->execute([
  'type' => isset($_GET['type'] ? $_GET['type'] : '%',
  'subject' => isset($_GET['subject'] ? $_GET['subject'] : '%',
]);
// ...
?>

index.html

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Document</title>
  <script src="jquery-3.4.1.min.js"></script>
  <script>
    function addFilter(name, value) {
      var url = '/test.php?';
      var urlParams = new URLSearchParams(window.location.search);
      var keys = urlParams.keys();
      for(key of keys) { 
        if (key == name) {
          continue;
        }
        url += key + '=' + urlParams.get(key) + '&';
      }

      url += name + '=' + value;
      window.history.pushState({}, "", );
      $.get(url, function(data) {
        $('#results').html(data);
      });
    }
  </script>
</head>
<body>
  <select onchange="addFilter('type', this.value)">
    <option></option>
    <option>value 1</option>
    <option>value 2</option>
  </select>
  <select onchange="addFilter('subject', this.value)">
    <option></option>
    <option>value 1</option>
    <option>value 2</option>
  </select>
  <div id="results"></div>
</body>
</html>

If you don't care/want the page to reload:

Just make the onchange event submit the form with the search parameters again. If you use jQuery, the submit method would work. Be sure to select the options appropriately in the select inputs, or they'll clear out each time (making it impossible to set more than one).

Jason
  • 556
  • 1
  • 5
  • 21