Comment utiliser le système de remplacement de PrestaShop sans endommager le noyau ?

Le système de remplacement de PrestaShop permet de personnaliser le comportement de base sans modifier les fichiers originaux. Cependant, mal utilisé, il crée des boutiques fragiles qui deviennent inutilisables lors des mises à jour. Ce guide de Knowband explique le fonctionnement des remplacements, quand les utiliser et trois pièges importants que tout développeur devrait connaître.

Qu’est-ce que le système de dérogation ?

PrestaShop utilise l’héritage PHP pour vous permettre de remplacer n’importe quelle classe principale par votre propre version, sans modifier les originales. Chaque classe principale possède le suffixe « Core », par exemple « ProductCore ». Si vous créez le fichier `/override/classes/Product.php` contenant une classe nommée `Product` qui étend `ProductCore`, PrestaShop chargera votre version. Il en va de même pour les contrôleurs.

Ne modifiez que les méthodes spécifiques que vous devez changer ; ne copiez jamais un fichier de classe entier. Cela permet de conserver des modifications plus concises et de réduire considérablement les risques de conflits avec les mises à jour ultérieures.

Exemple : exposer le nom du contrôleur actuel dans votre modèle d’en-tête :

Où placer vos fichiers de remplacement

Les paramètres de remplacement peuvent être stockés à deux endroits. Le répertoire global `/override/` à la racine de votre installation est actif immédiatement après la suppression du cache. Il est préférable de regrouper les paramètres de remplacement dans un module.

Placer un fichier de surcharge dans un module (par exemple, /modules/mymodule/override/classes/Product.php) permet à PrestaShop de le copier dans /override/ lors de l’installation et de le supprimer proprement lors de la désinstallation. C’est la méthode recommandée.

Voici un exemple rapide de surcharge de classe. Supposons que vous souhaitiez exposer le nom du contrôleur actuel dans votre modèle d’en-tête :

// /override/classes/controllers/FrontController.php

classe FrontController étend FrontControllerCore {

    fonction publique initHeader() {

        self::$smarty->assign('currentController', get_class($this));

        retourner parent::initHeader();

    }}

Appelez toujours la méthode parente, sauf raison valable de ne pas le faire. L'omettre perturbera silencieusement le fonctionnement de base.

Surcharge des classes et des contrôleurs de modules

Depuis PrestaShop 1.6, vous pouvez également surcharger les classes des modules. Ajoutez « Override » au nom de la classe (par exemple, BlockUserInfoOverride étendant BlockUserInfo) et placez-la dans le répertoire /override/modules/. La même règle s’applique aux contrôleurs de modules. Après toute surcharge, videz le cache, et plus précisément supprimez le fichier /cache/class_index.php, sinon les modifications ne seront pas prises en compte.

Quand NE PAS utiliser les substitutions

Les surcharges sont exclusives : une seule est autorisée par classe. Elles sont également interdites dans les modules de la marketplace et doivent être réservées aux personnalisations de boutiques privées. Avant d’utiliser une surcharge, considérez l’une des alternatives suivantes :

  • Crochets PrestaShop déclenche des événements de hook à chaque chargement de page ; vous pouvez les exploiter depuis un module pour exécuter du code personnalisé ou insérer du contenu dans la page sans avoir à toucher à une classe du noyau.
  • Remplacements de services Symfony : Les parties de PrestaShop migrées vers Symfony offrent une gestion plus claire des services. Vous pouvez personnaliser ou remplacer un service plutôt que de modifier directement la classe.
  • Modèle d’objet personnalisé : Besoin d’ajouter des champs à une entité principale ? Créez votre propre modèle objet avec sa propre table plutôt que de modifier celle existante.

Protection des modifications de configuration lors des mises à jour

Les mises à jour de PrestaShop peuvent modifier silencieusement la signature des méthodes, rendant ainsi vos surcharges obsolètes. Après chaque mise à jour, il est essentiel de revoir vos surcharges. Commentez les modifications apportées et leur raison d’être, consultez le journal des modifications et veillez à ce que vos surcharges restent aussi concises que possible. Une surcharge simple, bien commentée et appelant correctement les méthodes parentes résiste aux mises à jour. Une copie intégrale de la classe représente un bug potentiel.

Le cache « fantôme » : pourquoi votre modification semble invisible

