Question

In Magento 2 How to add Star Rating Filter in to Layered Navigation?

Was it helpful?

Solution

create one file Called Rating.php as of now i did in core module only which can be implemented in your custom module vendor\magento\module-catalog\Model\Layer\Filter\Rating.php

<?php
/**
 * Copyright © 2015 Magento. All rights reserved.
 * See COPYING.txt for license details.
 */

namespace Magento\Catalog\Model\Layer\Filter;


use Magento\Catalog\Model\Category as CategoryModel;
use Magento\Catalog\Model\CategoryFactory as CategoryModelFactory;
use Magento\Catalog\Model\Layer;
use Magento\Framework\Registry;
/**
 * Layer category filter
 *
 * @author      Magento Core Team <core@magentocommerce.com>
 */
class Rating extends \Magento\Catalog\Model\Layer\Filter\AbstractFilter
{
    /**
     * Active Category Id
     *
     * @var int
     */
    protected $_categoryId;

    /**
     * Applied Category
     *
     * @var \Magento\Catalog\Model\Category
     */
    protected $_appliedCategory;

    /**
     * Core data
     *
     * @var \Magento\Framework\Escaper
     */
    protected $_escaper;

    /**
     * Core registry
     *
     * @var \Magento\Framework\Registry
     */
    protected $_coreRegistry;

    /**
     * @var CategoryDataProvider
     */
    private $dataProvider;

    /**
     * Construct
     *
     * @param \Magento\Catalog\Model\Layer\Filter\ItemFactory $filterItemFactory
     * @param \Magento\Store\Model\StoreManagerInterface $storeManager
     * @param \Magento\Catalog\Model\Layer $layer
     * @param \Magento\Catalog\Model\Layer\Filter\Item\DataBuilder $itemDataBuilder
     * @param \Magento\Framework\Escaper $escaper
     * @param CategoryFactory $categoryDataProviderFactory
     * @param array $data
     */
    public function __construct(
        \Magento\Catalog\Model\Layer\Filter\ItemFactory $filterItemFactory,
        \Magento\Store\Model\StoreManagerInterface $storeManager,
        \Magento\Catalog\Model\Layer $layer,
        \Magento\Catalog\Model\Layer\Filter\Item\DataBuilder $itemDataBuilder,
        \Magento\Framework\Escaper $escaper,
        array $data = []
    ) {
        parent::__construct($filterItemFactory, $storeManager, $layer, $itemDataBuilder, $data);
        $this->_escaper = $escaper;
        $this->_requestVar = 'rat';
    }


    /**
     * Get filter value for reset current filter state
     *
     * @return mixed|null
     */
    public function getResetValue()
    {
        return $this->dataProvider->getResetValue();
    }

    /**
     * Apply category filter to layer
     *
     * @param   \Magento\Framework\App\RequestInterface $request
     * @return  $this
     */
    public function apply(\Magento\Framework\App\RequestInterface $request)
    {
        /**
         * Filter must be string: $fromPrice-$toPrice
         */
        $filter = $request->getParam($this->getRequestVar());
        if (!$filter) {
            return $this;
        }
        $filter = explode('-', $filter);
        list($from, $to) = $filter;
         $collection = $this->getLayer()->getProductCollection();

        $collection->getSelect()->joinLeft(array('rova'=> 'rating_option_vote_aggregated'),'e.entity_id =rova.entity_pk_value',array("percent"))
        ->where("rova.percent between ".$from." and ".$to)
        ->group('e.entity_id'); 
        //$this->getLayer()->getState()->addFilter($this->_createItem($text, $filter));
        //$collection->printlogquery(true);
        return $this;
    }

    /**
     * Get filter name
     *
     * @return \Magento\Framework\Phrase
     */
    public function getName()
    {
        return __('Rating');
    }





       /**
     * Get data array for building attribute filter items
     *
     * @throws \Magento\Framework\Exception\LocalizedException
     * @return array
     */
    protected function _getItemsData()
    {

        $facets = array(
            '0-20'=>'1 Start',
            '21-40'=>'2 Start',
            '41-60'=>'3 Start',
            '61-80'=>'4 Start',
            '81-100'=>'5 Start'
            );
     $collection = $this->getLayer()->getProductCollection();
        $data = [];
        if (count($facets) > 1) { // two range minimum
            $i=1;
            foreach ($facets as $key => $label) {
               $count=$this->prepareData($key,$collection,$i);
               $i++;
               $this->itemDataBuilder->addItemData(
                    $this->_escaper->escapeHtml($label),
                    $key,
                    $count
                );
            }
        }

        return $this->itemDataBuilder->build();

        /* $this->itemDataBuilder->addItemData(
            $this->tagFilter->filter('5 star'),
            '80-100',
            1
        );
        return $this->itemDataBuilder->build(); */
    }
    /**
     * @param string $key
     * @param int $count
     * @return array
     */
    private function prepareData($filter,$collection,$i)
    {
       $filter = explode('-', $filter);
        list($from, $to) = $filter;
          /** @var \Magento\CatalogSearch\Model\ResourceModel\Fulltext\Collection $productCollection */


        $collection->getSelect()->joinLeft(array('rova'.$i=> 'rating_option_vote_aggregated'),'e.entity_id =rova'.$i.'.entity_pk_value',array("percent"))
        ->where("rova".$i.".percent between ".$from." and ".$to)
        ->group('e.entity_id'); 
        //$collection->printlogquery(true); echo '<br>............................<br>';
        return $collection->getSize();
    }
}

