Magento est une plateforme e-commerce professionnelle open source, développée en PHP et utilisant Zend Framework. Cependant, certains internautes sur Internet ont déclaré qu'il y avait un bug dans Magento 2.2.5 et 2.2.6. Après que le produit ait défini un prix spécial et l'ait supprimé, le tri des prix était erroné.
1. Description du problème :Les deux versions 2.2.5 et 2.2.6 ont ce problème, qui est un bug système de Magento2. Fixez un prix spécial pour le produit, par exemple 0,5 yuan. Lorsque ce produit est trié par prix de bas en haut, il sera classé en premier ; puis supprimera le prix spécial, l'enregistrera et reconstruira l'index pour cela. Le produit est correct, mais même s'il est affiché, le prix est inférieur à celui-ci, et ce produit occupe toujours la première place par ordre de prix.
2. Localisation du problème :
1. Il y a un problème avec le tri des prix. Il doit y avoir un problème avec le stockage des données. Tout d'abord, recherchez les tableaux de données liés aux prix dans la base de données. Examinez catalog_product_index_price et catalog_category_entity_decimal ensemble. On constate que parmi les produits problématiques, les valeurs de final_price max_price min_price sont toutes 0. Lorsqu'elles sont remplacées par la même valeur que. prix, le tri des prix est correct. La table en question est identifiée comme catalog_product_index_price.
2. Déterminez pourquoi la valeur de prix finale de catalog_product_index_price dans ce tableau est enregistrée à 0 lors de l'enregistrement du produit. La sauvegarde du produit est liée au fichier supplier/magento/module-catalog/Controller/Adminhtml/Product/Save.php. Le débogage du point d'arrêt n'a pas réussi à détecter l'opération de sauvegarde de catalog_product_index_price. Plus tard, un collègue m'a rappelé que le produit serait réindexé après avoir été enregistré. Un simple test a révélé que la table catalog_product_index_price était effectivement enregistrée lors de la réindexation.
3. Lors de la réindexation, le débogage du point d'arrêt obtient l'instruction SQL qui est finalement insérée dans la table de données. Tant que l'instruction SQL est analysée, la source du problème peut être déterminée. Le point de départ de la réindexation se trouve dans le fichier supplier/magento/module-indexer/Console/Command/IndexerReindexCommand.php, mais il existe de nombreux indexeurs. Il faut beaucoup d'efforts et de patience pour trouver avec précision l'opération de réindexation des prix. Enfin, recherchez le fichier supplier/magento/module-catalog/Model/ResourceModel/Product/Indexer/Price/SimpleProductPrice.php, à partir duquel vous pouvez obtenir la variable $query de l'instruction SQL insérée dans la table temporaire, copier l'instruction SQL et mettez-le dans Navicat pour exécution. Vous pouvez trouver dans les données à insérer, final_price est 0. Ce qui suit analyse principalement cette instruction SQL.
4. Comme le montre l'instruction SQL ci-dessous, cette instruction est très longue et complexe. Il est très difficile d'analyser clairement sa structure logique interne. Premièrement, il n'y a pas d'expérience correspondante dans l'analyse d'instructions SQL complexes. Deuxièmement, cela prend beaucoup de temps. Après avoir été démontré et rappelé par des collègues, j'ai découvert que l'analyse de cette instruction SQL n'est pas aussi difficile qu'on l'imagine, car quelle que soit la complexité de l'instruction, elle est composée d'instructions de base. Cependant, plusieurs couches d'imbrication y sont ajoutées et. le jugement de la déclaration if le rend visible. Cela semble très compliqué, mais la méthode d'analyse de base est toujours : « diviser pour mieux vaincre, vaincre chacun ». Cet idiome chinois contient une riche sagesse philosophique. J'ai découvert que tous les problèmes de la vie réelle peuvent être résolus. être résolu avec cette théorie. Ce qui suit explique en détail comment « diviser pour mieux régner » et comment « se vaincre les uns les autres ».
Diviser pour régner, c'est ignorer les éléments non pertinents de cette instruction SQL et se concentrer uniquement sur les données de base. Il y a un problème avec final_price. Voyons comment son final_price est trouvé et regardons la partie sur fond jaune. Sa couche la plus externe est une couche de jugement IFNULL. Cela signifie que si le premier paramètre est vrai, alors il se renvoie lui-même, sinon il renvoie le deuxième paramètre. Le final_price est maintenant 0, alors on peut juger que le premier paramètre doit être FALSE. . Mais le premier paramètre est un long paragraphe, alors analysons-le attentivement. Parce qu'il y a beaucoup de nids, il n'est pas facile d'y voir clair. Pour le moment, nous devons introduire des outils externes pour le formater afin que la structure hiérarchique soit plus claire et plus claire. il peut voir le début et la fin du support. PHPStorm est un outil très utile. Copiez-y ce code et formatez-le avant de l'analyser.
Cassez chacun d'entre eux, car le code implique une comparaison et une opération de valeurs, nous devons donc apprendre à imprimer et afficher ces différentes valeurs, puis les utiliser une par une pour la comparaison et l'analyse. Il n'est pas difficile de l'imprimer (de l'interroger). Il suffit de l'imiter lui-même. Vous pouvez l'interroger en utilisant l'instruction IFNULL ou IF.
5. Après l'analyse ci-dessus, il a finalement été déterminé que le problème réside dans un attribut de produit appelé special_from_date. Lorsque special_price est enregistré, la valeur de cet attribut est également enregistrée, mais lorsque special_price est supprimé, il n'est pas supprimé. Les données restantes affecteront le jugement de l'instruction SQL ci-dessus, faisant passer la valeur de final_price à 0.
6. Après avoir localisé le problème, c'est la solution finale. Remplacez la méthode d'exécution du fichier supplier/magento/module-catalog/Observer/SetSpecialPriceStartDate.php et remplacez-la par ce qui suit. Sa fonction est de sauvegarder special_from_date lorsqu'il y a special_price et de supprimer special_from_date lorsqu'il n'y a pas de special_price. Après mise à jour du code, le problème a été résolu.
/** * Set the current date to Special Price From attribute if it empty * * If special price was deleted, Special Price From attribute will be deleted * * (Important! Otherwise indexer would be confused) * * @param \Magento\Framework\Event\Observer $observer * @return $this */ public function execute(\Magento\Framework\Event\Observer $observer) { /** @var $product \Magento\Catalog\Model\Product */ $product = $observer->getEvent()->getProduct(); if ($product->getSpecialPrice() && !$product->getSpecialFromDate()) { $product->setData('special_from_date', $this->localeDate->date()); } elseif (!$product->getSpecialPrice() && $product->getSpecialFromDate()) { $product->unsetData('special_from_date'); } return $this; }
三、总结:经此,定位问题,解决问题的能力又获得一丁点的提升。主要是学会了对复杂SQL语句的初步分析,知道了IFNULL、IF、LEAST函数的使用,AND比OR的优先级要高的事实。
SQL语句:
INSERT INTO `catalog_product_index_price_temp` SELECT `e`.`entity_id`, `cg`.`customer_group_id`, `pw`.`website_id`, IF(IFNULL(tas_tax_class_id.value_id, -1) > 0, tas_tax_class_id.value, tad_tax_class_id.value) AS `tax_class_id`, IFNULL((ta_price.value), 0) AS `price`, IFNULL((LEAST(ta_price.value, IF(ta_special_price.value IS NOT NULL AND IF(IFNULL(tas_special_from_date.value_id, -1) > 0, tas_special_from_date.value, tad_special_from_date.value) IS NULL OR DATE(IF(IFNULL(tas_special_from_date.value_id, -1) > 0, tas_special_from_date.value, tad_special_from_date.value)) <= cwd.website_date AND IF(IFNULL(tas_special_to_date.value_id, -1) > 0, tas_special_to_date.value, tad_special_to_date.value) IS NULL OR DATE(IF(IFNULL(tas_special_to_date.value_id, -1) > 0, tas_special_to_date.value, tad_special_to_date.value)) >= cwd.website_date, ta_special_price.value, ~0), IFNULL((IF(tier_price_1.value_id is NULL AND tier_price_2.value_id is NULL AND tier_price_3.value_id is NULL AND tier_price_4.value_id is NULL, NULL, LEAST(IFNULL((IF(tier_price_1.value = 0, ROUND(ta_price.value * (1 - ROUND(tier_price_1.percentage_value * cwd.rate, 4) / 100), 4), ROUND(tier_price_1.value * cwd.rate, 4))), ~0), IFNULL((IF(tier_price_2.value = 0, ROUND(ta_price.value * (1 - ROUND(tier_price_2.percentage_value * cwd.rate, 4) / 100), 4), ROUND(tier_price_2.value * cwd.rate, 4))), ~0), IFNULL((IF(tier_price_3.value = 0, ROUND(ta_price.value * (1 - ROUND(tier_price_3.percentage_value * cwd.rate, 4) / 100), 4), ROUND(tier_price_3.value * cwd.rate, 4))), ~0), IFNULL((IF(tier_price_4.value = 0, ROUND(ta_price.value * (1 - ROUND(tier_price_4.percentage_value * cwd.rate, 4) / 100), 4), ROUND(tier_price_4.value * cwd.rate, 4))), ~0)))), ~0))), 0) AS `final_price`, IFNULL((LEAST(ta_price.value, IF(ta_special_price.value IS NOT NULL AND IF(IFNULL(tas_special_from_date.value_id, -1) > 0, tas_special_from_date.value, tad_special_from_date.value) IS NULL OR DATE(IF(IFNULL(tas_special_from_date.value_id, -1) > 0, tas_special_from_date.value, tad_special_from_date.value)) <= cwd.website_date AND IF(IFNULL(tas_special_to_date.value_id, -1) > 0, tas_special_to_date.value, tad_special_to_date.value) IS NULL OR DATE(IF(IFNULL(tas_special_to_date.value_id, -1) > 0, tas_special_to_date.value, tad_special_to_date.value)) >= cwd.website_date, ta_special_price.value, ~0), IFNULL((IF(tier_price_1.value_id is NULL AND tier_price_2.value_id is NULL AND tier_price_3.value_id is NULL AND tier_price_4.value_id is NULL, NULL, LEAST(IFNULL((IF(tier_price_1.value = 0, ROUND(ta_price.value * (1 - ROUND(tier_price_1.percentage_value * cwd.rate, 4) / 100), 4), ROUND(tier_price_1.value * cwd.rate, 4))), ~0), IFNULL((IF(tier_price_2.value = 0, ROUND(ta_price.value * (1 - ROUND(tier_price_2.percentage_value * cwd.rate, 4) / 100), 4), ROUND(tier_price_2.value * cwd.rate, 4))), ~0), IFNULL((IF(tier_price_3.value = 0, ROUND(ta_price.value * (1 - ROUND(tier_price_3.percentage_value * cwd.rate, 4) / 100), 4), ROUND(tier_price_3.value * cwd.rate, 4))), ~0), IFNULL((IF(tier_price_4.value = 0, ROUND(ta_price.value * (1 - ROUND(tier_price_4.percentage_value * cwd.rate, 4) / 100), 4), ROUND(tier_price_4.value * cwd.rate, 4))), ~0)))), ~0))), 0) AS `min_price`, IFNULL((LEAST(ta_price.value, IF(ta_special_price.value IS NOT NULL AND IF(IFNULL(tas_special_from_date.value_id, -1) > 0, tas_special_from_date.value, tad_special_from_date.value) IS NULL OR DATE(IF(IFNULL(tas_special_from_date.value_id, -1) > 0, tas_special_from_date.value, tad_special_from_date.value)) <= cwd.website_date AND IF(IFNULL(tas_special_to_date.value_id, -1) > 0, tas_special_to_date.value, tad_special_to_date.value) IS NULL OR DATE(IF(IFNULL(tas_special_to_date.value_id, -1) > 0, tas_special_to_date.value, tad_special_to_date.value)) >= cwd.website_date, ta_special_price.value, ~0), IFNULL((IF(tier_price_1.value_id is NULL AND tier_price_2.value_id is NULL AND tier_price_3.value_id is NULL AND tier_price_4.value_id is NULL, NULL, LEAST(IFNULL((IF(tier_price_1.value = 0, ROUND(ta_price.value * (1 - ROUND(tier_price_1.percentage_value * cwd.rate, 4) / 100), 4), ROUND(tier_price_1.value * cwd.rate, 4))), ~0), IFNULL((IF(tier_price_2.value = 0, ROUND(ta_price.value * (1 - ROUND(tier_price_2.percentage_value * cwd.rate, 4) / 100), 4), ROUND(tier_price_2.value * cwd.rate, 4))), ~0), IFNULL((IF(tier_price_3.value = 0, ROUND(ta_price.value * (1 - ROUND(tier_price_3.percentage_value * cwd.rate, 4) / 100), 4), ROUND(tier_price_3.value * cwd.rate, 4))), ~0), IFNULL((IF(tier_price_4.value = 0, ROUND(ta_price.value * (1 - ROUND(tier_price_4.percentage_value * cwd.rate, 4) / 100), 4), ROUND(tier_price_4.value * cwd.rate, 4))), ~0)))), ~0))), 0) AS `max_price`, IF(tier_price_1.value_id is NULL AND tier_price_2.value_id is NULL AND tier_price_3.value_id is NULL AND tier_price_4.value_id is NULL, NULL, LEAST(IFNULL((IF(tier_price_1.value = 0, ROUND(ta_price.value * (1 - ROUND(tier_price_1.percentage_value * cwd.rate, 4) / 100), 4), ROUND(tier_price_1.value * cwd.rate, 4))), ~0), IFNULL((IF(tier_price_2.value = 0, ROUND(ta_price.value * (1 - ROUND(tier_price_2.percentage_value * cwd.rate, 4) / 100), 4), ROUND(tier_price_2.value * cwd.rate, 4))), ~0), IFNULL((IF(tier_price_3.value = 0, ROUND(ta_price.value * (1 - ROUND(tier_price_3.percentage_value * cwd.rate, 4) / 100), 4), ROUND(tier_price_3.value * cwd.rate, 4))), ~0), IFNULL((IF(tier_price_4.value = 0, ROUND(ta_price.value * (1 - ROUND(tier_price_4.percentage_value * cwd.rate, 4) / 100), 4), ROUND(tier_price_4.value * cwd.rate, 4))), ~0))) AS `tier_price` FROM `catalog_product_entity` AS `e` CROSS JOIN `customer_group` AS `cg` INNER JOIN `catalog_product_website` AS `pw` ON pw.product_id = e.entity_id INNER JOIN `catalog_product_index_website` AS `cwd` ON pw.website_id = cwd.website_id LEFT JOIN `catalog_product_index_tier_price` AS `tp` ON tp.entity_id = e.entity_id AND tp.customer_group_id = cg.customer_group_id AND tp.website_id = pw.website_id LEFT JOIN `catalog_product_entity_tier_price` AS `tier_price_1` ON tier_price_1.row_id = e.row_id AND tier_price_1.all_groups = 0 AND tier_price_1.customer_group_id = cg.customer_group_id AND tier_price_1.qty = 1 AND tier_price_1.website_id = 0 LEFT JOIN `catalog_product_entity_tier_price` AS `tier_price_2` ON tier_price_2.row_id = e.row_id AND tier_price_2.all_groups = 0 AND tier_price_2.customer_group_id = cg.customer_group_id AND tier_price_2.qty = 1 AND tier_price_2.website_id = pw.website_id LEFT JOIN `catalog_product_entity_tier_price` AS `tier_price_3` ON tier_price_3.row_id = e.row_id AND tier_price_3.all_groups = 1 AND tier_price_3.customer_group_id = 0 AND tier_price_3.qty = 1 AND tier_price_3.website_id = 0 LEFT JOIN `catalog_product_entity_tier_price` AS `tier_price_4` ON tier_price_4.row_id = e.row_id AND tier_price_4.all_groups = 1 AND tier_price_4.customer_group_id = 0 AND tier_price_4.qty = 1 AND tier_price_4.website_id = pw.website_id LEFT JOIN `catalog_product_entity_int` AS `tad_tax_class_id` ON tad_tax_class_id.row_id = e.row_id AND tad_tax_class_id.attribute_id = 149 AND tad_tax_class_id.store_id = 0 LEFT JOIN `catalog_product_entity_int` AS `tas_tax_class_id` ON tas_tax_class_id.row_id = e.row_id AND tas_tax_class_id.attribute_id = 149 AND tas_tax_class_id.store_id = cwd.default_store_id INNER JOIN `catalog_product_entity_int` AS `tad_status` ON tad_status.row_id = e.row_id AND tad_status.attribute_id = 97 AND tad_status.store_id = 0 LEFT JOIN `catalog_product_entity_int` AS `tas_status` ON tas_status.row_id = e.row_id AND tas_status.attribute_id = 97 AND tas_status.store_id = cwd.default_store_id LEFT JOIN `catalog_product_entity_decimal` AS `ta_price` ON ta_price.row_id = e.row_id AND ta_price.attribute_id = 77 AND ta_price.store_id = 0 LEFT JOIN `catalog_product_entity_decimal` AS `ta_special_price` ON ta_special_price.row_id = e.row_id AND ta_special_price.attribute_id = 78 AND ta_special_price.store_id = 0 LEFT JOIN `catalog_product_entity_datetime` AS `tad_special_from_date` ON tad_special_from_date.row_id = e.row_id AND tad_special_from_date.attribute_id = 79 AND tad_special_from_date.store_id = 0 LEFT JOIN `catalog_product_entity_datetime` AS `tas_special_from_date` ON tas_special_from_date.row_id = e.row_id AND tas_special_from_date.attribute_id = 79 AND tas_special_from_date.store_id = cwd.default_store_id LEFT JOIN `catalog_product_entity_datetime` AS `tad_special_to_date` ON tad_special_to_date.row_id = e.row_id AND tad_special_to_date.attribute_id = 80 AND tad_special_to_date.store_id = 0 LEFT JOIN `catalog_product_entity_datetime` AS `tas_special_to_date` ON tas_special_to_date.row_id = e.row_id AND tas_special_to_date.attribute_id = 80 AND tas_special_to_date.store_id = cwd.default_store_id WHERE ((IF(IFNULL(tas_status.value_id, -1) > 0, tas_status.value, tad_status.value) = 1) AND (e.type_id = 'simple') AND (e.entity_id BETWEEN 2 AND 21)) AND (e.created_in <= '1546224120') AND (e.updated_in > '1546224120')
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!