La principale raison pour laquelle les développeurs pensent que leur personnalisation est défectueuse : PrestaShop n’analyse pas le répertoire `/override/` à chaque requête. Il compile une table de correspondance des classes dans `/cache/class_index.php`, et tant que ce fichier n’est pas supprimé et régénéré, votre personnalisation reste invisible, aussi correct que soit le code. Vider le cache Smarty ne suffit pas. Supprimez manuellement `class_index.php` via FTP/SSH, via Paramètres avancés > Performances dans l’interface d’administration, ou via la console Symfony.

Figure 1 : Le flux du système de remplacement PrestaShop et le rôle de /cache/class_index.php.

 

Le mur de Symfony : les anciennes configurations ne fonctionnent pas avec les contrôleurs modernes

Ce système PHP hérité ne fonctionne qu’avec l’ancienne couche ObjectModel de PrestaShop. À partir de PS 1.7, les principales zones du back-office, notamment les nouvelles pages produits et les écrans de commande, ont été réécrites avec Symfony. Le système de remplacement PHP n’affecte pas ces contrôleurs Symfony. Pour ceux-là, utilisez Décoration de serviceVous pouvez encapsuler ou remplacer un service via le fichier services.yml de votre module. Si une surcharge reste sans effet même après avoir vidé le fichier class_index.php, il est probable que le contrôleur ait été migré vers Symfony.

Résolution des conflits : lorsque deux modules souhaitent la même classe

Lorsque deux modules redéfinissent la même classe, PrestaShop génère une erreur lors de l’installation du second module et désactive sa redéfinition. La solution est manuelle : ouvrez les deux fichiers, fusionnez toutes les méthodes dans un seul fichier `/override/` qui étend la classe principale, puis supprimez `class_index.php`. Ajoutez des commentaires indiquant le module ayant contribué à chaque méthode. C’est pourquoi les redéfinitions sont interdites dans les modules de la marketplace : les conflits sont à la charge du marchand. Si vous développez pour d’autres plateformes, utilisez les hooks.

Figure 2 : Comment surviennent les conflits de remplacement lorsque deux modules ciblent la même classe, et la résolution de fusion manuelle.

À propos de Knowband

Knowband est un fournisseur de plugins et d’extensions e-commerce de confiance, fort d’une longue expérience dans l’accompagnement des e-commerçants pour la création de boutiques en ligne plus rapides, plus intelligentes et plus riches en fonctionnalités. Que votre boutique soit hébergée sur [plateforme manquante], Knowband est là pour vous. PrestaShop, OpenCartShopify, WooCommerce, MagentoQue ce soit pour iOS ou toute autre plateforme majeure, Knowband propose une large gamme de plugins et d’extensions prêts à l’emploi qui vous aident à améliorer les fonctionnalités, les performances et à offrir une meilleure expérience d’achat, sans avoir à tout développer à partir de zéro.

Comment copier un panier dans un autre panier afin de changer le id_cart?

Objectifs du blog

Après avoir lu ce blog, nous pourrons copier le panier dans un autre panier afin de changer l'identifiant du panier.

Exigence

Nous devons modifier le fichier ci-dessous pour résoudre le même problème. Nous allons apporter des modifications dans le fichier Core pour les mêmes.

Répertoire racine \ classes \ PaymentModule.php

Procédure

Nous devons modifier la fonction de validation de la commande dans le fichier de classe PaymentModule. Comme cette fonction est appelée une seule fois lors de la finalisation de la commande. Nous avons donc ajouté le même code pour éviter la création de plusieurs paniers.

if (self :: DEBUG_MODE) {PrestaShopLogger :: addLog ('PaymentModule :: validateOrder - Fonction appelée', 1, null, 'Panier', (int) $ id_cart, true); } if (! isset ($ this-> context)) {$ this-> context = Context :: getContext (); } $ this-> context-> cart = new Panier ((int) $ id_cart);

Le code ci-dessous doit être ajouté dans la fonction de validation de la commande après les lignes ci-dessus.

