Jump to content
cboetens

How to create product filters?

Recommended Posts

Hi guys

I guess this is the right forum to post my question. If not, do let me know. Ok, I'm fairly new to ProcessWire and so far I really like the CMS! It's magnificent to use and really straight forward. Big thanks to Ryan for that.

Now, the question I have. I have a template called 'products_categories.php' in which I collect and render all the different categories (the categories basically act as a filter) for a variety of products and next to the categories, I render all the products, like so:

<?php include('includes/header.php') ?>
<?php
$categoryName = $sanitizer->pageName($input->urlSegment(1));
$subcategoryName = $sanitizer->pageName($input->urlSegment(2));

pre($categoryName);
pre($subcategoryName);

$categories = $page->get("template=products_categories");
$category = $categories->get("template=products_categories_item, name=" . $categoryName);

if($category->id){
    $session->category = $category->id;
    $subcategory = $category->get("template=products_categories_item, name=" . $subcategoryName);
    
    if($subcategory->id){
        $session->subcategory = $subcategory->id;
    }
}

$selector = "template=products_item";
if($category->id){
    $selector .= ", products_categories=" . $category;
}

if(isset($subcategory->id)){
    $selector .= ", products_categories=" . $subcategory;
}

$products_items = $page->find($selector);

?>
<div class="uk-section">
    <div class="uk-container">
        <?php if(!empty($page->headline)) { ?>
        <h1><?= $page->headline ?></h1>
        <?php } ?>
        
        
        <!-- **** COMMENT: create grid for products **** --->
        <div class="row">
            
            <div class="col-md-3">
                <div class="uk-card mr-3 uk-card-default uk-card-body">
                    <h3 class="uk-card-title">Productcategorieën</h3>
                    <ul class="uk-list">
                        <?php
                        foreach($categories->children() as $c){
                        ?>
                        <li class="<?= ($category->id===$c->id ? 'active ' : '') ?>">
                            <a href="<?= $page->url . $c->name . '/' ?>"><?= $c->title; ?></a>
                            <?php if($c->hasChildren){ ?>
                            <ul>
                                <?php foreach($c->children() as $sc) {?>
                                <li class="<?= ($category->id==$c->id && $subcategory->id==$sc->id ? 'active ' : '') ?>">
                                    <a href="<?= $page->url . $c->name . '/' . $sc->name . '/' ?>"><?= $sc->title; ?></a>
                                </li>
                                <?php } ?>
                            </ul>
                            <?php } ?>
                        </li>
                        <?php
                        }
                        ?>
                    </ul>
                </div>
            </div>
            
            <div class="col-md-9">
                <div class="uk-child-width-1-3@s uk-grid-match uk-grid-margin-small uk-grid-small" uk-grid>
                    <?php
                    foreach($products_items as $product) {
                    ?>
                    
                    <div class="uk-card">
                        <?php if(isset($product->image)) { ?>
                        <div class="uk-card-media-top">
                            <img src="<?= $product->image->URL; ?>" title="<?= $product->title; ?>" alt="<?= $product->intro; ?>">
                        </div>
                        <?php } ?>
                        <?php if(!empty($product->title) || !empty($product->intro)) {  ?>
                        <div class="uk-card-body uk-card-default">
                            <?php if(!empty($product->title)) { ?>
                            <h3 class="uk-card-title"><?= $product->title; ?></h3>
                            <?php } ?>
                            <?php if(!empty($product->intro)) { ?>
                            <p><?= $product->intro; ?></p>
                            <?php } ?>
                            <?php if (!empty($product->price)): ?>
                            <h3 class="uk-card-title">&euro;&nbsp;<?= $product->price; ?>&nbsp;(excl. btw)</h3>
                            <?php endif; ?>
                            <a class="uk-button uk-button-primary" href="<?= $product->url; ?>">Bekijk</a>
                        </div>
                        <?php } ?>
                    </div>
                    
                    
                    <?php } ?>
                </div>
            </div>
        </div>
    </div>
</div>
<?php include('includes/footer.php'); ?>

Now, I can't seem to get the filter to work. The URL behind every (sub)category should go straight back to the template 'products_categories.php', that way I can get a range of products according to the selected/ clicked URL.

What am I missing here? Is this not the correct way to handle things?

Any help is welcome!

Thanks.

Cédric

Share this post


Link to post
Share on other sites

