Jump to content

Edit History

GaetanV

GaetanV

Problem fixed...

The faceted module does not allow searching on characteristics that do not have predefined values.

To avoid this behavior, you must authorize the module to list the characteristics that do not have default values by overriding the file:

modules\ps_facetedsearch\ps_facetedsearch.php

For this add the file:

override\modules\ps_facetedsearch\ps_facetedsearch.php

With this content:

<?php

class Ps_FacetedsearchOverride extends Ps_Facetedsearch {
    
    /**
     * @var int
     */
    private $psLayeredFullTree;

    public function getContent()
    {
        global $cookie;
        $message = '';

        if (Tools::isSubmit('SubmitFilter')) {
            if (!Tools::getValue('layered_tpl_name')) {
                $message = $this->displayError($this->trans('Filter template name required (cannot be empty)', [], 'Modules.Facetedsearch.Admin'));
            } elseif (!Tools::getValue('categoryBox')) {
                $message = $this->displayError($this->trans('You must select at least one category.', [], 'Modules.Facetedsearch.Admin'));
            } else {
                // Get or generate id
                $idLayeredFilter = (int) Tools::getValue('id_layered_filter');
                if (Tools::getValue('scope')) {
                    $this->getDatabase()->execute('TRUNCATE TABLE ' . _DB_PREFIX_ . 'layered_filter');
                    $categories = $this->getDatabase()->executeS(
                        'SELECT id_category FROM ' . _DB_PREFIX_ . 'category'
                    );

                    foreach ($categories as $category) {
                        $_POST['categoryBox'][] = (int) $category['id_category'];
                    }
                }

                // Associate Shops
                if (isset($_POST['checkBoxShopAsso_layered_filter'])) {
                    $shopList = [];
                    foreach ($_POST['checkBoxShopAsso_layered_filter'] as $idShop => $row) {
                        $assos[] = ['id_shop' => (int) $idShop];
                        $shopList[] = (int) $idShop;
                    }
                } else {
                    $shopList = [(int) $this->getContext()->shop->id];
                }

                if (!empty($_POST['categoryBox']) && is_array($_POST['categoryBox'])) {
                    /* Clean categoryBox before use */
                    $_POST['categoryBox'] = array_map('intval', $_POST['categoryBox']);
                    $filterValues = [
                        'shop_list' => $shopList,
                    ];

                    foreach ($_POST['categoryBox'] as $idCategoryLayered) {
                        $filterValues['categories'][] = $idCategoryLayered;
                    }

                    foreach ($_POST as $key => $value) {
                        if (!preg_match('~^(?P<key>layered_selection_.*)(?<!_filter_)(?<!type)(?<!show_limit)$~', $key, $matches)) {
                            continue;
                        }

                        $filterValues[$matches['key']] = [
                            'filter_type' => (int) Tools::getValue($matches['key'] . '_filter_type', 0),
                            'filter_show_limit' => (int) Tools::getValue($matches['key'] . '_filter_show_limit', 0),
                        ];
                    }

                    $values = [
                        'name' => pSQL(Tools::getValue('layered_tpl_name')),
                        'filters' => pSQL(serialize($filterValues)),
                        'n_categories' => (int) count($filterValues['categories']),
                    ];

                    if (!$idLayeredFilter) {
                        $values['date_add'] = date('Y-m-d H:i:s');
                        $sql = 'INSERT INTO ' . _DB_PREFIX_ . 'layered_filter ' .
                             '(name, filters, n_categories, date_add, id_layered_filter) ' .
                             'VALUES (' .
                             '"' . pSQL($values['name']) . '", ' .
                             '"' . $values['filters'] . '", ' .
                             '' . (int) $values['n_categories'] . ', ' .
                             '"' . pSQL($values['date_add']) . '", ' .
                             '' . $idLayeredFilter . ')';
                        $this->getDatabase()->execute($sql);
                        $idLayeredFilter = (int) $this->getDatabase()->Insert_ID();
                    } else {
                        $this->getDatabase()->execute(
                            'DELETE FROM ' . _DB_PREFIX_ . 'layered_filter_shop WHERE `id_layered_filter` = ' . (int) $idLayeredFilter
                        );
                        $sql = 'UPDATE ' . _DB_PREFIX_ . 'layered_filter ' .
                             'SET name = "' . pSQL($values['name']) . '", ' .
                             'filters = "' . $values['filters'] . '", ' .
                             'n_categories = ' . (int) $values['n_categories'] . ' ' .
                             'WHERE id_layered_filter = ' . $idLayeredFilter;
                        $this->getDatabase()->execute($sql);
                    }

                    if (isset($assos)) {
                        foreach ($assos as $asso) {
                            $this->getDatabase()->execute(
                                'INSERT INTO ' . _DB_PREFIX_ . 'layered_filter_shop (`id_layered_filter`, `id_shop`)
    VALUES(' . $idLayeredFilter . ', ' . (int) $asso['id_shop'] . ')'
                            );
                        }
                    }

                    $this->buildLayeredCategories();
                    $message = $this->displayConfirmation(
                        $this->trans('Your filter', [], 'Modules.Facetedsearch.Admin') . ' "' .
                        Tools::safeOutput(Tools::getValue('layered_tpl_name')) . '" ' .
                        (
                            !empty($_POST['id_layered_filter']) ?
                            $this->trans('was updated successfully.', [], 'Modules.Facetedsearch.Admin') :
                            $this->trans('was added successfully.', [], 'Modules.Facetedsearch.Admin')
                        )
                    );
                }
            }
        } elseif (Tools::isSubmit('submitLayeredSettings')) {
            Configuration::updateValue('PS_LAYERED_CACHE_ENABLED', (int) Tools::getValue('ps_layered_cache_enabled'));
            Configuration::updateValue('PS_LAYERED_SHOW_QTIES', (int) Tools::getValue('ps_layered_show_qties'));
            Configuration::updateValue('PS_LAYERED_FULL_TREE', (int) Tools::getValue('ps_layered_full_tree'));
            Configuration::updateValue('PS_LAYERED_FILTER_PRICE_USETAX', (int) Tools::getValue('ps_layered_filter_price_usetax'));
            Configuration::updateValue('PS_LAYERED_FILTER_CATEGORY_DEPTH', (int) Tools::getValue('ps_layered_filter_category_depth'));
            Configuration::updateValue('PS_LAYERED_FILTER_PRICE_ROUNDING', (int) Tools::getValue('ps_layered_filter_price_rounding'));
            Configuration::updateValue('PS_LAYERED_FILTER_SHOW_OUT_OF_STOCK_LAST', (int) Tools::getValue('ps_layered_filter_show_out_of_stock_last'));
            Configuration::updateValue('PS_LAYERED_FILTER_BY_DEFAULT_CATEGORY', (int) Tools::getValue('ps_layered_filter_by_default_category'));

            $this->psLayeredFullTree = (int) Tools::getValue('ps_layered_full_tree');

            $message = '<div class="alert alert-success">' . $this->trans('Settings saved successfully', [], 'Modules.Facetedsearch.Admin') . '</div>';
            $this->invalidateLayeredFilterBlockCache();
        } elseif (Tools::getValue('deleteFilterTemplate')) {
            $layered_values = $this->getDatabase()->getValue(
                'SELECT filters
                FROM ' . _DB_PREFIX_ . 'layered_filter
                WHERE id_layered_filter = ' . (int) Tools::getValue('id_layered_filter')
            );

            if ($layered_values) {
                $this->getDatabase()->execute(
                    'DELETE FROM ' . _DB_PREFIX_ . 'layered_filter
                    WHERE id_layered_filter = ' . (int) Tools::getValue('id_layered_filter') . ' LIMIT 1'
                );
                $this->buildLayeredCategories();
                $message = $this->displayConfirmation($this->trans('Filter template deleted, categories updated (reverted to default Filter template).', [], 'Modules.Facetedsearch.Admin'));
            } else {
                $message = $this->displayError($this->trans('Filter template not found', [], 'Modules.Facetedsearch.Admin'));
            }
        }

        $categoryBox = [];
        $attributeGroups = $this->getDatabase()->executeS(
            'SELECT ag.id_attribute_group, ag.is_color_group, agl.name, COUNT(DISTINCT(a.id_attribute)) n
            FROM ' . _DB_PREFIX_ . 'attribute_group ag
            LEFT JOIN ' . _DB_PREFIX_ . 'attribute_group_lang agl ON (agl.id_attribute_group = ag.id_attribute_group)
            LEFT JOIN ' . _DB_PREFIX_ . 'attribute a ON (a.id_attribute_group = ag.id_attribute_group)
            WHERE agl.id_lang = ' . (int) $cookie->id_lang . '
            GROUP BY ag.id_attribute_group'
        );

        $features = $this->getDatabase()->executeS(
            'SELECT fl.id_feature, fl.name, COUNT(DISTINCT(fv.id_feature_value)) n
            FROM ' . _DB_PREFIX_ . 'feature_lang fl
            LEFT JOIN ' . _DB_PREFIX_ . 'feature_value fv ON (fv.id_feature = fl.id_feature)
            WHERE fl.id_lang = ' . (int) $cookie->id_lang . '
            GROUP BY fl.id_feature'
        );

        if (Shop::isFeatureActive() && count(Shop::getShops(true, null, true)) > 1) {
            $helper = new HelperForm();
            $helper->id = Tools::getValue('id_layered_filter', null);
            $helper->table = 'layered_filter';
            $helper->identifier = 'id_layered_filter';
            $this->context->smarty->assign('asso_shops', $helper->renderAssoShop());
        }

        $treeCategoriesHelper = new HelperTreeCategories('categories-treeview');
        $treeCategoriesHelper->setRootCategory((Shop::getContext() == Shop::CONTEXT_SHOP ? Category::getRootCategory()->id_category : 0))
                                                                     ->setUseCheckBox(true);

        $moduleUrl = Tools::getProtocol(Tools::usingSecureMode()) . $_SERVER['HTTP_HOST'] . $this->getPathUri();

        if (method_exists($this->context->controller, 'addJquery')) {
            $this->context->controller->addJS(_PS_JS_DIR_ . 'jquery/plugins/jquery.sortable.js');
        }

        $this->context->controller->addJS($this->_path . 'views/dist/back.js');
        $this->context->controller->addCSS($this->_path . 'views/dist/back.css');

        if (Tools::getValue('add_new_filters_template')) {
            $this->context->smarty->assign([
                'current_url' => $this->context->link->getAdminLink('AdminModules') . '&configure=ps_facetedsearch&tab_module=front_office_features&module_name=ps_facetedsearch',
                'uri' => $this->getPathUri(),
                'id_layered_filter' => 0,
                'template_name' => sprintf($this->trans('My template - %s', [], 'Modules.Facetedsearch.Admin'), date('Y-m-d')),
                'attribute_groups' => $attributeGroups,
                'features' => $features,
                'total_filters' => 6 + count($attributeGroups) + count($features),
            ]);

            $this->context->smarty->assign('categories_tree', $treeCategoriesHelper->render());

            return $this->display(__FILE__, 'views/templates/admin/add.tpl');
        }

        if (Tools::getValue('edit_filters_template')) {
            $idLayeredFilter = (int) Tools::getValue('id_layered_filter');
            $template = $this->getDatabase()->getRow(
                'SELECT *
                FROM `' . _DB_PREFIX_ . 'layered_filter`
                WHERE id_layered_filter = ' . $idLayeredFilter
            );

            if (!empty($template)) {
                $filters = Tools::unSerialize($template['filters']);
                $treeCategoriesHelper->setSelectedCategories($filters['categories']);
                $this->context->smarty->assign('categories_tree', $treeCategoriesHelper->render());

                $selectShops = $filters['shop_list'];
                unset($filters['categories']);
                unset($filters['shop_list']);

                $this->context->smarty->assign([
                    'current_url' => $this->context->link->getAdminLink('AdminModules') . '&configure=ps_facetedsearch&tab_module=front_office_features&module_name=ps_facetedsearch',
                    'uri' => $this->getPathUri(),
                    'id_layered_filter' => $idLayeredFilter,
                    'template_name' => $template['name'],
                    'attribute_groups' => $attributeGroups,
                    'features' => $features,
                    'filters' => $filters,
                    'total_filters' => 6 + count($attributeGroups) + count($features),
                    'default_filters' => $this->getDefaultFilters(),
                ]);

                return $this->display(__FILE__, 'views/templates/admin/view.tpl');
            }
        }
        if(!isset($this->psLayeredFullTree)) {
            $this->psLayeredFullTree = (int) Configuration::get('PS_LAYERED_FULL_TREE');;
        }

        $this->context->smarty->assign([
            'message' => $message,
            'uri' => $this->getPathUri(),
            'PS_LAYERED_INDEXED' => (int) Configuration::getGlobalValue('PS_LAYERED_INDEXED'),
            'current_url' => Tools::safeOutput(preg_replace('/&deleteFilterTemplate=[0-9]*&id_layered_filter=[0-9]*/', '', $_SERVER['REQUEST_URI'])),
            'id_lang' => $this->getContext()->cookie->id_lang,
            'token' => substr(Tools::hash('ps_facetedsearch/index'), 0, 10),
            'base_folder' => urlencode(_PS_ADMIN_DIR_),
            'price_indexer_url' => $moduleUrl . 'ps_facetedsearch-price-indexer.php' . '?token=' . substr(Tools::hash('ps_facetedsearch/index'), 0, 10),
            'full_price_indexer_url' => $moduleUrl . 'ps_facetedsearch-price-indexer.php' . '?token=' . substr(Tools::hash('ps_facetedsearch/index'), 0, 10) . '&full=1',
            'attribute_indexer_url' => $moduleUrl . 'ps_facetedsearch-attribute-indexer.php' . '?token=' . substr(Tools::hash('ps_facetedsearch/index'), 0, 10),
            'clear_cache_url' => $moduleUrl . 'ps_facetedsearch-clear-cache.php' . '?token=' . substr(Tools::hash('ps_facetedsearch/index'), 0, 10),
            'filters_templates' => $this->getDatabase()->executeS('SELECT * FROM ' . _DB_PREFIX_ . 'layered_filter ORDER BY date_add DESC'),
            'show_quantities' => Configuration::get('PS_LAYERED_SHOW_QTIES'),
            'cache_enabled' => Configuration::get('PS_LAYERED_CACHE_ENABLED'),
            'full_tree' => $this->psLayeredFullTree,
            'category_depth' => Configuration::get('PS_LAYERED_FILTER_CATEGORY_DEPTH'),
            'price_use_tax' => (bool) Configuration::get('PS_LAYERED_FILTER_PRICE_USETAX'),
            'limit_warning' => $this->displayLimitPostWarning(21 + count($attributeGroups) * 3 + count($features) * 3),
            'price_use_rounding' => (bool) Configuration::get('PS_LAYERED_FILTER_PRICE_ROUNDING'),
            'show_out_of_stock_last' => (bool) Configuration::get('PS_LAYERED_FILTER_SHOW_OUT_OF_STOCK_LAST'),
            'filter_by_default_category' => (bool) Configuration::get('PS_LAYERED_FILTER_BY_DEFAULT_CATEGORY'),
        ]);

        return $this->display(__FILE__, 'views/templates/admin/manage.tpl');
    }
}

