We launched new forums in March 2019—join us there. In a hurry for help with your website? Get Help Now!
    • 3316
    • 28 Posts
    I am trying to do some advanced filtering with Ditto by following this very handy tutorial: http://www.kunane.com/modx/ditto-multiple-filters.html

    Here's my project page: http://twj.publishpath.net/reviews

    Everything is working wonderfully except for the fact that the "All Dietary Needs" dropdown filter is no longer a single choice drop down list TV, but a "check as many as apply" TV.

    I found this comment on the same tutorial that provided code for such a need as mine (Julie's comment, reply #26) - http://www.kunane.com/modx/ditto-multiple-filters.html?jn0d94047b=2#jotnav0d94047bf9aa19386213f66408092ba5

    However, when I insert the code into my snippet, I get an invalid argument supplied for foreach() error. Here's the code in my ditto filter snippet:

    <?php
    global $modx;
    	$filter = "";
    	if (!empty($_GET['prCompany'])) $filter .= (empty($filter)?"":"|")."tvprCompany,".mysql_escape_string($_GET['prCompany']).",1";
    	if (!empty($_GET['prCategory'])) $filter .= (empty($filter)?"":"|")."tvprCategory,".mysql_escape_string($_GET['prCategory']) .",1";
    	if (!empty($_GET['prRating'])) $filter .= (empty($filter)?"":"|")."tvprRating,".mysql_escape_string($_GET['prRating']) .",1";
        if (!empty($_GET['prDiets'])) {
    		   foreach($_GET['prDiets'] as $key=> $value) {
    		   // loop thru and build the filter for ditto
    		   $filter .= 'prDiets,' . $value . ',7|';
    		   }
    		   // remove the last pipe symbol "|" from the string
    		   $filter = substr($filter,0,-1);
    		   }
    	return $modx->runSnippet('Ditto', array('parents'=>'270', 'tpl'=>'productReviewList', 'display'=>'5', 'sortBy'=>'pub_date', 'dateSource'=>'pub_date', 'dateFormat'=>'%d.%m.%Y', 'filter'=>$filter, 'paginate'=>'1', 'noResults'=>'There are no products matching your selections. Please try different criteria.'));
    ?>


    I've tinkered enough with it to know that the error is somewhere in the series of lines that start with if (!empty($_GET['prDiets'])) { but I just don't know much about PHP so this has really stumped me.

    Do you see any errors in the above code that could be causing this problem? Many Thanks! [ed. note: bigbuddy last edited this post 14 years, 11 months ago.]
      • 3316
      • 28 Posts
      Ok I really need an answer for this so the first person that posts a solution will get $25. I'll send it to your paypal email address as soon as I confirm your solution works. Thanks!
        • 16278
        • 928 Posts
        The SELECT code on the project page's Dietary Needs dropdown is still for single selection, and will return a scalar value, not an array as exepected by foreach. To make it return an array of the selected values, you need to add multiple to the attributes and empty square brackets after the name:
        <select multiple name="prDiets[]">
            <option value="">All Dietary Needs</option>
            <option value="None">None</option>
            <option value="Soy Free">Soy Free</option>
            <option value="Vegan">Vegan</option>
            <option value="Dairy Free">Dairy Free</option>
            <option value="Low Glycemic">Low Glycemic</option>
            <option value="Gluten Free">Gluten Free</option>
        </select>

        ;) KP
          • 3316
          • 28 Posts
          Hi KP,

          Thanks for the help. I made some progress, but now I get the following error when I select a dietary need (ie Soy Free) that should return a product:

          PHP error debug
            Error:	htmlspecialchars() expects parameter 1 to be string, array given	 
            Error type/ Nr.:	Warning - 2	 
            File:	/var/www/clients/client3/web119/web/assets/snippets/ditto/classes/ditto.class.inc.php	 
            Line:	1044	 
            Line 1044 source:	 $query[htmlspecialchars($param, ENT_QUOTES)] = htmlspecialchars($value, ENT_QUOTES);


          If I choose a dietary need for which there is no product (ie Gluten Free), I just get no results, which is correct.
            • 16278
            • 928 Posts
            I don't get that problem with a test set based on your snippet code above and using a multiple listbox TV prDiets with options
            All Dietary Needs==||None==None||Soy Free||Vegan||Dairy Free||Low Glycemic||Gluten Free


            Try inserting
            return print_r($filter, true); 
            as a line before your existing return , to see what's being fed to Ditto.

            I can see a problem with the way you are building the filter string. The Company, Category and ratings output is being added with a bar at the start if required. Your multiselect is adding a bar afterwards and removing the surplus one. So select one Category and one Dietary Need, and you have two filters running into each other with no bar between. You could use the same method of adding a bar at the start when adding to a nonempty string in your loop (also I've found you need to test for an empty element zero in the array, rather than an empty array):
            if (!empty($_GET['prDiets'][0])) {
            	           foreach($_GET['prDiets'] as $key=> $value) {
            	           // loop thru and build the filter for ditto
            	           $filter .= (empty($filter)?"":"|") . 'prDiets,' . $value . ',7';
            	           }
                       }


            or use an array to build the set of filters and implode them with a bar (much more respectable, IMHO):
            <?php
            global $modx;
                $filter = array();
            
                if (!empty($_GET['prCompany'])) { 
                	$filter[] = "tvprCompany," . mysql_escape_string($_GET['prCompany']) . ",1";
            	}
                if (!empty($_GET['prCategory'])) {
                	$filter[] = "tvprCategory," . mysql_escape_string($_GET['prCategory']) . ",1";
            	}
                if (!empty($_GET['prRating'])) {
                	$filter[] = "tvprRating," . mysql_escape_string($_GET['prRating']) . ",1";
            	}
            // have to check element 0 of the prDiets array for empty, not the array itself
                if (!empty($_GET['prDiets'][0])) {
                       foreach($_GET['prDiets'] as $key=> $value) {
            	           $filter[] = 'prDiets,' . $value . ',7';
                       }
                }
            
            	$filter = implode('|', $filter);
            
            	$output = $modx->runSnippet('Ditto', array( 'tpl'=>'productReviewList', 'filter'=>$filter, 'noResults'=>'There are no products matching your selections. Please try different criteria.'));
            
            	 return $output;
            ?>


            And a couple of logic problems: isn't "None" the same as "no selection"? That is, you need to give it a value of empty, unless you want to include "None" as a feature of every product's Dietary Needs. And if you select a three-star rating, don't you want to include items with a higher rating? (I think that make the comparison mode 3 rather than 1 for the rating filter).

            :) KP
              • 3316
              • 28 Posts
              Thanks KP. We're getting VERY close. It's now successfully filtering when only one dietary need is chosen. But when I choose None AND Soy Free I am getting no documents returned and I should be getting documents that match both soy free and none.

              To answer your question, None is a dietary need because if there is a product that is not soy free, gluten free, etc, then it will get a distinction of none...good question though.

              Your code is working great, but to be honest, I don't understand it all, but that's okay, the main goal is to get it work. Sorry I do not have any working knowledge of PHP (but I will learn some day).

              Your point is well taken about the star rating. Do you have any ideas on updating the code that would make it work?

              Thanks so much for your help!

              P.S. PM me your paypal email address so I can send you the $25 USD. Thanks!

                • 3316
                • 28 Posts
                Actually, on second thought, I just changed the Dietary Needs menu to a single select and I think that works better and makes much more sense. It seems to be functioning perfectly. The other thing we could do is improve the star rating to take that rating and anything above it like you suggested. Be sure to PM me your PayPal email address. Thanks!
                  • 16278
                  • 928 Posts
                  Yes, there are some tricky issues with multiple selection lists. There are some neat Javascript methods to transform a multiple select into a set of checkboxes, since it's not workable to collapse a standard multiple select into the same height as the single selects.

                  For the ratings, the list, updated to produce numerical values, will look like this:
                  <select name="prRating">
                      <option value="0">All Ratings</option>
                      <option value="0.5">Half Star</option>
                      <option value="1">1 Star</option>
                      <option value="1.5">1.5 Stars</option>
                      <option value="2">2 Stars</option>
                      <option value="2.5">2.5 Stars</option>
                      <option value="3">3 Stars</option>
                      <option value="3.5">3.5 Stars</option>
                      <option value="4">4 Stars</option>
                      <option value="4.5">4.5 Stars</option>
                      <option value="5">5 Stars</option>
                  </select>
                  


                  The matching TV prRatings:
                  All Ratings==0||Half Star==0.5||1 Star==1||1.5 Stars==1.5||2 Stars==2||2.5 Stars==2.5||3 Stars==3||3.5Stars==3.5||4 Stars==4||4.5 Stars==4.5||5 Stars==5


                  And the PHP, to use comparison mode 3 in the filter ("exclude docs with value less than the criterion")
                      if (!empty($_GET['prRating'])) {
                      	$filter[] = "tvprRating," . mysql_escape_string($_GET['prRating']) . ",3";
                  	} 
                  


                  :) KP
                    • 3316
                    • 28 Posts
                    This makes sense. Except I was using the TV values as CSS classes to format the star ratings. I used a transparent image of all 5 stars than a blue background image rectangle to fill in only the stars needed for each product (using background-position: negative number).

                    I'm sure there is another way to do it using the TV values you have...I'll have to think about it, but this makes a lot of sense for the drop down filter on the front end. Figuring out how to display the stars should the the easy part. Thanks!
                      • 3316
                      • 28 Posts
                      I got this working beautifully now. Thanks!

                      http://twj.publishpath.net/reviews