and in vendor\magento\module-catalog\Model\Layer\FilterList.php add rating filter option if you wrote custom module add this option by plugin

public function getFilters(\Magento\Catalog\Model\Layer $layer)
    {
        if (!count($this->filters)) {
            $this->filters = [
                $this->objectManager->create($this->filterTypes[self::CATEGORY_FILTER], ['layer' => $layer]),
                 $this->objectManager->create($this->filterTypes['Magento\Catalog\Model\Layer\Filter\Rating'], ['layer' => $layer]),
            ];
            foreach ($this->filterableAttributes->getList() as $attribute) {
                $this->filters[] = $this->createAttributeFilter($attribute, $layer);
            }
        }
        return $this->filters;
    }

it will bring rating filter and do filter with product collection but still product count and adding to state i not done if some one did please pot it

OTHER TIPS

 protected function _getItemsData()
{
    $s1='<div class="rating-summary" style="display: inline-block;margin-top: -5px;">
                                    <div class="rating-result" title="20%">
                                        <span style="width:20%"><span>1</span></span>
                                    </div>
                                </div>';

    $s2='<div class="rating-summary" style="display: inline-block;margin-top: -5px;">
                                    <div class="rating-result" title="40%">
                                        <span style="width:40%"><span>2</span></span>
                                    </div>
                                </div>';

    $s3='<div class="rating-summary" style="display: inline-block;margin-top: -5px;">
                                    <div class="rating-result" title="60%">
                                        <span style="width:60%"><span>3</span></span>
                                    </div>
                                </div>';

    $s4='<div class="rating-summary" style="display: inline-block;margin-top: -5px;">
                                    <div class="rating-result" title="80%">
                                        <span style="width:80%"><span>4</span></span>
                                    </div>
                                </div>';

    $s5='<div class="rating-summary" style="display: inline-block;margin-top: -5px;">
                                    <div class="rating-result" title="100%">
                                        <span style="width:100%"><span>5</span></span>
                                    </div>
                                </div>';


    $facets = array(
        '0-20'=>$s1,
        '21-40'=>$s2,
        '41-60'=>$s3,
        '61-80'=>$s4,
        '81-100'=>$s5,
    );

    $data = [];
    if (count($facets) > 1) { // two range minimum
        $i=1;
        foreach ($facets as $key => $label) {
            // $count=$this->prepareData($key,$collection,$i);
            $count='';
            $filter = explode('-', $key);
            list($from, $to) = $filter;

            //$collection = $this->getLayer()->getProductCollection();
            $collection=  $this->getLayer()->getCurrentCategory()->getProductCollection();

            $collection->getSelect()->joinLeft(array('rova'.$i=> 'rating_option_vote_aggregated'),'e.entity_id =rova'.$i.'.entity_pk_value',array("percent"))
                ->where("rova".$i.".percent between ".$from." and ".$to)
                ->group('e.entity_id');
          //  $collection->getSelect()->from('rating_option_vote_aggregated')->where("rating_option_vote_aggregated.percent between".$from."and".$to);
            //echo $collection->getSelect()->__toString();die;
           // $this->getLayer()->getState()->addFilter($this->_createItem($collection, $filter));

            $count = $collection->count();

            $i++;

            // echo $count;
            if($count > 0){
                $this->itemDataBuilder->addItemData(
                //$this->_escaper->escapeHtml($label),
                    $label,
                    $key,
                    $count
                );

                $count=0;
            }
        }
    }

    return $this->itemDataBuilder->build();

    /* $this->itemDataBuilder->addItemData(
        $this->tagFilter->filter('5 star'),
        '80-100',
        1
    );
    return $this->itemDataBuilder->build(); */
}

I have used the following code for custom prices.

    <?xml version="1.0" ?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
    <type name="Magento\Catalog\Model\Layer\FilterList">
        <plugin disabled="false" name="BA_BPriceLayeredNav_Layer_FilterList" sortOrder="10" type="BA\BNav\Plugin\Frontend\Magento\Catalog\Model\Layer\FilterList"/>
    </type>
</config>

app/code/BA/BNav/Plugin/Frontend/Magento/Catalog/Model/Layer/FilterList.php

    <?php
namespace BA\BNav\Plugin\Frontend\Magento\Catalog\Model\Layer;

class FilterList
{
    const CUSTOM_PRICE      = 'custom_price';

