Prestashop : afficher les produits en promotion en premier

Noël est une période de l’année particulière et les enjeux sont énormes pour les boutiques e-commerce. Il faut pouvoir se démarquer de la concurrence, engager des moyens supplémentaires pour promouvoir les produits de la boutique et mettre en avant des tarifs compétitifs. Lorsqu’on commence à avoir une boutique composée de plusieurs dizaines de produits par catégorie, il devient assez difficile avec Prestashop de les classer en fonction de leur état. Le classement par défaut des produits se fait en fonction de leurs positions dans la catégorie mais il peut être modifié dans l’administration du site :

filtre-prestashop-defaut

Seulement les articles en promotion sont éparpillés dans la catégorie et je vois mal demander à mes clients de modifier l’emplacement de ces produits à la main à chaque événement. La solution est donc de modifier le filtre de base de façon à ce que les articles en promotion soient toujours affichés avant les autres, quelque soit le filtre sélectionné dans l’administration ou via le sélecteur de la catégorie.

Pour pouvoir le faire sur la version 1.6.1.0 de Prestashop, il faut faire une surcharge de la classe Category.php. Créez le fichier Category.php dans le dossier /override/classes puis collez-y le contenu ci-dessous.

<?php

    class Category extends CategoryCore
    {
        public function getProducts($id_lang, $p, $n, $order_by = null, $order_way = null, $get_total = false, $active = true, $random = false, $random_number_products = 1, $check_access = true, Context $context = null)
        {
            if (!$context)
                $context = Context::getContext();

            if ($check_access && !$this->checkAccess($context->customer->id))
                return false;

            $front = in_array($context->controller->controller_type, array('front', 'modulefront'));
            $id_supplier = (int)Tools::getValue('id_supplier');

            /** Return only the number of products */
            if ($get_total) {
                $sql = 'SELECT COUNT(cp.`id_product`) AS total
          FROM `' . _DB_PREFIX_ . 'product` p
          ' . Shop::addSqlAssociation('product', 'p') . '
          LEFT JOIN `' . _DB_PREFIX_ . 'category_product` cp ON p.`id_product` = cp.`id_product`
          WHERE cp.`id_category` = ' . (int)$this->id .
                    ($front ? ' AND product_shop.`visibility` IN ("both", "catalog")' : '') .
                    ($active ? ' AND product_shop.`active` = 1' : '') .
                    ($id_supplier ? 'AND p.id_supplier = ' . (int)$id_supplier : '');

                return (int)Db::getInstance(_PS_USE_SQL_SLAVE_)->getValue($sql);
            }

            if ($p < 1)
                $p = 1;

            /** Tools::strtolower is a fix for all modules which are now using lowercase values for 'orderBy' parameter */
            $order_by = Validate::isOrderBy($order_by) ? Tools::strtolower($order_by) : 'position';
            $order_way = Validate::isOrderWay($order_way) ? Tools::strtoupper($order_way) : 'ASC';

            $order_by_prefix = false;
            if ($order_by == 'id_product' || $order_by == 'date_add' || $order_by == 'date_upd')
                $order_by_prefix = 'p';
            elseif ($order_by == 'name')
                $order_by_prefix = 'pl';
            elseif ($order_by == 'manufacturer' || $order_by == 'manufacturer_name') {
                $order_by_prefix = 'm';
                $order_by = 'name';
            } elseif ($order_by == 'position')
                $order_by_prefix = 'cp';

            if ($order_by == 'price')
                $order_by = 'orderprice';

            $nb_days_new_product = Configuration::get('PS_NB_DAYS_NEW_PRODUCT');
            if (!Validate::isUnsignedInt($nb_days_new_product))
                $nb_days_new_product = 20;

            // promo filter
            $extra_order_by = '`reduction` DESC, ';
            $extra_select = ', sp.`reduction_type` as reduction';
            $extra_left_join = 'LEFT JOIN `' . _DB_PREFIX_ . 'specific_price` sp
        ON p.`id_product` = sp.`id_product`';

            $sql = 'SELECT p.*, product_shop.*, stock.out_of_stock, IFNULL(stock.quantity, 0) AS quantity' . (Combination::isFeatureActive() ? ', IFNULL(product_attribute_shop.id_product_attribute, 0) AS id_product_attribute,
          product_attribute_shop.minimal_quantity AS product_attribute_minimal_quantity' : '') . ', pl.`description`, pl.`description_short`, pl.`available_now`,
          pl.`available_later`, pl.`link_rewrite`, pl.`meta_description`, pl.`meta_keywords`, pl.`meta_title`, pl.`name`, image_shop.`id_image` id_image,
          il.`legend` as legend, m.`name` AS manufacturer_name, cl.`name` AS category_default,
          DATEDIFF(product_shop.`date_add`, DATE_SUB("' . date('Y-m-d') . ' 00:00:00",
          INTERVAL ' . (int)$nb_days_new_product . ' DAY)) > 0 AS new, product_shop.price AS orderprice' . $extra_select . '
        FROM `' . _DB_PREFIX_ . 'category_product` cp
        LEFT JOIN `' . _DB_PREFIX_ . 'product` p
          ON p.`id_product` = cp.`id_product`
        ' . Shop::addSqlAssociation('product', 'p') .
                (Combination::isFeatureActive() ? ' LEFT JOIN `' . _DB_PREFIX_ . 'product_attribute_shop` product_attribute_shop
        ON (p.`id_product` = product_attribute_shop.`id_product` AND product_attribute_shop.`default_on` = 1 AND product_attribute_shop.id_shop=' . (int)$context->shop->id . ')' : '') . '
        ' . Product::sqlStock('p', 0) . '
        LEFT JOIN `' . _DB_PREFIX_ . 'category_lang` cl
          ON (product_shop.`id_category_default` = cl.`id_category`
          AND cl.`id_lang` = ' . (int)$id_lang . Shop::addSqlRestrictionOnLang('cl') . ')
        LEFT JOIN `' . _DB_PREFIX_ . 'product_lang` pl
          ON (p.`id_product` = pl.`id_product`
          AND pl.`id_lang` = ' . (int)$id_lang . Shop::addSqlRestrictionOnLang('pl') . ')
        LEFT JOIN `' . _DB_PREFIX_ . 'image_shop` image_shop
          ON (image_shop.`id_product` = p.`id_product` AND image_shop.cover=1 AND image_shop.id_shop=' . (int)$context->shop->id . ')
        LEFT JOIN `' . _DB_PREFIX_ . 'image_lang` il
          ON (image_shop.`id_image` = il.`id_image`
          AND il.`id_lang` = ' . (int)$id_lang . ')
        LEFT JOIN `' . _DB_PREFIX_ . 'manufacturer` m
          ON m.`id_manufacturer` = p.`id_manufacturer`
          ' . $extra_left_join . '
        WHERE product_shop.`id_shop` = ' . (int)$context->shop->id . '
          AND cp.`id_category` = ' . (int)$this->id
                . ($active ? ' AND product_shop.`active` = 1' : '')
                . ($front ? ' AND product_shop.`visibility` IN ("both", "catalog")' : '')
                . ($id_supplier ? ' AND p.id_supplier = ' . (int)$id_supplier : '')
                . ' GROUP BY cp.id_product';

            if ($random === true)
                $sql .= ' ORDER BY RAND() LIMIT ' . (int)$random_number_products;
            else
                $sql .= ' ORDER BY ' . $extra_order_by . (!empty($order_by_prefix) ? $order_by_prefix . '.' : '') . '`' . bqSQL($order_by) . '` ' . pSQL($order_way) . '
      LIMIT ' . (((int)$p - 1) * (int)$n) . ',' . (int)$n;

            $result = Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS($sql, true, false);

            if (!$result)
                return array();

            if ($order_by == 'orderprice' && empty($extra_order_by))
                Tools::orderbyPrice($result, $order_way);

            /** Modify SQL result */
            return Product::getProductsProperties($id_lang, $result);
        }
    }

Explications

De base, la requête est s’occupe de classer les produits ne tient pas compte de leurs prix spécifiques, il convient alors d’ajouter la table dans la requête générale et d’y ajouter les filtres nécessaires. Ils ont été ajoutée à partir de la ligne 57 :

// promo filter
$extra_order_by = '`reduction` DESC, ';
$extra_select = ', sp.`reduction_type` as reduction';
$extra_left_join = 'LEFT JOIN `'._DB_PREFIX_.'specific_price` sp
    ON p.`id_product` = sp.`id_product`';

Naturellement, la requête SQL (variable $sql) a été modifiée pour intégrer ces nouveaux filtres.

Voici le résultat sur ma boutique test :

trie-produit-promo

Prestashop : supprimer l'étape de livraison
HTTPs : Mise en place d'un certificat SSL gratuit sur dédié (Apache)

4 Comments on “Prestashop : afficher les produits en promotion en premier”

  1. Bonjour
    Super article (et blog) !
    J’essaye de faire à peu prêt ça, mais seulement pour les articles dont le bandeau promo est activé.
    J’ai bien vu qu’il s’agissait du champ « on_sale » de la table « ps_product », mais ça ne m’avance pas vraiment. J’ai tenté un $extra_order_by = ‘`on_sale` DESC, ‘; sans résultat.

    Sauriez-vous comment faire ?

  2. Ah j’ai trouvé !
    J’ai commenté votre code ($extra_order_by,$extra_select,$extra_left_join) .

    Et changé ceci :
    $sql .= ‘ ORDER BY p.on_sale DESC, ‘ . (!empty($order_by_prefix) ? $order_by_prefix.’.’ :  »).’`’.bqSQL($order_by).’` ‘.pSQL($order_way).’
    LIMIT ‘.(((int)$p – 1) * (int)$n).’,’.(int)$n;

    Ca marche, j’ai maintenant mes promos en tête de liste de produits.

  3. Ce n’est pas un controller mais une classe et non, il est tout à fait normal de faire des requêtes SQL au sein des classes.
    Jetez un oeil au core de Prestashop, il y en a partout.

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *