├── Block ├── Adminhtml │ └── System │ │ └── Config │ │ └── Form │ │ └── Field │ │ ├── Github.php │ │ ├── Logo.php │ │ ├── Marketplace.php │ │ ├── Skype.php │ │ └── Telegram.php ├── Copyright.php └── Swatches │ └── LayeredNavigation │ └── RenderLayered.php ├── Controller └── Index │ └── Index.php ├── LICENSE.md ├── Model ├── Layer │ ├── Filter │ │ ├── Attribute.php │ │ ├── Category.php │ │ ├── Decimal.php │ │ ├── Item.php │ │ └── Price.php │ └── ItemCollectionProvider.php ├── ResourceModel │ └── Fulltext │ │ └── Collection.php └── Url │ └── Builder.php ├── Observer └── Copyright.php ├── Plugin └── Model │ └── Adapter │ ├── Aggregation │ └── Checker │ │ └── Query │ │ └── CatalogView.php │ └── Mysql │ └── Filter │ └── Preprocessor.php ├── README.md ├── composer.json ├── etc ├── adminhtml │ ├── menu.xml │ └── system.xml ├── config.xml ├── di.xml ├── events.xml ├── frontend │ └── routes.xml └── module.xml ├── guidelines.pdf ├── registration.php └── view ├── adminhtml └── web │ └── images │ └── logo.png └── frontend ├── layout ├── catalog_category_view_type_layered.xml ├── catalogsearch_result_index.xml ├── default.xml └── slavayurthev_index_index.xml ├── templates ├── copyright.phtml ├── copyright │ └── link.phtml └── layer │ └── filter.phtml └── web ├── css └── styles.css └── img ├── checked.png └── unchecked.png /Block/Adminhtml/System/Config/Form/Field/Github.php: -------------------------------------------------------------------------------- 1 | ' 16 | .$element->getEscapedValue() 17 | .'

'; 18 | } 19 | } -------------------------------------------------------------------------------- /Block/Adminhtml/System/Config/Form/Field/Logo.php: -------------------------------------------------------------------------------- 1 |

'; 14 | } 15 | } -------------------------------------------------------------------------------- /Block/Adminhtml/System/Config/Form/Field/Marketplace.php: -------------------------------------------------------------------------------- 1 | ' 16 | .$element->getEscapedValue() 17 | .'

'; 18 | } 19 | } -------------------------------------------------------------------------------- /Block/Adminhtml/System/Config/Form/Field/Skype.php: -------------------------------------------------------------------------------- 1 | ' 16 | .$element->getEscapedValue() 17 | .'

'; 18 | } 19 | } -------------------------------------------------------------------------------- /Block/Adminhtml/System/Config/Form/Field/Telegram.php: -------------------------------------------------------------------------------- 1 | ' 16 | .$element->getEscapedValue() 17 | .'