Hello and welcome to the forum!

Glad you figured it out. Would be nice if you could also share what the problem was, so that others can learn from it.

Was it this line?

1 hour ago, cboetens said:

$products_items = $page->find($selector);

 

Share this post


Link to post
Share on other sites
3 minutes ago, gebeer said:

Hello and welcome to the forum!

Glad you figured it out. Would be nice if you could also share what the problem was, so that others can learn from it.

Was it this line?

 

Hi Gebeer!

Thanks for the kind words! Indirectly that line indeed caused the problem.

If you have a look at the two if-statements just above this line, you will see that I don't check if the values "$category->id" and "$subcategory->id" are set or not and if they are empty or not. With the faulty code the variable "$selector" in the line of code that you mentioned above is empty.

That way, the products are not being loaded when a category is selected. This was problem 1.

I've made a second adjustment. In my code I have renamed the template "products_categories.php" to "products.php". As an alternate template name for "products_categories.php" I have set "products.php". I don't really know if an alternate file name is necessary tough?

That way I can easily grab a category and select all products linking to that category, I guess?

The code that resolved my problem was the following:

if(isset($category->id) && !empty($category->id)){
    $selector .= ", products_categories=" . $category;
}

if(isset($subcategory->id) && !empty($subcategory->id)){
    $selector .= ", products_categories=" . $subcategory;
}

Feel free to post any feedback. It's an interesting subject.

Greetings.

  • Like 2

Share this post


Link to post
Share on other sites

Thanks for posting the solution to your problem!

16 minutes ago, cboetens said:

if(isset($category->id) && !empty($category->id)){

To test if a full page object was returned by $pages->get() you can use
 

if($category->id) {...}

This is usually enough. When PW doesn't find a page it returns an object NullPage. This NullPage object returns null for id. So the simple test above is sufficient.

Since you get your category and subcategory names from the urlSegments, you could test earlier in your code if these exist. If there is no urlSegment(1),

$categoryName = $sanitizer->pageName($input->urlSegment(1));

will return an empty string. So you could test for that.

I can recommend to start early on using the fabulous debugger module TracyDebugger. It makes dev life easier and can save a lot of time. I use it on every project. Also it is always goo to have the API reference open in a separate tab or browser window 🙂

  • Like 3

Share this post


Link to post
Share on other sites
13 minutes ago, gebeer said:

Thanks for posting the solution to your problem!

To test if a full page object was returned by $pages->get() you can use
 


if($category->id) {...}

This is usually enough. When PW doesn't find a page it returns an object NullPage. This NullPage object returns null for id. So the simple test above is sufficient.

Since you get your category and subcategory names from the urlSegments, you could test earlier in your code if these exist. If there is no urlSegment(1),


$categoryName = $sanitizer->pageName($input->urlSegment(1));

will return an empty string. So you could test for that.

I can recommend to start early on using the fabulous debugger module TracyDebugger. It makes dev life easier and can save a lot of time. I use it on every project. Also it is always goo to have the API reference open in a separate tab or browser window 🙂

Hi gebeer

Thanks for the excellent suggestions! I am going to test these out immediately.

In the meanwhile I've adjusted both of the if-statements. It's the little tips that make a lot of difference to me, thanks for that!

As I'm gaining more and more experience, I am sure I will discover a lot of other small tips.

Thanks again!

  • Like 1

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

  • Recently Browsing   0 members

    No registered users viewing this page.

  • Similar Content

    • By DooM
      Hello,
      I'm working on something like "structure generator" for my own needs which is run after clean installation of PW to create basic structure, fields, install modules, remove unnecessities, etc.
      I'm trying to remove site/templates/scripts and site/templates/styles folders, but I'm out of ideas already. When I try PHP's unlink function, it says that the path doesn't exists, which is obvious, because it will try to delete those folders under FileCompiler folder. This is probably because PW compilation process? I'm initializing this mine "structure generator" in ready.php.
      Do any of you guys have any idea how to get rid of those folders? My styles and scripts folders are separated, so I don't need those two inside templates folder.
      Thanks for every advice. 🙂
    • By MateThemes
      Hello everyone!
      I am trying to add my repeater matrix fields to the search selector, but unfortunately nothing seems to work for me.
      I have following search code in my search.php:
      <?php namespace ProcessWire; // look for a GET variable named 'q' and sanitize it $q = input()->get('q'); // sanitize to text, which removes markup, newlines, too long, etc. $q = sanitizer()->text($q); // did $q have anything in it after sanitizing to text? if($q) { // Make the search query appear in the top-right search box. // Always entity encode any user input that also gets output echo '<input id="search-query" value="' . sanitizer()->entities($q) . '">'; // Sanitize for placement within a selector string. This is important for any // values that you plan to bundle in a selector string like we are doing here. // It quotes them when necessary, and removes characters that might cause issues. $q = sanitizer()->selectorValue($q); // Search the title and body fields for our query text. // Limit the results to 50 pages. The has_parent!=2 excludes irrelevant admin // pages from the search, for when an admin user performs a search. $selector = "title|body~=$q, limit=50, has_parent!=2"; // Find pages that match the selector $matches = pages()->find($selector); } else { $matches = array(); } // unset the variable that we no longer need, since it can contain user input unset($q); ?> <main pw-replace='main'> <?php include('./includes/_pageheadersearch.php'); ?> <div id='content-body' class='uk-section uk-section-large uk-section-large'> <div class='uk-container uk-container-small'> <?php // did we find any matches? if(count($matches)) { // yes we did, render them echo ukAlert(sprintf(_n('Found %d page', 'Found %d pages', $matches->count), $matches->count), "default", "check"); echo ukDescriptionListPages($matches); } else { // we didn't find any echo ukAlert(__('Sorry, no results were found'), "danger", "warning"); } ?> </div> </div> </main> I have tried to add my fields to the selector code (repeater_matrix.aboutsblock_repeaters.mytextfield) . But I didn't get any results.
      What I am doing wrong?
      Thanks for your help!
    • By Robin S
      This module corrects a few things that I find awkward about the "Add New Template" workflow in the PW admin. I opened a wishlist topic a while back because it would good to resolve some of these things in the core, but this module is a stopgap for now.
      Originally I was going to share these as a few standalone hooks, but decided to bundle them together in a configurable module instead.
      Add Template Enhancements
      A module for ProcessWire CMS/CMF. Adds some efficiency enhancements when adding or cloning templates via admin.

      Features
      Derive label from name when new template added: if you like to give each of your templates a label then this feature can save some time. The label can be added automatically when templates are added in admin, in admin/API, or not at all. There are options for underscore/hyphen replacement and capitalisation of the label. Edit template after add: when adding only a single template, the template is automatically opened for editing after it is added. Copy field contexts when cloning: this copies the field contexts (a.k.a. overrides such as column width, label and description) from the source template to the new template when using the "Duplicate/clone this template?" feature on the Advanced tab. Copy field contexts when duplicating fields: this copies the field contexts if you select the "Duplicate fields used by another template" option when adding a new template. Usage
      Install the Add Template Enhancements module.
      Configure the module settings according to what suits you.
       
      https://github.com/Toutouwai/AddTemplateEnhancements
      https://modules.processwire.com/modules/add-template-enhancements/
    • By joe_ma
      On a new pw 3.0.89 installation of mine I've got a problem with role permissions. The setup is like this:
      Templates: home, basic-page, text-only, folder
      All of these have no restrictions in the "family" section and all of them have the same access definitions (see edit-template.jpg below)
      There is a role "redaktor" that has permission to edit, add, delete, move and also clone pages (see permissions.jpg below).
      Now, when logged in as a user with this role, I can only choose from basic-page and folder. The text-only template is not available.


    • By EyeDentify
      I as have been a bit confused for some time about how the "Markup Regions" functionality in Processwire worked.
      But i have know read a bit more and think that i am getting to grips with it. And Markup Regions is going to be huge.

      To aid me in understanding Markup Regions better i started to read the Source code for the new "Regular" theme in conjunction with the Blog about the markup regions.
      It helped me a great deal to understand the basics and more fine details of it.

      A tip is to open both links and use the Source code of the "Regular" theme while reading the blog post.
       
      The Source code:
      https://github.com/processwire/processwire/blob/dev/site-regular/templates/_main.php
      The Blog post:
      https://processwire.com/blog/posts/processwire-3.0.62-and-more-on-markup-regions/
       
      I hope this could help others starting out with markup regions.
      Just take it slow and read it a couple a times and soon you will see the greatness of markup regions.

      /EyeDentify 
×
×
  • Create New...