the only modification made in the new field is the replacement of the request:

        $features = $this->getDatabase()->executeS(
            'SELECT fl.id_feature, fl.name, COUNT(DISTINCT(fv.id_feature_value)) n
            FROM ' . _DB_PREFIX_ . 'feature_lang fl
            LEFT JOIN ' . _DB_PREFIX_ . 'feature_value fv ON (fv.id_feature = fl.id_feature)
            WHERE (fv.custom IS NULL OR fv.custom = 0) AND fl.id_lang = ' . (int) $cookie->id_lang . '
            GROUP BY fl.id_feature'
        );

By:

        $features = $this->getDatabase()->executeS(
            'SELECT fl.id_feature, fl.name, COUNT(DISTINCT(fv.id_feature_value)) n
            FROM ' . _DB_PREFIX_ . 'feature_lang fl
            LEFT JOIN ' . _DB_PREFIX_ . 'feature_value fv ON (fv.id_feature = fl.id_feature)
            WHERE fl.id_lang = ' . (int) $cookie->id_lang . '
            GROUP BY fl.id_feature'
        );

All features are now accessible from the faceted module backoffice  😉

Edit : Attention ! All custom values will be displayed, even if several custom values are identical, they will all be displayed.

GaetanV

GaetanV

Problem fixed...

