diff --git a/ps_facetedsearch.php b/ps_facetedsearch.php index b21d3aacb..1b1b40576 100644 --- a/ps_facetedsearch.php +++ b/ps_facetedsearch.php @@ -172,6 +172,9 @@ protected function getDefaultFilters() 'label' => 'Product price filter (slider)', 'slider' => true, ], + 'layered_selection_highlights' => [ + 'label' => 'Product highlights filter', + ], ]; } @@ -990,7 +993,7 @@ public function rebuildLayeredStructure() `controller` VARCHAR(64) NOT NULL, `id_category` INT(10) UNSIGNED NOT NULL, `id_value` INT(10) UNSIGNED NULL DEFAULT \'0\', - `type` ENUM(\'category\',\'id_feature\',\'id_attribute_group\',\'availability\',\'condition\',\'manufacturer\',\'weight\',\'price\') NOT NULL, + `type` ENUM(\'category\',\'id_feature\',\'id_attribute_group\',\'availability\',\'condition\',\'manufacturer\',\'weight\',\'price\',\'highlights\') NOT NULL, `position` INT(10) UNSIGNED NOT NULL, `filter_type` int(10) UNSIGNED NOT NULL DEFAULT 0, `filter_show_limit` int(10) UNSIGNED NOT NULL DEFAULT 0, @@ -1191,6 +1194,12 @@ public function rebuildLayeredCache($productsIds = [], $categoriesIds = [], $reb $doneCategories[(int) $idCategory]['p'] = true; $toInsert = true; } + + if (!isset($doneCategories[(int) $idCategory]['q'])) { + $filterData['layered_selection_highlights'] = ['filter_type' => Converter::WIDGET_TYPE_CHECKBOX, 'filter_show_limit' => 0]; + $doneCategories[(int) $idCategory]['e'] = true; + $toInsert = true; + } } } @@ -1295,6 +1304,8 @@ public function buildLayeredCategories() } elseif (substr($key, 0, 23) == 'layered_selection_feat_') { $sqlInsert .= '(' . (int) $idCategory . ', \'' . $controller . '\', ' . (int) $idShop . ', ' . (int) str_replace('layered_selection_feat_', '', $key) . ', \'id_feature\',' . (int) $n . ', ' . (int) $limit . ', ' . (int) $type . '),'; + } elseif ($key == 'layered_selection_highlights') { + $sqlInsert .= '(' . (int) $idCategory . ', \'' . $controller . '\', ' . (int) $idShop . ', NULL,\'highlights\',' . (int) $n . ', ' . (int) $limit . ', ' . (int) $type . '),'; } ++$nbSqlValuesToInsert; diff --git a/src/Filters/Block.php b/src/Filters/Block.php index cbd16e11a..ee7615614 100644 --- a/src/Filters/Block.php +++ b/src/Filters/Block.php @@ -138,6 +138,9 @@ public function getFilterBlock( case 'availability': $filterBlocks[] = $this->getAvailabilitiesBlock($filter, $selectedFilters); break; + case 'highlights': + $filterBlocks[] = $this->getHighlightsBlock($filter, $selectedFilters); + break; case 'manufacturer': $filterBlocks[] = $this->getManufacturersBlock($filter, $selectedFilters, $idLang); break; @@ -524,6 +527,99 @@ private function getAvailabilitiesBlock($filter, $selectedFilters) return $quantityBlock; } + /** + * Gets block for extra product properties like "new", "on sale" and "discounted" + * + * @param array $filter + * @param array $selectedFilters + * + * @return array + */ + private function getHighlightsBlock($filter, $selectedFilters) + { + // Prepare array with options + $highlightsOptions = []; + + // Products on sale - available everywhere + $highlightsOptions['sale'] = [ + 'name' => $this->context->getTranslator()->trans( + 'On sale', + [], + 'Modules.Facetedsearch.Shop' + ), + 'nbr' => 0, + ]; + $filteredSearchAdapter = $this->searchAdapter->getFilteredSearchAdapter('on_sale'); + $filteredSearchAdapter->addFilter('on_sale', [1], '='); + // We need to add the field to initial population of the filter, because we won't be joining any additional tables + $filteredSearchAdapter->getInitialPopulation()->addSelectField('on_sale'); + $highlightsOptions['sale']['nbr'] = $filteredSearchAdapter->count('on_sale'); + + // New products - available everywhere except that page + if ($this->query->getQueryType() != 'new-products') { + $highlightsOptions['new'] = [ + 'name' => $this->context->getTranslator()->trans( + 'New product', + [], + 'Modules.Facetedsearch.Shop' + ), + 'nbr' => 0, + ]; + $filteredSearchAdapter = $this->searchAdapter->getFilteredSearchAdapter('date_add'); + $timeCondition = date( + 'Y-m-d 00:00:00', + strtotime( + ((int) Configuration::get('PS_NB_DAYS_NEW_PRODUCT') > 0 ? + '-' . ((int) Configuration::get('PS_NB_DAYS_NEW_PRODUCT') - 1) . ' days' : + '+ 1 days') + ) + ); + $filteredSearchAdapter->addFilter('date_add', ["'" . $timeCondition . "'"], '>'); + // We need to add the field to initial population of the filter, because we won't be joining any additional tables + $filteredSearchAdapter->getInitialPopulation()->addSelectField('date_add'); + $highlightsOptions['new']['nbr'] = $filteredSearchAdapter->count('date_add'); + } + + // Discounted products - available everywhere except that page + if ($this->query->getQueryType() != 'prices-drop') { + $highlightsOptions['discount'] = [ + 'name' => $this->context->getTranslator()->trans( + 'Discounted', + [], + 'Modules.Facetedsearch.Shop' + ), + 'nbr' => 0, + ]; + $filteredSearchAdapter = $this->searchAdapter->getFilteredSearchAdapter('reduction'); + $filteredSearchAdapter->addFilter('reduction', [0], '>'); + // No need to add any selects here, because the initial population doesn't change + // and the where condition will operate over externally joined table + $highlightsOptions['discount']['nbr'] = $filteredSearchAdapter->count('reduction'); + } + + // If some filters are selected, we mark them as such + if (isset($selectedFilters['highlights'])) { + // We loop through selected filters and assign it to our options and remove the rest + foreach ($highlightsOptions as $key => $values) { + if (in_array($key, $selectedFilters['highlights'], true)) { + $highlightsOptions[$key]['checked'] = true; + } + } + } + + $conditionBlock = [ + 'type_lite' => 'highlights', + 'type' => 'highlights', + 'id_key' => 0, + 'name' => $this->context->getTranslator()->trans('Highlights', [], 'Modules.Facetedsearch.Shop'), + 'values' => $highlightsOptions, + 'filter_show_limit' => (int) $filter['filter_show_limit'], + 'filter_type' => $filter['filter_type'], + ]; + + return $conditionBlock; + } + /** * Get the manufacturers filter block * diff --git a/src/Filters/Converter.php b/src/Filters/Converter.php index 1ee79b5ba..4cecac680 100644 --- a/src/Filters/Converter.php +++ b/src/Filters/Converter.php @@ -46,6 +46,7 @@ class Converter const TYPE_MANUFACTURER = 'manufacturer'; const TYPE_PRICE = 'price'; const TYPE_WEIGHT = 'weight'; + const TYPE_HIGHLIGHTS = 'highlights'; const PROPERTY_URL_NAME = 'url_name'; const PROPERTY_COLOR = 'color'; @@ -114,6 +115,7 @@ public function getFacetsFromFilterBlocks(array $filterBlocks) switch ($filterBlock['type']) { case self::TYPE_CATEGORY: case self::TYPE_CONDITION: + case self::TYPE_HIGHLIGHTS: case self::TYPE_MANUFACTURER: case self::TYPE_AVAILABILITY: case self::TYPE_ATTRIBUTE_GROUP: @@ -348,6 +350,37 @@ public function createFacetedSearchFiltersFromQuery(ProductSearchQuery $query) } } break; + case self::TYPE_HIGHLIGHTS: + if (!isset($facetAndFiltersLabels[$filterLabel])) { + // No need to filter if no information + continue 2; + } + + $highlightsOptions = [ + $this->context->getTranslator()->trans( + 'New product', + [], + 'Modules.Facetedsearch.Shop' + ) => 'new', + $this->context->getTranslator()->trans( + 'On sale', + [], + 'Modules.Facetedsearch.Shop' + ) => 'sale', + $this->context->getTranslator()->trans( + 'Discounted', + [], + 'Modules.Facetedsearch.Shop' + ) => 'discount', + ]; + + $searchFilters[$filter['type']] = []; + foreach ($highlightsOptions as $highlightsOption => $optionId) { + if (isset($facetAndFiltersLabels[$filterLabel]) && in_array($highlightsOption, $facetAndFiltersLabels[$filterLabel])) { + $searchFilters[$filter['type']][] = $optionId; + } + } + break; case self::TYPE_FEATURE: $features = $this->dataAccessor->getFeatures($idLang); foreach ($features as $feature) { @@ -468,6 +501,8 @@ private function convertFilterTypeToLabel($filterType) return $this->context->getTranslator()->trans('Weight', [], 'Modules.Facetedsearch.Shop'); case self::TYPE_CONDITION: return $this->context->getTranslator()->trans('Condition', [], 'Modules.Facetedsearch.Shop'); + case self::TYPE_HIGHLIGHTS: + return $this->context->getTranslator()->trans('Highlights', [], 'Modules.Facetedsearch.Shop'); case self::TYPE_AVAILABILITY: return $this->context->getTranslator()->trans('Availability', [], 'Modules.Facetedsearch.Shop'); case self::TYPE_MANUFACTURER: diff --git a/src/Product/Search.php b/src/Product/Search.php index 79fc538d0..df69ecf1f 100644 --- a/src/Product/Search.php +++ b/src/Product/Search.php @@ -121,12 +121,12 @@ public function initSearch($selectedFilters) // Adds basic filters that are common for every search, like shop and group limitations $this->addCommonFilters(); + // Adds filters that specific for this controller + $this->addControllerSpecificFilters(); + // Add filters that the user has selected for current query $this->addSearchFilters($selectedFilters); - // Adds filters that specific for category page - $this->addControllerSpecificFilters(); - // Add group by and flush it, let's go $this->getSearchAdapter()->addGroupBy('id_product'); $this->getSearchAdapter()->useFiltersAsInitialPopulation(); @@ -169,6 +169,30 @@ private function addSearchFilters($selectedFilters) $this->addFilter('id_category', $filterValues); break; + case 'highlights': + if (in_array('new', $filterValues)) { + $timeCondition = date( + 'Y-m-d 00:00:00', + strtotime( + ((int) Configuration::get('PS_NB_DAYS_NEW_PRODUCT') > 0 ? + '-' . ((int) Configuration::get('PS_NB_DAYS_NEW_PRODUCT') - 1) . ' days' : + '+ 1 days') + ) + ); + // Reset filter to prevent two same filters if we are on new products page + $this->getSearchAdapter()->resetFilter('date_add'); + $this->getSearchAdapter()->addFilter('date_add', ["'" . $timeCondition . "'"], '>'); + } + if (in_array('discount', $filterValues)) { + // Reset filter to prevent two same filters if we are on "prices-drop" page + $this->getSearchAdapter()->resetFilter('reduction'); + $this->getSearchAdapter()->addFilter('reduction', [0], '>'); + } + if (in_array('sale', $filterValues)) { + $this->getSearchAdapter()->addFilter('on_sale', [1], '='); + } + break; + case 'availability': /* * $filterValues options can have following values: diff --git a/upgrade/upgrade-3.13.0.php b/upgrade/upgrade-3.13.0.php index 5a8bc80ca..7799ad914 100644 --- a/upgrade/upgrade-3.13.0.php +++ b/upgrade/upgrade-3.13.0.php @@ -23,6 +23,11 @@ function upgrade_module_3_13_0(Ps_Facetedsearch $module) { + // Add new filter type into database + Db::getInstance()->execute( + 'ALTER TABLE `' . _DB_PREFIX_ . 'layered_category` + CHANGE `type` `type` ENUM(\'category\',\'id_feature\',\'id_attribute_group\',\'availability\',\'condition\',\'manufacturer\',\'weight\',\'price\',\'highlights\') NOT NULL;'); + $newHooks = [ 'actionFeatureValueFormBuilderModifier', 'actionAfterCreateFeatureValueFormHandler', diff --git a/views/templates/admin/add.tpl b/views/templates/admin/add.tpl index 4096abad9..02348988c 100644 --- a/views/templates/admin/add.tpl +++ b/views/templates/admin/add.tpl @@ -111,6 +111,36 @@ + +
  • +
    + +
    +
    + {l s='Product highlights filter' d='Modules.Facetedsearch.Admin'} +
    +
    + +
    + {call get_limit_select element="layered_selection_highlights"} +
    +
    +
    + +
    + +

    {l s='Checkbox' d='Modules.Facetedsearch.Admin'}

    +
    +
    +
  • +