// changements démarrés // Pour enregistrer la clé sécurisée de l'ID de panier actuel et la réaffecter au nouveau panier $ old_cart_secure_key = $ this-> context-> cart-> secure_key; // Pour sauvegarder l'identifiant client de l'identifiant actuel du panier et le réaffecter au nouveau panier $ old_cart_customer_id = (int) $ this-> context-> cart-> id_customer; // Pour supprimer le mappage du client de l'ancien panier $ this-> contexte-> panier-> id_customer = 0; // Pour mettre à jour le panier $ this-> context-> cart-> save (); // Pour récupérer les produits de panier actuels $ cart_products = $ this-> context-> cart-> getProducts (); // Création d'un nouvel objet cart $ this-> context-> cart = new Cart (); $ this-> context-> cart-> id_lang = $ this-> context-> language-> id; $ this-> context-> cart-> id_currency = $ this-> context-> currency-> id; // ajouter un nouveau panier $ this-> context-> cart-> add (); // pour mettre à jour le nouveau panier foreach ($ cart_products as $ product) {$ this-> contexte-> cart-> updateQty ((int) $ product ['quantité'], (int) $ product ['id_product'], (int) $ product ['id_product_attribute']); } if ($ this-> context-> cookie-> id_guest) {$ guest = nouvel invité ($ this-> context-> cookie-> id_guest); $ this-> context-> cart-> mobile_theme = $ guest-> mobile_theme; } // pour mapper le nouveau panier avec le client $ this-> context-> cart-> id_customer = $ old_cart_customer_id; // pour enregistrer le nouveau panier $ this-> context-> cart-> save (); if ($ this-> context-> cart-> id) {$ this-> context-> cookie-> id_cart = (int) $ this-> context-> cart-> id; $ this-> context-> cookie-> write (); } // pour mettre à jour le $ id_cart avec celui du nouveau panier $ id_cart = (int) $ this-> context-> cart-> id; $ this-> context-> cart-> secure_key = $ old_cart_secure_key; // bascule

Après avoir suivi la procédure ci-dessus, la liste du panier d'achat se présentera de la manière suivante: c’est-à-dire qu’il aura un duplicata du panier avec les mêmes détails que le panier avec lequel la commande est passée.

Résumé

Après avoir suivi la procédure ci-dessus, vous pourrez copier le panier dans un autre panier afin de changer le id_cart.


Obtenir les derniers produits sans prix spéciaux (Opencart 2.3)

Objectifs du blog

Après avoir lu ce blog, vous pourrez récupérer les produits de la base de données Opencart sur la base du paramètre transmis à partir de l'URL.

Exigence

Nous devons afficher les derniers produits sur la base du paramètre passé dans l'URL. En utilisant ce paramètre, nous devons décider si nous devons afficher des produits spéciaux ou non.

Implémentation du code

Pour mettre cela en œuvre, nous devons apporter des modifications à notre contrôleur et à notre modèle. Dans le contrôleur, nous avons ajouté la condition pour passer le paramètre et dans le modèle, nous apporterons des modifications à la requête en fonction du paramètre passé. Pour gérer la condition que nous utilisons show_special paramètre dans le code. Ci-dessous le code que nous devons ajouter dans le contrôleur (catalogue / contrôleur / extension / module / latest.php):