The faceted module does not allow searching on characteristics that do not have predefined values.

To avoid this behavior, you must authorize the module to list the characteristics that do not have default values by overriding the file:

modules\ps_facetedsearch\ps_facetedsearch.php

For this add the file:

override\modules\ps_facetedsearch\ps_facetedsearch.php

With this content:

<?php

class Ps_FacetedsearchOverride extends Ps_Facetedsearch {
    
    /**
     * @var int
     */
    private $psLayeredFullTree;

    public function getContent()
    {
        global $cookie;
        $message = '';

        if (Tools::isSubmit('SubmitFilter')) {
            if (!Tools::getValue('layered_tpl_name')) {
                $message = $this->displayError($this->trans('Filter template name required (cannot be empty)', [], 'Modules.Facetedsearch.Admin'));
            } elseif (!Tools::getValue('categoryBox')) {
                $message = $this->displayError($this->trans('You must select at least one category.', [], 'Modules.Facetedsearch.Admin'));
            } else {
                // Get or generate id
                $idLayeredFilter = (int) Tools::getValue('id_layered_filter');
                if (Tools::getValue('scope')) {
                    $this->getDatabase()->execute('TRUNCATE TABLE ' . _DB_PREFIX_ . 'layered_filter');
                    $categories = $this->getDatabase()->executeS(
                        'SELECT id_category FROM ' . _DB_PREFIX_ . 'category'
                    );

                    foreach ($categories as $category) {
                        $_POST['categoryBox'][] = (int) $category['id_category'];
                    }
                }

                // Associate Shops
                if (isset($_POST['checkBoxShopAsso_layered_filter'])) {
                    $shopList = [];
                    foreach ($_POST['checkBoxShopAsso_layered_filter'] as $idShop => $row) {
                        $assos[] = ['id_shop' => (int) $idShop];
                        $shopList[] = (int) $idShop;
                    }
                } else {
                    $shopList = [(int) $this->getContext()->shop->id];
                }

                if (!empty($_POST['categoryBox']) && is_array($_POST['categoryBox'])) {
                    /* Clean categoryBox before use */
                    $_POST['categoryBox'] = array_map('intval', $_POST['categoryBox']);
                    $filterValues = [
                        'shop_list' => $shopList,
                    ];

                    foreach ($_POST['categoryBox'] as $idCategoryLayered) {
                        $filterValues['categories'][] = $idCategoryLayered;
                    }

                    foreach ($_POST as $key => $value) {
                        if (!preg_match('~^(?P<key>layered_selection_.*)(?<!_filter_)(?<!type)(?<!show_limit)$~', $key, $matches)) {
                            continue;
                        }

                        $filterValues[$matches['key']] = [
                            'filter_type' => (int) Tools::getValue($matches['key'] . '_filter_type', 0),
                            'filter_show_limit' => (int) Tools::getValue($matches['key'] . '_filter_show_limit', 0),
                        ];
                    }

                    $values = [
                        'name' => pSQL(Tools::getValue('layered_tpl_name')),
                        'filters' => pSQL(serialize($filterValues)),
                        'n_categories' => (int) count($filterValues['categories']),
                    ];

                    if (!$idLayeredFilter) {
                        $values['date_add'] = date('Y-m-d H:i:s');
                        $sql = 'INSERT INTO ' . _DB_PREFIX_ . 'layered_filter ' .
                             '(name, filters, n_categories, date_add, id_layered_filter) ' .
                             'VALUES (' .
                             '"' . pSQL($values['name']) . '", ' .
                             '"' . $values['filters'] . '", ' .
                             '' . (int) $values['n_categories'] . ', ' .
                             '"' . pSQL($values['date_add']) . '", ' .
                             '' . $idLayeredFilter . ')';
                        $this->getDatabase()->execute($sql);
                        $idLayeredFilter = (int) $this->getDatabase()->Insert_ID();
                    } else {
                        $this->getDatabase()->execute(
                            'DELETE FROM ' . _DB_PREFIX_ . 'layered_filter_shop WHERE `id_layered_filter` = ' . (int) $idLayeredFilter
                        );
                        $sql = 'UPDATE ' . _DB_PREFIX_ . 'layered_filter ' .
                             'SET name = "' . pSQL($values['name']) . '", ' .
                             'filters = "' . $values['filters'] . '", ' .
                             'n_categories = ' . (int) $values['n_categories'] . ' ' .
                             'WHERE id_layered_filter = ' . $idLayeredFilter;
                        $this->getDatabase()->execute($sql);
                    }

                    if (isset($assos)) {
                        foreach ($assos as $asso) {
                            $this->getDatabase()->execute(
                                'INSERT INTO ' . _DB_PREFIX_ . 'layered_filter_shop (`id_layered_filter`, `id_shop`)
    VALUES(' . $idLayeredFilter . ', ' . (int) $asso['id_shop'] . ')'
                            );
                        }
                    }

                    $this->buildLayeredCategories();
                    $message = $this->displayConfirmation(
                        $this->trans('Your filter', [], 'Modules.Facetedsearch.Admin') . ' "' .
                        Tools::safeOutput(Tools::getValue('layered_tpl_name')) . '" ' .
                        (
                            !empty($_POST['id_layered_filter']) ?
                            $this->trans('was updated successfully.', [], 'Modules.Facetedsearch.Admin') :
                            $this->trans('was added successfully.', [], 'Modules.Facetedsearch.Admin')
                        )
                    );
                }
            }
        } elseif (Tools::isSubmit('submitLayeredSettings')) {
            Configuration::updateValue('PS_LAYERED_CACHE_ENABLED', (int) Tools::getValue('ps_layered_cache_enabled'));
            Configuration::updateValue('PS_LAYERED_SHOW_QTIES', (int) Tools::getValue('ps_layered_show_qties'));
            Configuration::updateValue('PS_LAYERED_FULL_TREE', (int) Tools::getValue('ps_layered_full_tree'));
            Configuration::updateValue('PS_LAYERED_FILTER_PRICE_USETAX', (int) Tools::getValue('ps_layered_filter_price_usetax'));
            Configuration::updateValue('PS_LAYERED_FILTER_CATEGORY_DEPTH', (int) Tools::getValue('ps_layered_filter_category_depth'));
            Configuration::updateValue('PS_LAYERED_FILTER_PRICE_ROUNDING', (int) Tools::getValue('ps_layered_filter_price_rounding'));
            Configuration::updateValue('PS_LAYERED_FILTER_SHOW_OUT_OF_STOCK_LAST', (int) Tools::getValue('ps_layered_filter_show_out_of_stock_last'));
            Configuration::updateValue('PS_LAYERED_FILTER_BY_DEFAULT_CATEGORY', (int) Tools::getValue('ps_layered_filter_by_default_category'));

            $this->psLayeredFullTree = (int) Tools::getValue('ps_layered_full_tree');

            $message = '<div class="alert alert-success">' . $this->trans('Settings saved successfully', [], 'Modules.Facetedsearch.Admin') . '</div>';
            $this->invalidateLayeredFilterBlockCache();
        } elseif (Tools::getValue('deleteFilterTemplate')) {
            $layered_values = $this->getDatabase()->getValue(
                'SELECT filters
                FROM ' . _DB_PREFIX_ . 'layered_filter
                WHERE id_layered_filter = ' . (int) Tools::getValue('id_layered_filter')
            );

            if ($layered_values) {
                $this->getDatabase()->execute(
                    'DELETE FROM ' . _DB_PREFIX_ . 'layered_filter
                    WHERE id_layered_filter = ' . (int) Tools::getValue('id_layered_filter') . ' LIMIT 1'
                );
                $this->buildLayeredCategories();
                $message = $this->displayConfirmation($this->trans('Filter template deleted, categories updated (reverted to default Filter template).', [], 'Modules.Facetedsearch.Admin'));
            } else {
                $message = $this->displayError($this->trans('Filter template not found', [], 'Modules.Facetedsearch.Admin'));
            }
        }

        $categoryBox = [];
        $attributeGroups = $this->getDatabase()->executeS(
            'SELECT ag.id_attribute_group, ag.is_color_group, agl.name, COUNT(DISTINCT(a.id_attribute)) n
            FROM ' . _DB_PREFIX_ . 'attribute_group ag
            LEFT JOIN ' . _DB_PREFIX_ . 'attribute_group_lang agl ON (agl.id_attribute_group = ag.id_attribute_group)
            LEFT JOIN ' . _DB_PREFIX_ . 'attribute a ON (a.id_attribute_group = ag.id_attribute_group)
            WHERE agl.id_lang = ' . (int) $cookie->id_lang . '
            GROUP BY ag.id_attribute_group'
        );

        $features = $this->getDatabase()->executeS(
            'SELECT fl.id_feature, fl.name, COUNT(DISTINCT(fv.id_feature_value)) n
            FROM ' . _DB_PREFIX_ . 'feature_lang fl
            LEFT JOIN ' . _DB_PREFIX_ . 'feature_value fv ON (fv.id_feature = fl.id_feature)
            WHERE fl.id_lang = ' . (int) $cookie->id_lang . '
            GROUP BY fl.id_feature'
        );

        if (Shop::isFeatureActive() && count(Shop::getShops(true, null, true)) > 1) {
            $helper = new HelperForm();
            $helper->id = Tools::getValue('id_layered_filter', null);
            $helper->table = 'layered_filter';
            $helper->identifier = 'id_layered_filter';
            $this->context->smarty->assign('asso_shops', $helper->renderAssoShop());
        }

        $treeCategoriesHelper = new HelperTreeCategories('categories-treeview');
        $treeCategoriesHelper->setRootCategory((Shop::getContext() == Shop::CONTEXT_SHOP ? Category::getRootCategory()->id_category : 0))
                                                                     ->setUseCheckBox(true);

        $moduleUrl = Tools::getProtocol(Tools::usingSecureMode()) . $_SERVER['HTTP_HOST'] . $this->getPathUri();

        if (method_exists($this->context->controller, 'addJquery')) {
            $this->context->controller->addJS(_PS_JS_DIR_ . 'jquery/plugins/jquery.sortable.js');
        }

        $this->context->controller->addJS($this->_path . 'views/dist/back.js');
        $this->context->controller->addCSS($this->_path . 'views/dist/back.css');

        if (Tools::getValue('add_new_filters_template')) {
            $this->context->smarty->assign([
                'current_url' => $this->context->link->getAdminLink('AdminModules') . '&configure=ps_facetedsearch&tab_module=front_office_features&module_name=ps_facetedsearch',
                'uri' => $this->getPathUri(),
                'id_layered_filter' => 0,
                'template_name' => sprintf($this->trans('My template - %s', [], 'Modules.Facetedsearch.Admin'), date('Y-m-d')),
                'attribute_groups' => $attributeGroups,
                'features' => $features,
                'total_filters' => 6 + count($attributeGroups) + count($features),
            ]);

            $this->context->smarty->assign('categories_tree', $treeCategoriesHelper->render());

            return $this->display(__FILE__, 'views/templates/admin/add.tpl');
        }

        if (Tools::getValue('edit_filters_template')) {
            $idLayeredFilter = (int) Tools::getValue('id_layered_filter');
            $template = $this->getDatabase()->getRow(
                'SELECT *
                FROM `' . _DB_PREFIX_ . 'layered_filter`
                WHERE id_layered_filter = ' . $idLayeredFilter
            );

            if (!empty($template)) {
                $filters = Tools::unSerialize($template['filters']);
                $treeCategoriesHelper->setSelectedCategories($filters['categories']);
                $this->context->smarty->assign('categories_tree', $treeCategoriesHelper->render());

                $selectShops = $filters['shop_list'];
                unset($filters['categories']);
                unset($filters['shop_list']);

                $this->context->smarty->assign([
                    'current_url' => $this->context->link->getAdminLink('AdminModules') . '&configure=ps_facetedsearch&tab_module=front_office_features&module_name=ps_facetedsearch',
                    'uri' => $this->getPathUri(),
                    'id_layered_filter' => $idLayeredFilter,
                    'template_name' => $template['name'],
                    'attribute_groups' => $attributeGroups,
                    'features' => $features,
                    'filters' => $filters,
                    'total_filters' => 6 + count($attributeGroups) + count($features),
                    'default_filters' => $this->getDefaultFilters(),
                ]);

                return $this->display(__FILE__, 'views/templates/admin/view.tpl');
            }
        }
        if(!isset($this->psLayeredFullTree)) {
            $this->psLayeredFullTree = (int) Configuration::get('PS_LAYERED_FULL_TREE');;
        }

        $this->context->smarty->assign([
            'message' => $message,
            'uri' => $this->getPathUri(),
            'PS_LAYERED_INDEXED' => (int) Configuration::getGlobalValue('PS_LAYERED_INDEXED'),
            'current_url' => Tools::safeOutput(preg_replace('/&deleteFilterTemplate=[0-9]*&id_layered_filter=[0-9]*/', '', $_SERVER['REQUEST_URI'])),
            'id_lang' => $this->getContext()->cookie->id_lang,
            'token' => substr(Tools::hash('ps_facetedsearch/index'), 0, 10),
            'base_folder' => urlencode(_PS_ADMIN_DIR_),
            'price_indexer_url' => $moduleUrl . 'ps_facetedsearch-price-indexer.php' . '?token=' . substr(Tools::hash('ps_facetedsearch/index'), 0, 10),
            'full_price_indexer_url' => $moduleUrl . 'ps_facetedsearch-price-indexer.php' . '?token=' . substr(Tools::hash('ps_facetedsearch/index'), 0, 10) . '&full=1',
            'attribute_indexer_url' => $moduleUrl . 'ps_facetedsearch-attribute-indexer.php' . '?token=' . substr(Tools::hash('ps_facetedsearch/index'), 0, 10),
            'clear_cache_url' => $moduleUrl . 'ps_facetedsearch-clear-cache.php' . '?token=' . substr(Tools::hash('ps_facetedsearch/index'), 0, 10),
            'filters_templates' => $this->getDatabase()->executeS('SELECT * FROM ' . _DB_PREFIX_ . 'layered_filter ORDER BY date_add DESC'),
            'show_quantities' => Configuration::get('PS_LAYERED_SHOW_QTIES'),
            'cache_enabled' => Configuration::get('PS_LAYERED_CACHE_ENABLED'),
            'full_tree' => $this->psLayeredFullTree,
            'category_depth' => Configuration::get('PS_LAYERED_FILTER_CATEGORY_DEPTH'),
            'price_use_tax' => (bool) Configuration::get('PS_LAYERED_FILTER_PRICE_USETAX'),
            'limit_warning' => $this->displayLimitPostWarning(21 + count($attributeGroups) * 3 + count($features) * 3),
            'price_use_rounding' => (bool) Configuration::get('PS_LAYERED_FILTER_PRICE_ROUNDING'),
            'show_out_of_stock_last' => (bool) Configuration::get('PS_LAYERED_FILTER_SHOW_OUT_OF_STOCK_LAST'),
            'filter_by_default_category' => (bool) Configuration::get('PS_LAYERED_FILTER_BY_DEFAULT_CATEGORY'),
        ]);

        return $this->display(__FILE__, 'views/templates/admin/manage.tpl');
    }
}