    protected $filterTypes = [
       self::CUSTOM_PRICE => \BA\BasysPriceLayeredNav\Model\Layer\Filter\CustomPrice::class,
    ];
    public function __construct(
        \Magento\Framework\ObjectManagerInterface $objectManager
    ) {
        $this->objectManager = $objectManager;
    }

    public function afterGetFilters(
        \Magento\Catalog\Model\Layer\FilterList $subject,
        $result,
        $layer
    ) {
        $result[] = $this->objectManager->create($this->filterTypes[self::CUSTOM_PRICE], ['layer' => $layer]);
        return $result;
    }
}

app/code/BA/BNav/Model/Layer/Filter/CustomPrice.php

    <?php
namespace BA\BNav\Model\Layer\Filter;

use Psr\Log\LoggerInterface;

class CustomPrice extends \Magento\Catalog\Model\Layer\Filter\AbstractFilter
{
   
    /**
     * @var \Psr\Log\LoggerInterface
     */
    protected $logger;
    protected $resource;
    protected $connection;

    /**
     * Construct
     *
     * @param \Magento\Catalog\Model\Layer\Filter\ItemFactory $filterItemFactory
     * @param \Magento\Store\Model\StoreManagerInterface $storeManager
     * @param \Magento\Catalog\Model\Layer $layer
     * @param \Magento\Catalog\Model\Layer\Filter\Item\DataBuilder $itemDataBuilder
     * @param \Magento\Framework\Escaper $escaper
     * @param CategoryFactory $categoryDataProviderFactory
     * @param array $data
     */
    public function __construct(
        \Magento\Catalog\Model\Layer\Filter\ItemFactory $filterItemFactory,
        \Magento\Store\Model\StoreManagerInterface $storeManager,
        \Magento\Catalog\Model\Layer $layer,
        \Magento\Catalog\Model\Layer\Filter\Item\DataBuilder $itemDataBuilder,
        \Magento\Framework\Pricing\PriceCurrencyInterface $priceCurrency,
        array $data = [],
        LoggerInterface $logger,
        \Magento\Framework\App\ResourceConnection $resource
    ) {
        parent::__construct($filterItemFactory, $storeManager, $layer, $itemDataBuilder, $data);
        $this->_requestVar = 'ba_price';
        $this->logger = $logger;
        $this->resource = $resource;
        $this->priceCurrency = $priceCurrency;
    }

    /**
     * Apply category filter to layer
     *
     * @param   \Magento\Framework\App\RequestInterface $request
     * @return  $this
     */
    public function apply(\Magento\Framework\App\RequestInterface $request)
    {
        
        $filter = $request->getParam($this->getRequestVar());
        if (!$filter) {
            return $this;
        }
        $filter = explode('-', $filter);
        list($from, $to) = $filter;
       $collection = $this->getLayer()->getProductCollection();
    $collection->getSelect()
    ->join(['mapp'=>'ba__product_map'], 'e.entity_id = mapp.entity_id')
    ->join(['productprice'=>'ba__product_price'], 'productprice.basys_id = mapp.basys_id')
    ->where('productprice.catalog_id = 1462')
    ->where('productprice.price >= ?', $from)
    ->where('productprice.price <= ?', $to);
        $this->logger->info($collection->getSelect()->__toString());
        
        $this->getLayer()
        ->getState()
        ->addFilter(
            $this->_createItem($this->_renderRangeLabel(empty($from) ? 0 : $from, $to), $filter)
        );
        return $this;
    }

    /**
     * Get filter name
     *
     * @return \Magento\Framework\Phrase
     */
    public function getName()
    {
        return __('Custom Price');
    }

    /**
     * Prepare text of range label
     *
     * @param float|string $fromPrice
     * @param float|string $toPrice
     * @return float|\Magento\Framework\Phrase
     */
    protected function _renderRangeLabel($fromPrice, $toPrice)
    {
        $formattedFromPrice = $this->priceCurrency->format($fromPrice);
        if ($toPrice === '') {
            return __('%1 and above', $formattedFromPrice);
        } else {
            return __('%1 - %2', $formattedFromPrice, $this->priceCurrency->format($toPrice));
        }
    }

    /**
     * Get data array for building category filter items
     *
     * @return array
     */
    protected function _getItemsData()
    {
        $ranges = [
            '0-4.99'=>'£0 - £4.99',
            '5-9.99'=>'£5 - £9.99',
            '10-20'=>'£10 - £20'
        ];
        $this->connection = $this->resource->getConnection();

        foreach ($ranges as $key => $label) {
            $count = $this->prepareData($key);
                $this->itemDataBuilder->addItemData(
                    $label,
                    $key,
                    $count
                );
        }
        return $this->itemDataBuilder->build();
    }

    private function prepareData($filter)
    {
        $filter = explode('-', $filter);
        list($from, $to) = $filter;
      
        $query = $this->connection->fetchAll('SELECT * FROM catalog_product_entity 
        WHERE price BETWEEN '.$from.' and '.$to);
        return count($query);
    }
}
Licensed under: CC-BY-SA with attribution
Not affiliated with magento.stackexchange
scroll top