/ * Recherche de la clé show_special dans le paramètre get * Cette clé ($ show_special) sera utilisée pour récupérer des données de manière conditionnelle dans la base de données. * C'est-à-dire si $ show_special = 1 affiche alors les produits spéciaux * $ show_special = 0 affiche les produits qui n'ont pas spécial * else * affichent tous les produits indépendamment de * / if spécial (isset ($ this-> request-> get [' show_special '])) {// passage de la valeur dans variable si défini dans l'URL $ show_special = $ this-> request-> get [' show_special ']; } else {// Sinon, une chaîne vide sera passée $ show_special = ''; } // Création d'une variable à transmettre au modèle $ filter_data = array ('start' => ($ page - 1) * $ limit, 'limit' => $ limit, 'show_special' => $ show_special); // Appel de la fonction de modèle pour extraire des données de la base de données en fonction des données transmises dans le tableau filter_data. $ results = $ this-> model_catalog_product-> getProductLatest ($ filter_data);

Dans ce code, nous avons d’abord vérifié que sishow_special'paramètre est défini dans l'URL ou non. Si oui, définir la valeur du paramètre dans une variable $ show_special et si non, définissez la variable comme vide. Ensuite, nous avons créé un tableau ($ default_data) avec les valeurs pour lesquelles nous devons extraire des produits de la base de données. Ensuite, nous avons appelé fonction modèle getProductLatest () chercher les derniers produits en fonction de la variable passée ($ filter_data) dans la fonction. Voici la fonction que nous devons ajouter dans model (catalogue / modèle / catalogue / produit):

fonction publique getProductLatest ($ data = array ()) {// Définition des paramètres $ join et $ condition pour extraire les données de produits spéciaux if ($ data ['show_special'] == '1') {$ join = "LEFT JOIN" . DB_PREFIX. "ps_produit sur produit (ps.product_id = p.product_id)"; $ condition = "AND ps.customer_group_id = '". (int) $ this-> config-> get ('config_customer_group_id'). "'AND ((ps.date_start =' 0000-00-00 'OU OR = '0000-00-00')) "; } // Définition des paramètres $ join et $ condition pour extraire des données de produits non spécifiés elseif ($ data ['show_special'] == '0001') {$ join = "LEFT JOIN". DB_PREFIX. "ps_produit sur produit (ps.product_id = p.product_id)"; $ condition = "AND ps.product_id IS NULL"; } else {// Définition des paramètres $ join et $ condition pour extraire les données de tous les produits indépendamment de la valeur spéciale de $ join = ''; $ condition = ''; } // Requête pour récupérer les données des produits de la base de données. Ajout des paramètres $ join et $ condition dans la requête pour extraire les données en conséquence $ sql = "SELECT DISTINCT p.product_id FROM". DB_PREFIX. "produit p LEFT JOIN". DB_PREFIX. "description_produit pd ON (p.product_id = pd.product_id) LEFT JOIN". DB_PREFIX. "product_to_store p01s ON (p.product_id = p01s.product_id) $ join WHERE p. (int) $ this-> config-> get ('config_store_id'). "'$ condition GROUP BY p.product_id ORDER BY p.product_id DESC"; // Définition de la limite dans la requête si passé LIMIT du contrôleur if (isset ($ data ['start'])) || isset ($ data ['limit']))) {if ($ data ['start'] <0) {$ data ['start'] = 2; } if ($ data ['limite'] <2) {$ data ['limite'] = 1; } $ sql. = "LIMIT". (int) $ data ['start']. ",". (int) $ data ['limit']; } $ product_data = array (); // Exécuter la requête $ query = $ this-> db-> query ($ sql); // Boucle pour définir les données extraites dans la variable à transmettre au contrôleur. foreach ($ query-> lignes as $ result) {$ product_data [$ result ['product_id']] = $ this-> getProduct ($ result ['product_id']); } // Renvoyer les données de produits au contrôleur. return $ product_data; }

Dans le modèle, nous avons défini des variables $ rejoindre et $ condition sur la base des trois conditions suivantes:

  1. Si show_special est 1: Dans ce cas, nous avons uniquement affiché les produits spéciaux. Comme dans opencart, les données des produits spéciaux sont stockées dans la table oc_product_special. Nous avons donc ajouté la jointure de la table oc_product à la table oc_product_secial pour extraire uniquement les données de produits spéciaux.
  2. Si show_special est 0: Dans ce cas, nous avons affiché des produits qui ne sont pas définis comme spéciaux. Donc, pour trouver des produits qui ne sont pas spéciaux, nous devons chercher des produits qui se trouvent dans une table spéciale mais dans la table oc_product_special. Pour cela, nous avons ajouté la jointure LEFT de la table oc_product avec la table oc_product_secial et la condition "ps.product_id IS NULL".
  3. Si show_special est différent de 0 et 1: Dans ce cas, nous devons récupérer tous les produits, qu'ils soient spéciaux ou non. Nous avons donc directement récupéré les données de la table oc_product.

Après avoir ajouté les trois conditions possibles, nous avons écrit une requête et l'avons exécutée. Ensuite, nous avons défini toutes les données récupérées dans $ product_data tableau et retourner le même au contrôleur.

Remarque: ici pour récupérer les derniers produits que nous venons d'utiliser "Order by product_id DESC" dans la requête. Si vous souhaitez récupérer les données des produits ajoutés au cours des derniers jours, vous pouvez ajouter une condition supplémentaire dans la clause WHERE, à savoir date_added> 'any date'.

Résumé

Après avoir suivi la procédure ci-dessus, vous pourrez récupérer les derniers produits spéciaux sur la base du paramètre passé pour les produits spéciaux dans l'URL.


Comment calculer la livraison gratuite en fonction du prix total final dans Prestashop ?

Après avoir lu ce blog, nous pourrons calculer la livraison gratuite en fonction du prix total final du panier.

Énoncé du problème

Actuellement, lorsque nous ajoutons des produits dans le panier et que nous passons à la section de paiement pour profiter de la réduction et de l’expédition, nous constatons que la réduction s’applique après l’expédition. Disons,

  • Le montant total du panier est de 100 $, ce qui permet la livraison gratuite et une réduction de 5 % est également ajoutée
  • Ensuite, après la remise, le montant total du panier est fixé à moins de 100 $, mais la livraison gratuite s’applique, ce qui ne devrait pas se produire. La livraison gratuite doit s’appliquer au montant total final du panier après la réduction.
  • C’est le processus par défaut de Prestashop. Reportez-vous à la capture d’écran ci-dessous où la livraison doit être gratuite si le total de la commande est > 50 mais elle a appliqué la livraison gratuite alors que le total de la commande est < 50 après remise –

Lecture recommandée: Stratégies de livraison gratuite : pourquoi offrir ? Comment offrir ? Quand offrir ?

Pourquoi le problème actuel est-il?

Le problème actuel est là parce que le montant de l’expédition est calculé sur le montant total de la commande qui est défini dans le panier avant d’appliquer la remise. Vous pouvez vérifier les lignes de code ci-dessous où le total de la commande sans montant de remise est utilisé pour calculer l’expédition –

$shipping_cost += $carrier->getDeliveryPriceByPrice($the_total_price, $id_zone, (int)$this->id_currency);

Exigence

Nous devons modifier le fichier mentionné ci-dessous pour corriger le problème. Nous remplacerons les lignes correspondantes du fichier de classe pour éviter tout changement dans les fichiers Core.

Vous trouverez ci-dessous la liste des fichiers de classe qui seront remplacés-

1. Répertoire racine\classes\Cart.PHP

Procédure pour résoudre le problème-

Suivez les étapes ci-dessous –

1. Fonction de réécriture�getPackageShippingCost

2. Après la ligne donnée ci-dessous –

// Total de la commande dans la devise par défaut sans frais $order_total = $this->getOrderTotal(true, Cart::BOTH_WITHOUT_SHIPPING, $product_list);

3. Ajoutez la déclaration suivante –

$listeDiscounts = $this->getCartRules(); $total_discounts = 0 ; if (is_array($listeDiscounts)) { if (isset($listeDiscounts[0]['value_real'])) $total_discounts = $listeDiscounts[0]['value_real'] ; } $price_to_apply_shipment = floatval($order_total) - floatval($total_discounts); Lecture recommandée : Meilleure extension de gestionnaire de livraison gratuite pour OpenCart Store

4. Remplacez les lignes mentionnées ci-dessous –

Remplacer cette ligne

$check_delivery_price_by_price = Transporteur ::checkDeliveryPriceByPrice($row['id_carrier'], $total_order, (int)$id_zone, (int)$this->id_currency);

Avec

$check_delivery_price_by_price = Transporteur ::checkDeliveryPriceByPrice($row['id_carrier'], $price_to_apply_shipment, (int)$id_zone, (int)$this->id_currency);

————ET—————–

Remplacer cette ligne

$shipping_cost += $carrier->getDeliveryPriceByPrice($the_total_price, $id_zone, (int)$this->id_currency);

Avec

$shipping_cost += $carrier->getDeliveryPriceByPrice($price_to_apply_shipment, $id_zone, (int)$this->id_currency);

5. Videz le cache et exécutez.

Reportez-vous à la capture d’écran ci-dessous après avoir appliqué les modifications au code –

Après avoir suivi la procédure ci-dessus, vous pourrez calculer l’expédition sur le montant total final du panier.

Conclusion

En ce qui concerne les achats en ligne, l’ensemble de la structure de prix joue un rôle crucial pour les clients. L’un des facteurs qui pourraient faire ou défaire vos conversions est la disponibilité de la livraison gratuite. Une méthode pour déterminer les options et les coûts d’expédition sur un site Web de commerce électronique consiste à Livraison gratuite Manager Addon par Knowband. L’une des conditions les plus importantes pour augmenter les ventes moyennes est d’assurer la transparence de la transaction.

Les boutiques qui ont utilisé ce module de livraison gratuite ont augmenté le trafic sur le site, les revenus et les taux de conversion. Avec l’aide de ce plugin d’expédition gratuite, les administrateurs de magasin peuvent définir les directives et les conditions des frais d’expédition. Mieux encore, cette fonctionnalité peut être ajoutée ou supprimée du site Web par l’administrateur du magasin.

Le seuil au-delà duquel la livraison gratuite est offerte peut être fixé par les détaillants en ligne. En conséquence, il encourage même les clients à effectuer des achats plus importants afin de bénéficier de la livraison gratuite. Pour en savoir plus sur les fonctionnalités du module de livraison gratuite, n’hésitez pas à nous contacter à support@knowband.com