Product Specs Filter

Open to all! Ask other shopowners for help.
ecartz
Core Team
Posts: 3084
Joined: Tue Nov 05, 2019 6:02 pm
Phoenix Version:
Has thanked: 4 times
Been thanked: 208 times

Re: Product Specs Filter

Post by ecartz »

Sorry, change

Code: Select all

    $this->column_list =& $parameters['column_list'];
to

Code: Select all

    $this->column_list = array_keys($parameters['column_specifications']);
Or go back and redo the change entirely, as I edited the previous post.


Join The Code Co-op to get access to your library in the Code Co-op Forum
LeeFoster
Contributor
Posts: 263
Joined: Sun Feb 28, 2021 9:41 pm
Phoenix Version: v1.0.8.20
Has thanked: 1 time
Been thanked: 5 times

Re: Product Specs Filter

Post by LeeFoster »

ecartz wrote: Thu Aug 18, 2022 10:19 am Sorry, change

Code: Select all

    $this->column_list =& $parameters['column_list'];
to

Code: Select all

    $this->column_list = array_keys($parameters['column_specifications']);
Or go back and redo the change entirely, as I edited the previous post.
Changing to your updated code now gives me

Code: Select all

Notice: Undefined variable: column_specifications in filter.php on line 54

Notice: Trying to access array offset on value of type null in filter.php on line 54
This is the whole code for the file as it currently is, and feel free to tell me if I'm an idiot and did something wrong.

Code: Select all