the only modification made in the new field is the replacement of the request:

        $features = $this->getDatabase()->executeS(
            'SELECT fl.id_feature, fl.name, COUNT(DISTINCT(fv.id_feature_value)) n
            FROM ' . _DB_PREFIX_ . 'feature_lang fl
            LEFT JOIN ' . _DB_PREFIX_ . 'feature_value fv ON (fv.id_feature = fl.id_feature)
            WHERE (fv.custom IS NULL OR fv.custom = 0) AND fl.id_lang = ' . (int) $cookie->id_lang . '
            GROUP BY fl.id_feature'
        );

By:

        $features = $this->getDatabase()->executeS(
            'SELECT fl.id_feature, fl.name, COUNT(DISTINCT(fv.id_feature_value)) n
            FROM ' . _DB_PREFIX_ . 'feature_lang fl
            LEFT JOIN ' . _DB_PREFIX_ . 'feature_value fv ON (fv.id_feature = fl.id_feature)
            WHERE fl.id_lang = ' . (int) $cookie->id_lang . '
            GROUP BY fl.id_feature'
        );

All features are now accessible from the faceted module backoffice  😉

GaetanV

GaetanV

Problem fixed...

The faceted module does not allow searching on characteristics that do not have predefined values.

To avoid this behavior, you must authorize the module to list the characteristics that do not have default values by overriding the file:

modules\ps_facetedsearch\ps_facetedsearch.php

For this add the file:

override\modules\ps_facetedsearch\ps_facetedsearch.php

With this content:

<?php

class Ps_FacetedsearchOverride extends Ps_Facetedsearch {
    public function getContent()
    {
        global $cookie;
        $message = '';

        if (Tools::isSubmit('SubmitFilter')) {
            if (!Tools::getValue('layered_tpl_name')) {
                $message = $this->displayError($this->trans('Filter template name required (cannot be empty)', [], 'Modules.Facetedsearch.Admin'));
            } elseif (!Tools::getValue('categoryBox')) {
                $message = $this->displayError($this->trans('You must select at least one category.', [], 'Modules.Facetedsearch.Admin'));
            } else {
                // Get or generate id
                $idLayeredFilter = (int) Tools::getValue('id_layered_filter');
                if (Tools::getValue('scope')) {
                    $this->getDatabase()->execute('TRUNCATE TABLE ' . _DB_PREFIX_ . 'layered_filter');
                    $categories = $this->getDatabase()->executeS(
                        'SELECT id_category FROM ' . _DB_PREFIX_ . 'category'
                    );

                    foreach ($categories as $category) {
                        $_POST['categoryBox'][] = (int) $category['id_category'];
                    }
                }

                // Associate Shops
                if (isset($_POST['checkBoxShopAsso_layered_filter'])) {
                    $shopList = [];
                    foreach ($_POST['checkBoxShopAsso_layered_filter'] as $idShop => $row) {
                        $assos[] = ['id_shop' => (int) $idShop];
                        $shopList[] = (int) $idShop;
                    }
                } else {
                    $shopList = [(int) $this->getContext()->shop->id];
                }

                if (!empty($_POST['categoryBox']) && is_array($_POST['categoryBox'])) {
                    /* Clean categoryBox before use */
                    $_POST['categoryBox'] = array_map('intval', $_POST['categoryBox']);
                    $filterValues = [
                        'shop_list' => $shopList,
                    ];

                    foreach ($_POST['categoryBox'] as $idCategoryLayered) {
                        $filterValues['categories'][] = $idCategoryLayered;
                    }

                    foreach ($_POST as $key => $value) {
                        if (!preg_match('~^(?P<key>layered_selection_.*)(?<!_filter_)(?<!type)(?<!show_limit)$~', $key, $matches)) {
                            continue;
                        }

                        $filterValues[$matches['key']] = [
                            'filter_type' => (int) Tools::getValue($matches['key'] . '_filter_type', 0),
                            'filter_show_limit' => (int) Tools::getValue($matches['key'] . '_filter_show_limit', 0),
                        ];
                    }

                    $values = [
                        'name' => pSQL(Tools::getValue('layered_tpl_name')),
                        'filters' => pSQL(serialize($filterValues)),
                        'n_categories' => (int) count($filterValues['categories']),
                    ];

                    if (!$idLayeredFilter) {
                        $values['date_add'] = date('Y-m-d H:i:s');
                        $sql = 'INSERT INTO ' . _DB_PREFIX_ . 'layered_filter ' .
                             '(name, filters, n_categories, date_add, id_layered_filter) ' .
                             'VALUES (' .
                             '"' . pSQL($values['name']) . '", ' .
                             '"' . $values['filters'] . '", ' .
                             '' . (int) $values['n_categories'] . ', ' .
                             '"' . pSQL($values['date_add']) . '", ' .
                             '' . $idLayeredFilter . ')';
                        $this->getDatabase()->execute($sql);
                        $idLayeredFilter = (int) $this->getDatabase()->Insert_ID();
                    } else {
                        $this->getDatabase()->execute(
                            'DELETE FROM ' . _DB_PREFIX_ . 'layered_filter_shop WHERE `id_layered_filter` = ' . (int) $idLayeredFilter
                        );
                        $sql = 'UPDATE ' . _DB_PREFIX_ . 'layered_filter ' .
                             'SET name = "' . pSQL($values['name']) . '", ' .
                             'filters = "' . $values['filters'] . '", ' .
                             'n_categories = ' . (int) $values['n_categories'] . ' ' .
                             'WHERE id_layered_filter = ' . $idLayeredFilter;
                        $this->getDatabase()->execute($sql);
                    }

                    if (isset($assos)) {
                        foreach ($assos as $asso) {
                            $this->getDatabase()->execute(
                                'INSERT INTO ' . _DB_PREFIX_ . 'layered_filter_shop (`id_layered_filter`, `id_shop`)
    VALUES(' . $idLayeredFilter . ', ' . (int) $asso['id_shop'] . ')'
                            );
                        }
                    }

                    $this->buildLayeredCategories();
                    $message = $this->displayConfirmation(
                        $this->trans('Your filter', [], 'Modules.Facetedsearch.Admin') . ' "' .
                        Tools::safeOutput(Tools::getValue('layered_tpl_name')) . '" ' .
                        (
                            !empty($_POST['id_layered_filter']) ?
                            $this->trans('was updated successfully.', [], 'Modules.Facetedsearch.Admin') :
                            $this->trans('was added successfully.', [], 'Modules.Facetedsearch.Admin')
                        )
                    );
                }
            }
        } elseif (Tools::isSubmit('submitLayeredSettings')) {
            Configuration::updateValue('PS_LAYERED_CACHE_ENABLED', (int) Tools::getValue('ps_layered_cache_enabled'));
            Configuration::updateValue('PS_LAYERED_SHOW_QTIES', (int) Tools::getValue('ps_layered_show_qties'));
            Configuration::updateValue('PS_LAYERED_FULL_TREE', (int) Tools::getValue('ps_layered_full_tree'));
            Configuration::updateValue('PS_LAYERED_FILTER_PRICE_USETAX', (int) Tools::getValue('ps_layered_filter_price_usetax'));
            Configuration::updateValue('PS_LAYERED_FILTER_CATEGORY_DEPTH', (int) Tools::getValue('ps_layered_filter_category_depth'));
            Configuration::updateValue('PS_LAYERED_FILTER_PRICE_ROUNDING', (int) Tools::getValue('ps_layered_filter_price_rounding'));
            Configuration::updateValue('PS_LAYERED_FILTER_SHOW_OUT_OF_STOCK_LAST', (int) Tools::getValue('ps_layered_filter_show_out_of_stock_last'));
            Configuration::updateValue('PS_LAYERED_FILTER_BY_DEFAULT_CATEGORY', (int) Tools::getValue('ps_layered_filter_by_default_category'));

            $this->psLayeredFullTree = (int) Tools::getValue('ps_layered_full_tree');

            $message = '<div class="alert alert-success">' . $this->trans('Settings saved successfully', [], 'Modules.Facetedsearch.Admin') . '</div>';
            $this->invalidateLayeredFilterBlockCache();
        } elseif (Tools::getValue('deleteFilterTemplate')) {
            $layered_values = $this->getDatabase()->getValue(
                'SELECT filters
                FROM ' . _DB_PREFIX_ . 'layered_filter
                WHERE id_layered_filter = ' . (int) Tools::getValue('id_layered_filter')
            );

            if ($layered_values) {
                $this->getDatabase()->execute(
                    'DELETE FROM ' . _DB_PREFIX_ . 'layered_filter
                    WHERE id_layered_filter = ' . (int) Tools::getValue('id_layered_filter') . ' LIMIT 1'
                );
                $this->buildLayeredCategories();
                $message = $this->displayConfirmation($this->trans('Filter template deleted, categories updated (reverted to default Filter template).', [], 'Modules.Facetedsearch.Admin'));
            } else {
                $message = $this->displayError($this->trans('Filter template not found', [], 'Modules.Facetedsearch.Admin'));
            }
        }

        $categoryBox = [];
        $attributeGroups = $this->getDatabase()->executeS(
            'SELECT ag.id_attribute_group, ag.is_color_group, agl.name, COUNT(DISTINCT(a.id_attribute)) n
            FROM ' . _DB_PREFIX_ . 'attribute_group ag
            LEFT JOIN ' . _DB_PREFIX_ . 'attribute_group_lang agl ON (agl.id_attribute_group = ag.id_attribute_group)
            LEFT JOIN ' . _DB_PREFIX_ . 'attribute a ON (a.id_attribute_group = ag.id_attribute_group)
            WHERE agl.id_lang = ' . (int) $cookie->id_lang . '
            GROUP BY ag.id_attribute_group'
        );

        $features = $this->getDatabase()->executeS(
            'SELECT fl.id_feature, fl.name, COUNT(DISTINCT(fv.id_feature_value)) n
            FROM ' . _DB_PREFIX_ . 'feature_lang fl
            LEFT JOIN ' . _DB_PREFIX_ . 'feature_value fv ON (fv.id_feature = fl.id_feature)
            WHERE fl.id_lang = ' . (int) $cookie->id_lang . '
            GROUP BY fl.id_feature'
        );

        if (Shop::isFeatureActive() && count(Shop::getShops(true, null, true)) > 1) {
            $helper = new HelperForm();
            $helper->id = Tools::getValue('id_layered_filter', null);
            $helper->table = 'layered_filter';
            $helper->identifier = 'id_layered_filter';
            $this->context->smarty->assign('asso_shops', $helper->renderAssoShop());
        }

        $treeCategoriesHelper = new HelperTreeCategories('categories-treeview');
        $treeCategoriesHelper->setRootCategory((Shop::getContext() == Shop::CONTEXT_SHOP ? Category::getRootCategory()->id_category : 0))
                                                                     ->setUseCheckBox(true);

        $moduleUrl = Tools::getProtocol(Tools::usingSecureMode()) . $_SERVER['HTTP_HOST'] . $this->getPathUri();

        if (method_exists($this->context->controller, 'addJquery')) {
            $this->context->controller->addJS(_PS_JS_DIR_ . 'jquery/plugins/jquery.sortable.js');
        }

        $this->context->controller->addJS($this->_path . 'views/dist/back.js');
        $this->context->controller->addCSS($this->_path . 'views/dist/back.css');

        if (Tools::getValue('add_new_filters_template')) {
            $this->context->smarty->assign([
                'current_url' => $this->context->link->getAdminLink('AdminModules') . '&configure=ps_facetedsearch&tab_module=front_office_features&module_name=ps_facetedsearch',
                'uri' => $this->getPathUri(),
                'id_layered_filter' => 0,
                'template_name' => sprintf($this->trans('My template - %s', [], 'Modules.Facetedsearch.Admin'), date('Y-m-d')),
                'attribute_groups' => $attributeGroups,
                'features' => $features,
                'total_filters' => 6 + count($attributeGroups) + count($features),
            ]);

            $this->context->smarty->assign('categories_tree', $treeCategoriesHelper->render());

            return $this->display(__FILE__, 'views/templates/admin/add.tpl');
        }

        if (Tools::getValue('edit_filters_template')) {
            $idLayeredFilter = (int) Tools::getValue('id_layered_filter');
            $template = $this->getDatabase()->getRow(
                'SELECT *
                FROM `' . _DB_PREFIX_ . 'layered_filter`
                WHERE id_layered_filter = ' . $idLayeredFilter
            );

            if (!empty($template)) {
                $filters = Tools::unSerialize($template['filters']);
                $treeCategoriesHelper->setSelectedCategories($filters['categories']);
                $this->context->smarty->assign('categories_tree', $treeCategoriesHelper->render());

                $selectShops = $filters['shop_list'];
                unset($filters['categories']);
                unset($filters['shop_list']);

                $this->context->smarty->assign([
                    'current_url' => $this->context->link->getAdminLink('AdminModules') . '&configure=ps_facetedsearch&tab_module=front_office_features&module_name=ps_facetedsearch',
                    'uri' => $this->getPathUri(),
                    'id_layered_filter' => $idLayeredFilter,
                    'template_name' => $template['name'],
                    'attribute_groups' => $attributeGroups,
                    'features' => $features,
                    'filters' => $filters,
                    'total_filters' => 6 + count($attributeGroups) + count($features),
                    'default_filters' => $this->getDefaultFilters(),
                ]);

                return $this->display(__FILE__, 'views/templates/admin/view.tpl');
            }
        }

        $this->context->smarty->assign([
            'message' => $message,
            'uri' => $this->getPathUri(),
            'PS_LAYERED_INDEXED' => (int) Configuration::getGlobalValue('PS_LAYERED_INDEXED'),
            'current_url' => Tools::safeOutput(preg_replace('/&deleteFilterTemplate=[0-9]*&id_layered_filter=[0-9]*/', '', $_SERVER['REQUEST_URI'])),
            'id_lang' => $this->getContext()->cookie->id_lang,
            'token' => substr(Tools::hash('ps_facetedsearch/index'), 0, 10),
            'base_folder' => urlencode(_PS_ADMIN_DIR_),
            'price_indexer_url' => $moduleUrl . 'ps_facetedsearch-price-indexer.php' . '?token=' . substr(Tools::hash('ps_facetedsearch/index'), 0, 10),
            'full_price_indexer_url' => $moduleUrl . 'ps_facetedsearch-price-indexer.php' . '?token=' . substr(Tools::hash('ps_facetedsearch/index'), 0, 10) . '&full=1',
            'attribute_indexer_url' => $moduleUrl . 'ps_facetedsearch-attribute-indexer.php' . '?token=' . substr(Tools::hash('ps_facetedsearch/index'), 0, 10),
            'clear_cache_url' => $moduleUrl . 'ps_facetedsearch-clear-cache.php' . '?token=' . substr(Tools::hash('ps_facetedsearch/index'), 0, 10),
            'filters_templates' => $this->getDatabase()->executeS('SELECT * FROM ' . _DB_PREFIX_ . 'layered_filter ORDER BY date_add DESC'),
            'show_quantities' => Configuration::get('PS_LAYERED_SHOW_QTIES'),
            'cache_enabled' => Configuration::get('PS_LAYERED_CACHE_ENABLED'),
            'full_tree' => $this->psLayeredFullTree,
            'category_depth' => Configuration::get('PS_LAYERED_FILTER_CATEGORY_DEPTH'),
            'price_use_tax' => (bool) Configuration::get('PS_LAYERED_FILTER_PRICE_USETAX'),
            'limit_warning' => $this->displayLimitPostWarning(21 + count($attributeGroups) * 3 + count($features) * 3),
            'price_use_rounding' => (bool) Configuration::get('PS_LAYERED_FILTER_PRICE_ROUNDING'),
            'show_out_of_stock_last' => (bool) Configuration::get('PS_LAYERED_FILTER_SHOW_OUT_OF_STOCK_LAST'),
            'filter_by_default_category' => (bool) Configuration::get('PS_LAYERED_FILTER_BY_DEFAULT_CATEGORY'),
        ]);

        return $this->display(__FILE__, 'views/templates/admin/manage.tpl');
    }
}

the only modification made in the new field is the replacement of the request:

        $features = $this->getDatabase()->executeS(
            'SELECT fl.id_feature, fl.name, COUNT(DISTINCT(fv.id_feature_value)) n
            FROM ' . _DB_PREFIX_ . 'feature_lang fl
            LEFT JOIN ' . _DB_PREFIX_ . 'feature_value fv ON (fv.id_feature = fl.id_feature)
            WHERE (fv.custom IS NULL OR fv.custom = 0) AND fl.id_lang = ' . (int) $cookie->id_lang . '
            GROUP BY fl.id_feature'
        );

By:

        $features = $this->getDatabase()->executeS(
            'SELECT fl.id_feature, fl.name, COUNT(DISTINCT(fv.id_feature_value)) n
            FROM ' . _DB_PREFIX_ . 'feature_lang fl
            LEFT JOIN ' . _DB_PREFIX_ . 'feature_value fv ON (fv.id_feature = fl.id_feature)
            WHERE fl.id_lang = ' . (int) $cookie->id_lang . '
            GROUP BY fl.id_feature'
        );

All features are now accessible from the faceted module backoffice  😉

×
×
  • Create New...