brawlins4
brawlins4

Reputation: 322

Filter Search Results from a Dropdown in PHP

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/[email protected]/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>

Upvotes: 2

Views: 1945

Answers (1)

Jason
Jason

Reputation: 556

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).

Upvotes: 3

Related Questions