'; 18 | } 19 | } -------------------------------------------------------------------------------- /Block/Copyright.php: -------------------------------------------------------------------------------- 1 | urlBuilder = $urlBuilder; 20 | parent::__construct( 21 | $context, 22 | $eavAttribute, 23 | $layerAttribute, 24 | $swatchHelper, 25 | $mediaHelper, 26 | $data 27 | ); 28 | } 29 | public function buildUrl($attributeCode, $optionId){ 30 | if(in_array($optionId, $this->urlBuilder->getValuesFromUrl($attributeCode))){ 31 | return $this->urlBuilder->getRemoveFilterUrl($attributeCode, $optionId); 32 | } 33 | else{ 34 | return $this->urlBuilder->getFilterUrl($attributeCode, $optionId); 35 | } 36 | } 37 | } -------------------------------------------------------------------------------- /Controller/Index/Index.php: -------------------------------------------------------------------------------- 1 | resultPageFactory = $resultPageFactory; 18 | parent::__construct($context); 19 | } 20 | public function execute() { 21 | $resultPage = $this->resultPageFactory->create(); 22 | $resultPage->getConfig()->getTitle()->set(__('Slava Yurthev Copyright')); 23 | $layout = $resultPage->getLayout(); 24 | $layout->addBlock( 25 | 'SY\MultipleLayeredNavigation\Block\Copyright', 26 | 'sy_copyright', 27 | 'content' 28 | ); 29 | $this->_eventManager->dispatch('sy_copyright', ['layout' => $layout]); 30 | return $resultPage; 31 | } 32 | } -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Slava Yurthev 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Model/Layer/Filter/Attribute.php: -------------------------------------------------------------------------------- 1 | tagFilter = $tagFilter; 35 | $this->urlBuilder = $urlBuilder; 36 | $this->request = $request; 37 | $this->storeManager = $storeManager; 38 | $this->collectionProvider = $collectionProvider; 39 | } 40 | public function apply(\Magento\Framework\App\RequestInterface $request){ 41 | $values = $this->urlBuilder->getValuesFromUrl($this->_requestVar); 42 | if (!$values){ 43 | return $this; 44 | } 45 | $productCollection = $this->getLayer()->getProductCollection(); 46 | $this->applyToCollection($productCollection); 47 | foreach ($values as $value){ 48 | $label = $this->getOptionText($value); 49 | $this->getLayer()->getState()->addFilter($this->_createItem($label, $value)); 50 | } 51 | return $this; 52 | } 53 | public function applyToCollection($collection){ 54 | $attribute = $this->getAttributeModel(); 55 | $attributeValue = $this->urlBuilder->getValuesFromUrl($this->_requestVar); 56 | if (empty($attributeValue)){ 57 | return $this; 58 | } 59 | $collection->addFieldToFilter($attribute->getAttributeCode(), array('in' => $attributeValue)); 60 | } 61 | protected function _getItemsData(){ 62 | $values = $this->urlBuilder->getValuesFromUrl($this->_requestVar); 63 | $productCollection = $this->getLayer()->getProductCollection(); 64 | $collection = $this->collectionProvider->getCollection($this->getCurrentCategory()); 65 | $collection->updateSearchCriteriaBuilder(); 66 | $this->getLayer()->prepareProductCollection($collection); 67 | foreach ($productCollection->getAddedFilters() as $field => $condition) { 68 | if ($this->getAttributeModel()->getAttributeCode() == $field) { 69 | continue; 70 | } 71 | $collection->addFieldToFilter($field, $condition); 72 | } 73 | $attribute = $this->getAttributeModel(); 74 | $optionsFacetedData = $this->getFacetedData(); 75 | $options = $attribute->getFrontend()->getSelectOptions(); 76 | foreach ($options as $option) { 77 | if(empty($option['value'])) { 78 | continue; 79 | } 80 | if(isset($optionsFacetedData[$option['value']])){ 81 | $count = $this->getOptionItemsCount($optionsFacetedData, $option['value']); 82 | $this->itemDataBuilder->addItemData( 83 | $this->tagFilter->filter($option['label']), 84 | $option['value'], 85 | $count 86 | ); 87 | } 88 | } 89 | return $this->itemDataBuilder->build(); 90 | } 91 | private function getOptionItemsCount($faceted, $key){ 92 | if(isset($faceted[$key]['count'])){ 93 | return $faceted[$key]['count']; 94 | } 95 | return 0; 96 | } 97 | private function getFacetedData(){ 98 | $collection = $this->collectionProvider->getCollection($this->getCurrentCategory()); 99 | $collection->updateSearchCriteriaBuilder(); 100 | $collection->addCategoryFilter($this->getCurrentCategory()); 101 | if($this->getCurrentCategory()->getId() == $this->storeManager->getStore()->getRootCategoryId()){ 102 | $collection->addSearchFilter($this->request->getParam('q')); 103 | } 104 | return $collection->getFacetedData($this->getAttributeModel()->getAttributeCode()); 105 | } 106 | private function getCurrentCategory(){ 107 | return $this->getLayer()->getCurrentCategory(); 108 | } 109 | } -------------------------------------------------------------------------------- /Model/Layer/Filter/Category.php: -------------------------------------------------------------------------------- 1 | escaper = $escaper; 36 | $this->dataProvider = $categoryDataProviderFactory->create(['layer' => $this->getLayer()]); 37 | $this->urlBuilder = $urlBuilder; 38 | $this->collectionProvider = $collectionProvider; 39 | } 40 | public function apply(\Magento\Framework\App\RequestInterface $request){ 41 | $values = $this->urlBuilder->getValuesFromUrl($this->_requestVar); 42 | if (!$values) { 43 | return $this; 44 | } 45 | $productCollection = $this->getLayer()->getProductCollection(); 46 | $this->applyToCollection($productCollection); 47 | $categoryCollection = ObjectManager::getInstance()->create( 48 | \Magento\Catalog\Model\ResourceModel\Category\Collection::class 49 | ); 50 | $categoryCollection->addAttributeToFilter('entity_id', ['in' => $values])->addAttributeToSelect('name'); 51 | $categoryItems = $categoryCollection->getItems(); 52 | foreach ($values as $value) { 53 | if (isset($categoryItems[$value])) { 54 | $category = $categoryItems[$value]; 55 | $label = $category->getName(); 56 | $this->getLayer() 57 | ->getState() 58 | ->addFilter($this->_createItem($label, $value)); 59 | } 60 | } 61 | return $this; 62 | } 63 | protected function _getItemsData(){ 64 | $values = $this->urlBuilder->getValuesFromUrl($this->_requestVar); 65 | $productCollection = $this->getLayer()->getProductCollection(); 66 | $collection = $this->collectionProvider->getCollection($this->getLayer()->getCurrentCategory()); 67 | $collection->updateSearchCriteriaBuilder(); 68 | $this->getLayer()->prepareProductCollection($collection); 69 | foreach ($productCollection->getAddedFilters() as $field => $condition) { 70 | if ($field === 'category_ids') { 71 | $collection->addFieldToFilter($field, $this->getLayer()->getCurrentCategory()->getId()); 72 | continue; 73 | } 74 | $collection->addFieldToFilter($field, $condition); 75 | } 76 | $optionsFacetedData = $collection->getFacetedData('category'); 77 | $category = $this->dataProvider->getCategory(); 78 | $categories = $category->getChildrenCategories(); 79 | if ($category->getIsActive()) { 80 | foreach ($categories as $category) { 81 | if ($category->getIsActive()) { 82 | if(isset($optionsFacetedData[$category->getId()])){ 83 | $count = $this->getOptionItemsCount($optionsFacetedData, $category->getId()); 84 | $this->itemDataBuilder->addItemData( 85 | $this->escaper->escapeHtml($category->getName()), 86 | $category->getId(), 87 | $count 88 | ); 89 | } 90 | } 91 | } 92 | } 93 | return $this->itemDataBuilder->build(); 94 | } 95 | public function applyToCollection($collection){ 96 | $values = $this->urlBuilder->getValuesFromUrl($this->_requestVar); 97 | if (empty($values)) { 98 | return $this; 99 | } 100 | $collection->addCategoriesFilter(['in' => $values]); 101 | return $this; 102 | } 103 | private function getOptionItemsCount($faceted, $key){ 104 | if(isset($faceted[$key]['count'])){ 105 | return $faceted[$key]['count']; 106 | } 107 | return 0; 108 | } 109 | } -------------------------------------------------------------------------------- /Model/Layer/Filter/Decimal.php: -------------------------------------------------------------------------------- 1 | _url->getRemoveFilterUrl( 13 | $this->getFilter()->getRequestVar(), 14 | $this->getValue(), 15 | [$this->_htmlPagerBlock->getPageVarName() => null] 16 | ); 17 | } 18 | public function getUrl(){ 19 | return $this->_url->getFilterUrl( 20 | $this->getFilter()->getRequestVar(), 21 | $this->getValue(), 22 | [$this->_htmlPagerBlock->getPageVarName() => null], 23 | false 24 | ); 25 | } 26 | public function isActive(){ 27 | $values = ObjectManager::getInstance()->create( 28 | \SY\MultipleLayeredNavigation\Model\Url\Builder::class 29 | ) 30 | ->getValuesFromUrl($this->getFilter()->getRequestVar()); 31 | if(!empty($values)){ 32 | return in_array($this->getValue(), $values); 33 | } 34 | } 35 | } -------------------------------------------------------------------------------- /Model/Layer/Filter/Price.php: -------------------------------------------------------------------------------- 1 | dataProvider = $dataProviderFactory->create(['layer' => $this->getLayer()]); 45 | $this->urlBuilder = $urlBuilder; 46 | $this->collectionProvider = $collectionProvider; 47 | $this->priceCurrency = $priceCurrency; 48 | } 49 | public function apply(\Magento\Framework\App\RequestInterface $request){ 50 | $this->applyToCollection($this->getLayer()->getProductCollection(), true); 51 | return $this; 52 | } 53 | public function applyToCollection($collection, $addFilter = false){ 54 | $values = $this->urlBuilder->getValuesFromUrl($this->_requestVar); 55 | if (!$values){ 56 | return $this; 57 | } 58 | if($addFilter) { 59 | foreach ($values as $value) { 60 | list($from, $to) = explode("-", $value); 61 | $label = $this->_renderRangeLabel($from, $to); 62 | $this->getLayer()->getState()->addFilter($this->_createItem($label, $value)); 63 | } 64 | } 65 | $collection->addFieldToFilter( 66 | 'price', 67 | [ 68 | 'from' => $this->getMin(), 69 | 'to' => $this->getMax() 70 | ] 71 | ); 72 | return $this; 73 | } 74 | public function getMax(){ 75 | return $this->getCollectionWithoutFilter()->getMaxPrice(); 76 | } 77 | public function getMin(){ 78 | return $this->getCollectionWithoutFilter()->getMinPrice(); 79 | } 80 | protected function getTo($from){ 81 | $to = ''; 82 | $interval = $this->dataProvider->getInterval(); 83 | if ($interval && is_numeric($interval[1]) && $interval[1] > $from) { 84 | $to = $interval[1]; 85 | } 86 | return $to; 87 | } 88 | protected function getFrom($from){ 89 | $to = ''; 90 | $interval = $this->dataProvider->getInterval(); 91 | if ($interval && is_numeric($interval[0]) && $interval[0] < $from) { 92 | $to = $interval[0]; 93 | } 94 | return $to; 95 | } 96 | protected function _getItemsData(){ 97 | $values = $this->urlBuilder->getValuesFromUrl($this->_requestVar); 98 | $attribute = $this->getAttributeModel(); 99 | $productCollection = $this->getLayer()->getProductCollection(); 100 | $facets = $this->getCollectionWithoutFilter()->getFacetedData($attribute->getAttributeCode()); 101 | $data = []; 102 | if(!empty($facets)){ 103 | $i=0; 104 | foreach ($facets as $key => $aggregation) { 105 | if (strpos($key, '_') === false) { 106 | continue; 107 | } 108 | list($from, $to) = explode('_', $key); 109 | if($from == '*') { 110 | $from = $this->getMin(); 111 | } 112 | if($to == '*') { 113 | $to = $this->getMax(); 114 | } 115 | $to -= self::PRICE_DELTA; 116 | // Improved price ranges 117 | if($from >= $to){ 118 | if($i>0){ 119 | if($from >= $data[$i-1]['to']){ 120 | $merged = $data[$i-1]; 121 | $merged['count'] += $aggregation['count']; 122 | $merged['to'] = $from; 123 | $merged['value'] = $merged['from'].'-'.$merged['to']; 124 | $merged['label'] = $this->_renderRangeLabel($merged['from'], $merged['to']); 125 | $data[$i-1] = $merged; 126 | } 127 | } 128 | continue; 129 | } 130 | $item = [ 131 | 'label' => $this->_renderRangeLabel($from, $to), 132 | 'value' => $from.'-'.$to, 133 | 'count' => $aggregation['count'], 134 | 'from' => $from, 135 | 'to' => $to 136 | ]; 137 | $data[$i] = $item; 138 | $i++; 139 | } 140 | } 141 | if(count($data) > 1) { 142 | foreach ($data as $item) { 143 | $this->itemDataBuilder->addItemData( 144 | $item['label'], 145 | $item['value'], 146 | $item['count'] 147 | ); 148 | } 149 | } 150 | return $this->itemDataBuilder->build(); 151 | } 152 | protected function _renderRangeLabel($fromPrice, $toPrice){ 153 | $fromPrice = empty($fromPrice) ? 0 : $fromPrice * $this->getCurrencyRate(); 154 | $toPrice = empty($toPrice) ? $toPrice : $toPrice * $this->getCurrencyRate(); 155 | $formattedFromPrice = $this->priceCurrency->format($fromPrice); 156 | if ($toPrice === '') { 157 | return __('%1 and above', $formattedFromPrice); 158 | } elseif ($fromPrice == $toPrice && $this->dataProvider->getOnePriceIntervalValue()) { 159 | return $formattedFromPrice; 160 | } else { 161 | return __('%1 - %2', $formattedFromPrice, $this->priceCurrency->format($toPrice)); 162 | } 163 | } 164 | protected function getCollectionWithoutFilter(){ 165 | if (!$this->emptyCollection) { 166 | $productCollection = $this->getLayer()->getProductCollection(); 167 | $this->emptyCollection = $this->collectionProvider->getCollection( 168 | $this->getLayer()->getCurrentCategory() 169 | ); 170 | $this->emptyCollection->updateSearchCriteriaBuilder(); 171 | $this->getLayer()->prepareProductCollection($this->emptyCollection); 172 | foreach ($productCollection->getAddedFilters() as $field => $condition) { 173 | if ($this->getAttributeModel()->getAttributeCode() == $field) { 174 | continue; 175 | } 176 | $this->emptyCollection->addFieldToFilter($field, $condition); 177 | } 178 | } 179 | return $this->emptyCollection; 180 | } 181 | } -------------------------------------------------------------------------------- /Model/Layer/ItemCollectionProvider.php: -------------------------------------------------------------------------------- 1 | storeManager = $storeManager; 17 | $this->collectionFactory = $collectionFactory; 18 | } 19 | public function getCollection(\Magento\Catalog\Model\Category $category){ 20 | if ($category->getId() == $this->storeManager->getStore()->getRootCategoryId()) { 21 | $collection = $this->collectionFactory->create(['searchRequestName' => 'quick_search_container']); 22 | } else { 23 | $collection = $this->collectionFactory->create(); 24 | $collection->addCategoryFilter($category); 25 | } 26 | return $collection; 27 | } 28 | } -------------------------------------------------------------------------------- /Model/ResourceModel/Fulltext/Collection.php: -------------------------------------------------------------------------------- 1 | _addedFilters[$field] = $condition; 15 | } 16 | return parent::addFieldToFilter($field, $condition); 17 | } 18 | public function addCategoriesFilter(array $categoriesFilter){ 19 | $this->addFieldToFilter('category_ids', $categoriesFilter); 20 | return $this; 21 | } 22 | public function getAddedFilters(){ 23 | return $this->_addedFilters; 24 | } 25 | public function updateSearchCriteriaBuilder(){ 26 | $searchCriteriaBuilder = ObjectManager::getInstance() 27 | ->create(\Magento\Framework\Api\Search\SearchCriteriaBuilder::class); 28 | $this->setSearchCriteriaBuilder($searchCriteriaBuilder); 29 | return $this; 30 | } 31 | protected function _prepareStatisticsData(){ 32 | $this->_renderFilters(); 33 | return parent::_prepareStatisticsData(); 34 | } 35 | } -------------------------------------------------------------------------------- /Model/Url/Builder.php: -------------------------------------------------------------------------------- 1 | true, '_use_rewrite' => true, '_query' => $query]; 12 | $values = array_unique( 13 | array_merge( 14 | $this->getValuesFromUrl($code), 15 | [$value] 16 | ) 17 | ); 18 | $params['_query'][$code] = implode(',', $values); 19 | return urldecode($this->getUrl('*/*/*', $params)); 20 | } 21 | public function getRemoveFilterUrl($code, $value, $query = []){ 22 | $params = ['_current' => true, '_use_rewrite' => true, '_query' => $query, '_escape' => true]; 23 | $values = $this->getValuesFromUrl($code); 24 | $key = array_search($value, $values); 25 | unset($values[$key]); 26 | $params['_query'][$code] = $values ? implode(',', $values) : null; 27 | return urldecode($this->getUrl('*/*/*', $params)); 28 | } 29 | public function getValuesFromUrl($code){ 30 | return array_filter(explode(',', $this->_getRequest()->getParam($code))); 31 | } 32 | } -------------------------------------------------------------------------------- /Observer/Copyright.php: -------------------------------------------------------------------------------- 1 | getLayout()->addBlock( 11 | 'Magento\Framework\View\Element\Text', 12 | 'sy_copyright_multiple_layered_navigation', 13 | 'sy_copyright' 14 | )->setData( 15 | 'text', 16 | 'Magento 2 Multiple Layered Navigation Extension' 17 | ); 18 | return $this; 19 | } 20 | } -------------------------------------------------------------------------------- /Plugin/Model/Adapter/Aggregation/Checker/Query/CatalogView.php: -------------------------------------------------------------------------------- 1 | categoryRepository = $categoryRepository; 24 | $this->storeManager = $storeManager; 25 | } 26 | public function aroundIsApplicable( 27 | \Magento\CatalogSearch\Model\Adapter\Aggregation\Checker\Query\CatalogView $subject, 28 | \Closure $proceed, 29 | RequestInterface $request 30 | ){ 31 | if ($request->getName() === 'catalog_view_container') { 32 | return $this->hasAnchorCategory($request); 33 | } 34 | return $proceed($request); 35 | } 36 | private function hasAnchorCategory(RequestInterface $request){ 37 | $queryType = $request->getQuery()->getType(); 38 | $result = false; 39 | if ($queryType === QueryInterface::TYPE_BOOL) { 40 | $categories = $this->getCategoriesFromQuery($request->getQuery()); 41 | foreach ($categories as $category) { 42 | if ($category && $category->getIsAnchor()) { 43 | $result = true; 44 | break; 45 | } 46 | } 47 | } 48 | return $result; 49 | } 50 | private function getCategoriesFromQuery(QueryInterface $queryExpression){ 51 | $categoryIds = $this->getCategoryIdsFromQuery($queryExpression); 52 | $categories = []; 53 | foreach ($categoryIds as $categoryId) { 54 | try { 55 | $categories[] = $this->categoryRepository 56 | ->get($categoryId, $this->storeManager->getStore()->getId()); 57 | } catch (NoSuchEntityException $e) {} 58 | } 59 | return $categories; 60 | } 61 | private function getCategoryIdsFromQuery(QueryInterface $queryExpression){ 62 | $queryFilterArray = []; 63 | $queryFilterArray[] = $queryExpression->getMust(); 64 | $queryFilterArray[] = $queryExpression->getShould(); 65 | $categoryIds = []; 66 | foreach ($queryFilterArray as $item) { 67 | if (!empty($item) && isset($item['category'])) { 68 | $queryFilter = $item['category']; 69 | $values = $queryFilter->getReference()->getValue(); 70 | if (is_array($values)) { 71 | $categoryIds = array_merge($categoryIds, $values['in']); 72 | } else { 73 | $categoryIds[] = $values; 74 | } 75 | } 76 | } 77 | return $categoryIds; 78 | } 79 | } -------------------------------------------------------------------------------- /Plugin/Model/Adapter/Mysql/Filter/Preprocessor.php: -------------------------------------------------------------------------------- 1 | urlBuilder = $urlBuilder; 15 | } 16 | public function aroundProcess( 17 | \Magento\CatalogSearch\Model\Adapter\Mysql\Filter\Preprocessor $subject, 18 | \Closure $proceed, 19 | \Magento\Framework\Search\Request\FilterInterface $filter, 20 | $isNegation, 21 | $query 22 | ){ 23 | if($filter->getField() === 'price'){ 24 | $values = $this->urlBuilder->getValuesFromUrl('price'); 25 | if(!empty($values)){ 26 | $statements = []; 27 | foreach ($values as $value) { 28 | list($from, $to) = explode("-", $value); 29 | $statement = [ 30 | $this->getSqlStringByArray( 31 | [floatval($from)], 32 | 'final_price', 33 | '>=' 34 | ), 35 | $this->getSqlStringByArray( 36 | [floatval($to)], 37 | 'final_price', 38 | '<=' 39 | ) 40 | ]; 41 | $statements[] = '('.implode(" AND ", $statement).')'; 42 | } 43 | return implode(" OR ", $statements); 44 | } 45 | } 46 | if($filter->getField() === 'category_ids'){ 47 | if(is_array($filter->getValue())){ 48 | if(isset($filter->getValue()['in'])){ 49 | return $this->getSqlStringByArray($filter->getValue()['in']); 50 | } 51 | return $this->getSqlStringByArray($filter->getValue()); 52 | } 53 | elseif(is_string($filter->getValue())){ 54 | return $this->getSqlStringByArray([$filter->getValue()]); 55 | } 56 | } 57 | return $proceed($filter, $isNegation, $query); 58 | } 59 | private function getSqlStringByArray( 60 | $array = [], 61 | $field = 'category_ids_index.category_id', 62 | $operator = '=', 63 | $rule = 'OR' 64 | ){ 65 | $statements = []; 66 | if(!empty($array)){ 67 | foreach ($array as $value) { 68 | $statements[] = $field.' '.$operator.' '.$value; 69 | } 70 | } 71 | return implode(' '.$rule.' ', $statements); 72 | } 73 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Multiple Layered Navigation 2 | 3 | Multiple Layered Navigation Extension for Magento 2.x 4 | 5 | [GUIDE](https://github.com/SlavaYurthev/MultipleLayeredNavigation-M2/wiki) 6 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "sy/multiple-layered-navigation", 3 | "description": "N/A", 4 | "license": "MIT", 5 | "type": "magento2-module", 6 | "version": "0.3.1", 7 | "minimum-stability": "dev", 8 | "require": { 9 | "php": "~5.6.0|7.0.2|7.0.4|~7.0.6|~7.1.0" 10 | }, 11 | "authors": [ 12 | { 13 | "name": "Slava Yurthev", 14 | "email": "slavik-iii@ukr.net", 15 | "homepage": "https://github.com/SlavaYurthev/", 16 | "role": "Developer" 17 | } 18 | ], 19 | "autoload": { 20 | "files": [ "registration.php" ], 21 | "psr-4": { 22 | "SY\\MultipleLayeredNavigation\\": "" 23 | } 24 | } 25 | } -------------------------------------------------------------------------------- /etc/adminhtml/menu.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 11 | 16 | 22 | 29 | 30 | -------------------------------------------------------------------------------- /etc/adminhtml/system.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | separator-top 16 | 17 | slava_yurthev 18 | Magento_Backend::content 19 | 20 | 21 | 25 | 26 | 27 | SY\MultipleLayeredNavigation\Block\Adminhtml\System\Config\Form\Field\Skype 28 | 29 | 30 | 31 | SY\MultipleLayeredNavigation\Block\Adminhtml\System\Config\Form\Field\Telegram 32 | 33 | 34 | 35 | SY\MultipleLayeredNavigation\Block\Adminhtml\System\Config\Form\Field\Github 36 | 37 | 38 | 39 | SY\MultipleLayeredNavigation\Block\Adminhtml\System\Config\Form\Field\Marketplace 40 | 41 | 42 |
43 |
44 |
-------------------------------------------------------------------------------- /etc/config.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 11 | 12 | 13 | darks_v1rus 14 | darks_virus 15 | SlavaYurthev 16 | Slava Yurthev 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /etc/di.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 11 | 12 | 13 | SY\MultipleLayeredNavigation\Model\Layer\Filter\Attribute 14 | SY\MultipleLayeredNavigation\Model\Layer\Filter\Price 15 | SY\MultipleLayeredNavigation\Model\Layer\Filter\Decimal 16 | SY\MultipleLayeredNavigation\Model\Layer\Filter\Category 17 | 18 | 19 | 20 | 21 | 22 | 23 | SY\MultipleLayeredNavigation\Model\Layer\Filter\Attribute 24 | SY\MultipleLayeredNavigation\Model\Layer\Filter\Price 25 | SY\MultipleLayeredNavigation\Model\Layer\Filter\Decimal 26 | SY\MultipleLayeredNavigation\Model\Layer\Filter\Category 27 | 28 | 29 | 30 | 31 | 32 | SY\MultipleLayeredNavigation\Model\Layer\ItemCollectionProvider 33 | 34 | 35 | 36 | 37 | SY\MultipleLayeredNavigation\Model\Layer\ItemCollectionProvider 38 | 39 | 40 | 41 | 42 | SY\MultipleLayeredNavigation\Model\Url\Builder 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | SY\MultipleLayeredNavigation\Model\Url\Builder 51 | 52 | 53 | 54 | 55 | SY\MultipleLayeredNavigation\Model\Layer\Filter\Item 56 | 57 | 58 | 59 | 60 | 61 | 62 | -------------------------------------------------------------------------------- /etc/events.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /etc/frontend/routes.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /etc/module.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /guidelines.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SlavaYurthev/MultipleLayeredNavigation-M2/6e573c932149754742eb792744da83156c92022d/guidelines.pdf -------------------------------------------------------------------------------- /registration.php: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 11 | 12 | 13 | SY_MultipleLayeredNavigation::layer/filter.phtml 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /view/frontend/layout/catalogsearch_result_index.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 11 | 12 | 13 | SY_MultipleLayeredNavigation::layer/filter.phtml 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /view/frontend/layout/default.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /view/frontend/layout/slavayurthev_index_index.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /view/frontend/templates/copyright.phtml: -------------------------------------------------------------------------------- 1 |

2 | 9 |

-------------------------------------------------------------------------------- /view/frontend/templates/copyright/link.phtml: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /view/frontend/templates/layer/filter.phtml: -------------------------------------------------------------------------------- 1 | 8 |
    9 | 10 |
  1. 11 | getCount() > 0): ?> 12 | 13 | getLabel(); ?> 14 | helper('\Magento\Catalog\Helper\Data')->shouldDisplayProductCountOnLayer()): ?> 15 | getCount() ?>getCount() == 1):?> 16 | 17 | 18 | 19 | getLabel(); ?> 20 | helper('\Magento\Catalog\Helper\Data')->shouldDisplayProductCountOnLayer()): ?> 21 | getCount() ?>getCount() == 1):?> 22 | 23 | 24 |
  2. 25 | 26 |
-------------------------------------------------------------------------------- /view/frontend/web/css/styles.css: -------------------------------------------------------------------------------- 1 | ol.sy-multiple-layered-navigation-items li a { 2 | display: inline-block; 3 | width: 100%; 4 | padding-left: 24px; 5 | margin-left: 0; 6 | margin-right: 0; 7 | min-height: 18px; 8 | box-sizing: border-box; 9 | background-image: url(../img/unchecked.png); 10 | background-repeat: no-repeat; 11 | background-position: left center; 12 | background-color: transparent !important; 13 | } 14 | ol.sy-multiple-layered-navigation-items li.active a { 15 | background-image: url(../img/checked.png); 16 | } -------------------------------------------------------------------------------- /view/frontend/web/img/checked.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SlavaYurthev/MultipleLayeredNavigation-M2/6e573c932149754742eb792744da83156c92022d/view/frontend/web/img/checked.png -------------------------------------------------------------------------------- /view/frontend/web/img/unchecked.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SlavaYurthev/MultipleLayeredNavigation-M2/6e573c932149754742eb792744da83156c92022d/view/frontend/web/img/unchecked.png --------------------------------------------------------------------------------