<?php
/*
  Copyright (c) 2019, G Burton
  All rights reserved.

  Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:

  1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.

  2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.

  3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.

  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

class hook_shop_filter_filter {
  var $version = '1.0.0';
  var $siteend = null;	
	public $sitestart = null;
	protected $column_specifications;

  public function listen_filterStart($parameters) {
    $this->column_specifications =& $parameters['column_specifications'];
  }
	
	  function listen_drawForm() {
	global $current_category_id;  
	 $this->siteend .= '<!-- Product Listing Filter hooked -->' . PHP_EOL;
   
	  if(isset($_GET['cPath'])){ 
	  $cPath = $_GET['cPath'];
	  } else {
		  $cPath = '';
	  }
	  $filter_html = null;
	  
   	 
    $filter_html .= '<div class="card mb-2 card-body alert-filters" style="text-align: left;">';
	  $filter_html .= '<h4>Product Filter</h4>';
	  $filter_html .= '<div class="row"><div class="col-md-2">';
	  $filter_html .= '<h5>Price</h5>';
	  $filter_html .= '<input type="hidden" id="hidden_minimum_price" value="0" />';
      $filter_html .= ' <input type="hidden" id="hidden_maximum_price" value="50" />';
      $filter_html .= '<p id="price_show">£0 - £50</p>';
      $filter_html .= '<div id="price_range"></div>';
		  
		$filter_html .= '<ul class="nav">';
        $filter_html .= '<li class="nav-item dropdown">';
        $filter_html .= '<a href="#" class="nav-link text-dark dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">'. TEXT_SORT_BY .'<span class="caret"></span></a>';
        $filter_html .= '<div class="dropdown-menu">';
           
    foreach (array_keys($this->column_specifications) as $i => $column) {
      if ($column_specifications[$column]['sortable']) {
        echo splitPageResults::create_sort_heading($_GET['sort'], $i+1, $column_specifications[$column]['heading']);
      }
    }
      $filter_html .= '</div>';
      $filter_html .= '</li>';
      $filter_html .= '</ul>';
		  
      $filter_html .= ' </div>	';
		  
	$cPath_array = Guarantor::ensure_global('category_tree')->parse_path($cPath);
    $cPath = implode( '_', $cPath_array );
    $current_category_id = end( $cPath_array );
	  
		  $category_groups_array = tep_db_query("SELECT DISTINCT pt.groups_id FROM products_to_categories p2c LEFT JOIN products_tags pt on pt.products_id = p2c.products_id WHERE p2c.categories_id = '" . $current_category_id . "'");
		  
		 while ($category_groups_tags = tep_db_fetch_array($category_groups_array)){
		  
		  $category_groups[] = $category_groups_tags["groups_id"];
		 }
		 
	 $group_master_array = [];
    // get groups in sorting order
    $group_name_query = tep_db_query("select * from tags_groups where language_id = '1' and tags_groups_id IN (". implode(", ", $category_groups) .") order by tags_groups_sort, tags_groups_name");
    while ($group_name = tep_db_fetch_array($group_name_query)) {
      $group_master_array[$group_name['tags_groups_id']]['group'] = $group_name['tags_groups_name'];

      // get group members in sorting order
      $group_member_query = tep_db_query("select tgm.*, tgm2tg.tags_groups_id from tags_groups_members tgm left join tags_groups_members_to_tags_groups tgm2tg on tgm.tags_groups_members_id = tgm2tg.tags_groups_members_id where language_id = '1' and tgm2tg.tags_groups_id = '" . (int)$group_name['tags_groups_id'] . "' and tgm2tg.tags_groups_id IN (". implode(", ", $category_groups) .") order by tgm.tags_groups_members_sort, tgm.tags_groups_members_name");
      while ($group_member = tep_db_fetch_array($group_member_query)) {
        $group_master_array[$group_name['tags_groups_id']]['members'][] = $group_member;
      }
    }
           foreach ($group_master_array as $k => $v) { 
			   $filter_html .= '<div class="col-md-2">';
	      $filter_html .= '<h5>' . $v['group'] . '</h5>';
	      $filter_html .= '<div style="height: 180px; overflow-y: auto; overflow-x: hidden;">';
			foreach ($v['members'] as $n => $m) {   
	      $filter_html .= '<div class="list-group-item checkbox" style="padding:1px 1px">';
	      $filter_html .= '<label><input type="checkbox" class="common_selector" data-brand="brand'.$m['tags_groups_id'].'" name="'. $v['group'] .'" value="' . $m['tags_groups_members_id'] . '"  > ' . $m['tags_groups_members_name'] .'</label>';
	      $filter_html .= '</div>';
	       }
	      $filter_html .= '</div>';
	      $filter_html .= '</div>';
	   }
     $filter_html .= '<input type="hidden" name="cPath" id="cPath" value="'.$_GET['cPath'].'"/>';
	  
	  $filter_html .= '</div>';
		  
	      $filter_html .= '</div>';
 
    return $filter_html;
 
  }
	
				
	public function listen_injectSiteEnd() {
      $this->siteend .= '<!-- Filter jquery hooked -->' . PHP_EOL;
      
     $this->siteend .= '<script src="https://cdnjs.cloudflare.com/ajax/libs/jqueryui/1.13.2/jquery-ui.min.js" integrity="sha512-57oZ/vW8ANMjR/KQ6Be9v/+/h6bq9/l3f0Oc7vn6qMqyhvPd1cvKBRWWpzu0QoneImqr2SkmO4MSqU+RpHom3Q==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>' . PHP_EOL;
      
      $filterJquery = <<<EOD
	  <script type="text/javascript">
	  $(document).ready(function() {

  filter_data();

  function filter_data() {
    $('.filter_data').html('<div id="loading" style="" ></div>');
     var action = 'fetch_data';
var minimum_price = $('#hidden_minimum_price').val();
    var maximum_price = $('#hidden_maximum_price').val();
    var cPath = $("#cPath").val();
	var vfilter = get_filter('values');
	var nfilter = get_filter('name');
     // to get the brand names: filter.brand
      // or if you wan tthe category values: filter.values
      $.ajax({
url: "ext/filter/fetch_data.php",
      method: "POST",
      data: {
        action: action,
        minimum_price: minimum_price,
        maximum_price: maximum_price,
        brand: filter.brand,
		value: vfilter.values,
		name: nfilter.name,
        cPath: cPath
      },
      success: function(data) {
        $("#product_list").html(data);
      }
    });
  }


  // filters by dataset and value
  function get_filter(what) {
    let filter = {
      [what]: [],
	  name:[],
      values: []
    };
    $('.common_selector:checked').each(function() {
      let b = $(this).data(what)
      if (!filter[what].includes(b)) filter[what].push(b)
      filter.values.push($(this).val())
	  filter.name.push($(this).attr("name"))
    })
    console.log('filter', filter)
    return filter;
  }

  $('.common_selector').click(function() {
    filter_data();
  });

    $('#price_range').slider({
        range:true,
        min:0,
        max:50,
        values:[0, 50],
        step:1,
        stop:function(event, ui)
        {
            $('#price_show').html('£' + ui.values[0] + ' - ' + '£' + ui.values[1]);
            $('#hidden_minimum_price').val(ui.values[0]);
            $('#hidden_maximum_price').val(ui.values[1]);
            filter_data();
        }
    });

});
</script> 
EOD;
		
		$this->siteend .= $filterJquery . PHP_EOL;
		
    return $this->siteend;
  }

}
ecartz
Core Team
Posts: 3084
Joined: Tue Nov 05, 2019 6:02 pm
Phoenix Version:
Has thanked: 4 times
Been thanked: 208 times

Re: Product Specs Filter

Post by ecartz »

Code: Select all

      if ($this->column_specifications[$column]['sortable']) {
        echo splitPageResults::create_sort_heading($_GET['sort'], $i+1, $this->column_specifications[$column]['heading']);
Inside the foreach, add this-> twice. Sorry, I didn't look past the column_list notice.
LeeFoster
Contributor
Posts: 263
Joined: Sun Feb 28, 2021 9:41 pm
Phoenix Version: v1.0.8.20
Has thanked: 1 time
Been thanked: 5 times

Re: Product Specs Filter

Post by LeeFoster »

ecartz wrote: Fri Aug 19, 2022 8:35 am

Code: Select all

      if ($this->column_specifications[$column]['sortable']) {
        echo splitPageResults::create_sort_heading($_GET['sort'], $i+1, $this->column_specifications[$column]['heading']);
Inside the foreach, add this-> twice. Sorry, I didn't look past the column_list notice.
Ok that has them showing, just not in the dropdown. So I changed your code above to

Code: Select all

if ($this->column_specifications[$column]['sortable']) {
        $filter_html .= splitPageResults::create_sort_heading($_GET['sort'], $i+1, $this->column_specifications[$column]['heading']);
and it now works to display it.

Next step is to get it to actually change the sort order. Can I connect to sortable_product_column.php or am I best writing my own code?
ecartz
Core Team
Posts: 3084
Joined: Tue Nov 05, 2019 6:02 pm
Phoenix Version:
Has thanked: 4 times
Been thanked: 208 times

Re: Product Specs Filter

Post by ecartz »

LeeFoster wrote: Fri Aug 19, 2022 8:48 am Next step is to get it to actually change the sort order. Can I connect to sortable_product_column.php or am I best writing my own code?
Have you tried it? Does it work?

I wouldn't expect you to have to do more to get sorting working. Actually, it's not clear to me why you needed to do this much. Anyway, it seems that you are using sortable_product_column, as that's what sets the column_specifications and calls the filterStart listener.
LeeFoster
Contributor
Posts: 263
Joined: Sun Feb 28, 2021 9:41 pm
Phoenix Version: v1.0.8.20
Has thanked: 1 time
Been thanked: 5 times

Re: Product Specs Filter

Post by LeeFoster »

ecartz wrote: Fri Aug 19, 2022 9:48 am Have you tried it? Does it work?

I wouldn't expect you to have to do more to get sorting working. Actually, it's not clear to me why you needed to do this much. Anyway, it seems that you are using sortable_product_column, as that's what sets the column_specifications and calls the filterStart listener.
Not tried it yet.

It needs to be done as the core function for displaying product listings is over written meaning the core sorting doesn't work.
LeeFoster
Contributor
Posts: 263
Joined: Sun Feb 28, 2021 9:41 pm
Phoenix Version: v1.0.8.20
Has thanked: 1 time
Been thanked: 5 times

Re: Product Specs Filter

Post by LeeFoster »

There's some JQuery code in the code quoted above that calls the file fetch_data.php which overwrites the current product listing, this is where I need to amend the sort order. The code for fetch_data.php is below, this is where I need the sorting to take place.

Code: Select all

<?php

chdir('../../');
  require 'includes/application_top.php';

require language::map_to_translation('filter/data.php');



			$cPath = $_POST['cPath'] ?? '';
			if ( tep_not_null( $cPath ) ) {
	$cPath_array = Guarantor::ensure_global('category_tree')->parse_path($cPath);
    //$cPath_array = tep_parse_category_path( $cPath );
    $cPath = implode( '_', $cPath_array );
    $current_category_id = end( $cPath_array );
} else {
    $current_category_id = 0;
}


if(isset($_POST["action"])){
	$select_str  = "select distinct p.products_id, m.*, p.*, pd.*, p.products_quantity as in_stock, IF(s.status, s.specials_new_products_price, NULL) as specials_new_products_price, IF(s.status, s.specials_new_products_price, p.products_price) as final_price, if(s.status, 1, 0) as is_special "; 
	
	$from_str = "from products p left join manufacturers m using(manufacturers_id) left join specials s on p.products_id = s.products_id";
	
	if(isset($_POST["name"]))
	{
	$from_str .= " left join products_tags pt on p.products_id = pt.products_id";
	}
	
	$from_str .= ", products_description pd, categories c, products_to_categories p2c";
	
$where_str = " where p.products_status = '1' and p.products_id = pd.products_id and p.products_id = p2c.products_id and p2c.categories_id = c.categories_id and p2c.products_id = p.products_id and p2c.products_id = pd.products_id and p2c.categories_id = ".(int)$current_category_id."";
	
		
	if(isset($_POST["minimum_price"], $_POST["maximum_price"]) && !empty($_POST["minimum_price"]) || !empty($_POST["maximum_price"]))
	{
		$where_str .= " AND p.products_price BETWEEN '".$_POST["minimum_price"]."' AND '".$_POST["maximum_price"]."'";
	}
	
	if(isset($_POST['name']) && ($_POST['value'])){
	
$a = $_POST['name'];
$b = $_POST['value'];

$array = array_map(null, $a, $b);

foreach($array as $key=>$sub){  // could be done "by reference" but that impacts code stability
    if(!strlen(current($sub))){  // if subarray value has no length
        unset($array[$key]);
    }
}

function array_group_by(array $arr, callable $key_selector) {
  $result = array();
  foreach ($arr as $i) {
    $key = call_user_func($key_selector, $i);
    $result[$key][] = $i;
  }  
  return $result;
}
	
$grouped = array_group_by($array, function($i){  return $i[0]; });
	
$count = count($grouped);	
	
foreach ($grouped as $group => $types) {
   
	$brand_filter = '';
    foreach ($types as $type) {
				$brand_filter .= $type["1"] . ",";
}
$brand_filter = trim($brand_filter, ',');//removes the final comma
		
    }
 $where_str .= " AND pt.groups_members_id IN('".$brand_filter."')";

	}
	
	
	$order_str = " ORDER BY pd.products_name";
	
	$listing_sql = $select_str . $from_str . $where_str . $order_str;
	
	$listing_results = tep_db_query($listing_sql);
	
		if(tep_db_num_rows($listing_results) > 0){
  		
  while ($listing = tep_db_fetch_array($listing_results)) {
	  ?> 
	<div class="col mb-2">
		
      <div class="card h-100 is-product" data-is-special="<?php echo (int)$listing['is_special'] ?>" data-product-price="<?php echo $currencies->display_raw($listing['final_price'], tep_get_tax_rate($listing['products_tax_class_id'])) ?>" data-product-manufacturer="<?php echo max(0, (int)$listing['manufacturers_id']) ?>">
	 	  <div class="card-header">
			  <h5 class="card-title"><a href="<?php echo tep_href_link('product_info.php', 'products_id=' . (int)$listing['products_id']) . '">' . $listing['products_name'] ?></a></h5>
			</div>
				  
				  
			<div class="row no-gutters">
    <div class="col-md-6">
		<a href="<?php echo tep_href_link('product_info.php', (isset($sort) ? 'sort=' . $sort . '&' : '') . 'products_id=' . (int)$listing['products_id']) ?>">
			<div class="image-blurred-edge">
				<?php echo tep_image('images/' . $listing['products_image'], htmlspecialchars($listing['products_name']), SMALL_IMAGE_WIDTH, SMALL_IMAGE_HEIGHT, '', true, 'card-img-top') ?>
			</div>
		</a>
	</div>	
  <div class="card-body col-md-6">
    
    <h6 class="card-subtitle mb-2 text-muted"><?php 
        if ($listing['is_special'] == 1) {
              echo sprintf(IS_PRODUCT_SHOW_PRICE_DATA_SPECIAL, $currencies->display_price($listing['products_price'], tep_get_tax_rate($listing['products_tax_class_id'])), $currencies->display_price($listing['specials_new_products_price'], tep_get_tax_rate($listing['products_tax_class_id'])));
            }
            else {
              echo sprintf(IS_PRODUCT_SHOW_PRICE_DATA, $currencies->display_price($listing['products_price'], tep_get_tax_rate($listing['products_tax_class_id'])));
            }
          ?></h6>
   
 

  <p class="card-text pt-0 border-0">
  <?php 
              echo $listing['products_seo_description'];
			  ?>
	  
	  </p>
	 
  </div>
	  </div>							
			
       </div>
     </div>
<?php
		}
		}else{
			echo "<center><h3>No Products Found!</h3></center>";
		}
}
?>
LeeFoster
Contributor
Posts: 263
Joined: Sun Feb 28, 2021 9:41 pm
Phoenix Version: v1.0.8.20
Has thanked: 1 time
Been thanked: 5 times

Re: Product Specs Filter

Post by LeeFoster »

I've managed to include sortable_product_columns.php now but it only ever shows the default sort by so in my case products_name.

Presumable because sortable_product_columns.php is looking for sort using $_GET and my fetch_data.php file either doesn't get the sort data or when I add it into the JQuery it's done as $_POST
LeeFoster
Contributor
Posts: 263
Joined: Sun Feb 28, 2021 9:41 pm
Phoenix Version: v1.0.8.20
Has thanked: 1 time
Been thanked: 5 times

Re: Product Specs Filter

Post by LeeFoster »

Never mind, got it all working now.
azpro
Contributor
Posts: 177
Joined: Fri Nov 06, 2020 8:25 am
Phoenix Version: v1.1.0.6
Has thanked: 30 times
Been thanked: 34 times

Re: Product Specs Filter

Post by azpro »

LeeFoster wrote: Fri Aug 19, 2022 11:20 am Never mind, got it all working now.
Hi!

Would you be able to share your final code?

Thanks!


Join The Code Co-op to get access to your library in the Code Co-op Forum
Post Reply