Jump to content

Product catalog with filter


Erik
 Share

Recommended Posts

I want to build a product catalog where products can be filtered by 5 attributes.

It must be possible to filter on multiple attributes. e.g. size, weight.

Now I am looking for the simplest way to do this in Processwire.

Who will help me on my way?

Link to comment
Share on other sites

What exactly do you need help with? The most common/straightforward way to do this is to grab and sanitize the GET variables from a form submission and use them to create a selector:

<?php 
//Default selector to get ALL products
$selector = "template=products";

// Get color variable and sanitize
$color = $input->get->text('color');

if($color){
  $selector = "template=products, color=$color";
}

$products = $pages->find($selector);

?>
<form action=".">
  <select name="color">
    <option value="red">Red</option>
  </select>
</form>

<div class="products">
  <?php 
    // Render the products list
	foreach($products as $product){
	  echo "<div>....</div>"
  	}
	?>
</div>

 

Link to comment
Share on other sites

1 hour ago, rick said:

Those are beautiful doors

And not just the doors. The guys have more than 1000 lamps of all the electrified ages 😉 They are clients of mine since 2004. The website design is a little bit outdated but the priority is to keep everything rolling... according to the owner 😟

  • Like 2
Link to comment
Share on other sites

Well in this case I'd say each of your products would have a field per category. For example with the door's website:

  • Max Price: Integer field
  • DIN left or right: Select Options or Page Reference field (I'd use page reference)
  • With glass: Checkbox field
  • Width: Integer field
  • Height: Integer field

This would be template door, and child of a unique page with template doors.  So in this scenario, since each product category has a different set of filters, I'd say each product has its own template, so you'd have templates: handles, door locks, door signs, accessories, each of them with its own set of fields. 

So you'd end up with a tree like:

Products
-- Doors
--- Doors 1
--- Doors 2
-- Accesories
--- Acc 1
--- Acc 2

Link to comment
Share on other sites

11 hours ago, elabx said:

Well in this case I'd say each of your products would have a field per category. For example with the door's website:

  • Max Price: Integer field
  • DIN left or right: Select Options or Page Reference field (I'd use page reference)
  • With glass: Checkbox field
  • Width: Integer field
  • Height: Integer field

This would be template door, and child of a unique page with template doors.  So in this scenario, since each product category has a different set of filters, I'd say each product has its own template, so you'd have templates: handles, door locks, door signs, accessories, each of them with its own set of fields. 

So you'd end up with a tree like:

Products
-- Doors
--- Doors 1
--- Doors 2
-- Accesories
--- Acc 1
--- Acc 2

Okay, I understand.

But how do you filter on the front-end?

I installed the skyscraper demo (https://demo.processwire.com/) to see how it works but find it too complex for what I want.

Are there any other examples?

Link to comment
Share on other sites

15 hours ago, elabx said:

So in this scenario, since each product category has a different set of filters, I'd say each product has its own template, so you'd have templates: handles, door locks, door signs, accessories, each of them with its own set of fields. 

Mhh... no. There is only one template for all categories. The editors switch on/off filters how they please with checkboxes, and I check for those in the category template. The editors are quite educated in using this functionality, for example a lamp has no width attributes, or at least it doesn't make sense, and they know it.

Link to comment
Share on other sites

@Erik for the sake of simplicity lets assume you have only a field for price. Your filter would be in a category/listing template, that lists your products(like in my example)

/*
The general outline would be:

1. Check if a filter was set by checking GET-parameter in URL
2. Read the parameter and build your selector for the $pages->find("");
3. Execute the $pages->find with your selector
4. In case no filter was set execute your standard pages->find
5. List the result
*/

// 1. and 2. Check for GET-parameter in your URL
if($input->get->price != ''){
  $price = $sanitizer->int($input->get->price);// in my case price is integer not float
  $selector .= ', price<='.$price;
  $value_price = $price;// save the value for use in the filter input
}
// ...more parameter/filter to check here
// ...


// Very simplified...
if($input->get){
  // 3. execute the find on your products
  $allpages = $pages->find("parent=$page, $selector");
  $log->save('filter', $selector);// write the build selector to a log, so you can check it
}else{
  // 4. on input->get so list all unfiltered pages
  $allpages = $pages->find("parent=$page");
}

// 5. List your products
foreach($allpages as $one){
// your list of products, filtered or not...
}

This would be the first step.

The second step would be to build the form with the filter input field:

// in my case I use delayed output, but you may not...
$filterform .= '<form id="filter" action="'.$page->url.'" method="get">';
$filterform .= "<label for='price'>Max Price</label>";
$filterform .= "<input type='number' name='price' id='price' placeholder='input max price...' value='$value_price'>";
// ...
$content .= $filterform;

The rest is pretty much fine tuning...

  • Like 2
Link to comment
Share on other sites

2 hours ago, Klenkes said:

@Erik for the sake of simplicity lets assume you have only a field for price. Your filter would be in a category/listing template, that lists your products(like in my example)


/*
The general outline would be:

1. Check if a filter was set by checking GET-parameter in URL
2. Read the parameter and build your selector for the $pages->find("");
3. Execute the $pages->find with your selector
4. In case no filter was set execute your standard pages->find
5. List the result
*/

// 1. and 2. Check for GET-parameter in your URL
if($input->get->price != ''){
  $price = $sanitizer->int($input->get->price);// in my case price is integer not float
  $selector .= ', price<='.$price;
  $value_price = $price;// save the value for use in the filter input
}
// ...more parameter/filter to check here
// ...


// Very simplified...
if($input->get){
  // 3. execute the find on your products
  $allpages = $pages->find("parent=$page, $selector");
  $log->save('filter', $selector);// write the build selector to a log, so you can check it
}else{
  // 4. on input->get so list all unfiltered pages
  $allpages = $pages->find("parent=$page");
}

// 5. List your products
foreach($allpages as $one){
// your list of products, filtered or not...
}

This would be the first step.

The second step would be to build the form with the filter input field:


// in my case I use delayed output, but you may not...
$filterform .= '<form id="filter" action="'.$page->url.'" method="get">';
$filterform .= "<label for='price'>Max Price</label>";
$filterform .= "<input type='number' name='price' id='price' placeholder='input max price...' value='$value_price'>";
// ...
$content .= $filterform;

The rest is pretty much fine tuning...

I have already made many (simple) Processwire websites, but never with this functionality.

I have studied this example for an hour, but I think my knowledge of PHP is now lacking.

Link to comment
Share on other sites

On 3/17/2021 at 3:46 PM, elabx said:

What exactly do you need help with? The most common/straightforward way to do this is to grab and sanitize the GET variables from a form submission and use them to create a selector:


<?php 
//Default selector to get ALL products
$selector = "template=products";

// Get color variable and sanitize
$color = $input->get->text('color');

if($color){
  $selector = "template=products, color=$color";
}

$products = $pages->find($selector);

?>
<form action=".">
  <select name="color">
    <option value="red">Red</option>
  </select>
</form>

<div class="products">
  <?php 
    // Render the products list
	foreach($products as $product){
	  echo "<div>....</div>"
  	}
	?>
</div>

 

Thanks @elabx,

I've used your example and I'm starting to get it.

But what does your code look like if there is a second choice next to "color" in the search form? For example: size

 

Link to comment
Share on other sites

A bit like this. Fixed the initial template to singular "product", its the same idea I just like it better this way 🙂

<?php 
//Default selector to get ALL products
$selector = "template=product";

// Get color and size variables and sanitize
$color = $input->get->text('color');
$size = $input->get->int('size');

// I changed the approach here to append the possible parameters to the selector string. 

if($color){
  $selector = $selector .= ",color=$color";
}
if($size){
  // See how this uses greater than or equal to
  $selector = $selector .= ",size>=$size";
}
// At this point, if you have both parameters for the search, you'll have a selector like this:
// 'template=product,color=blue,size>=10'

$products = $pages->find($selector);

?>
<form action=".">
  <select name="color">
    <option value="red">Red</option>
  </select>
  <input type="text" name="size">
</form>

<div class="products">
  <?php 
    // Render the products list
	foreach($products as $product){
	  echo "<div>....</div>"
  	}
	?>
</div>

 

  • Like 1
Link to comment
Share on other sites

22 hours ago, elabx said:

A bit like this. Fixed the initial template to singular "product", its the same idea I just like it better this way 🙂


<?php 
//Default selector to get ALL products
$selector = "template=product";

// Get color and size variables and sanitize
$color = $input->get->text('color');
$size = $input->get->int('size');

// I changed the approach here to append the possible parameters to the selector string. 

if($color){
  $selector = $selector .= ",color=$color";
}
if($size){
  // See how this uses greater than or equal to
  $selector = $selector .= ",size>=$size";
}
// At this point, if you have both parameters for the search, you'll have a selector like this:
// 'template=product,color=blue,size>=10'

$products = $pages->find($selector);

?>
<form action=".">
  <select name="color">
    <option value="red">Red</option>
  </select>
  <input type="text" name="size">
</form>

<div class="products">
  <?php 
    // Render the products list
	foreach($products as $product){
	  echo "<div>....</div>"
  	}
	?>
</div>

 

Thanks for this example @elabx.

I made my own version with checkboxes (multiselect). Only then did I come across the following problem.

When I check 2 checkboxes in the same group, For example: Color = Red and Color = Green then only the products with the field green are displayed.

After submit this what my url looks like: domain.com/?color=red&color=green

I think it's too hard for me on a Friday 😉

  • Like 1
Link to comment
Share on other sites

How are you doing your markup?

You should have something like this:

<input type="checkbox" name="color[]" value="red">
<input type="checkbox" name="color[]" value="blue">

Now color is an array:

$selector = "";

$colorOptions = $sanitizer->array($input->get->color,'text');

if(count($colorOptions){
	$colorOptions = implode('|',$colorOptions);
	$selector = ",color=$colorOptions";
}

 

  • Like 2
Link to comment
Share on other sites

2 hours ago, Erik said:

I studied PHP this weekend. As a result, I am starting to understand more and more of your code.
I'm not quite there yet.

Awesome! Great to hear that! Sometimes you just have to stare at the code a bit of time until it clicks, keep on it! Let me know if I can help you with something else.

  • Like 4
Link to comment
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
 Share

  • Recently Browsing   0 members

    No registered users viewing this page.

×
×
  • Create New...