Magento是一款專業開源電子商務平台,採用php進行開發,使用Zend Framework框架。然而網路上有網友說Magento 2.2.5和2.2.6的bug 產品設定special price又刪除後價格排序有誤,小編把問題給總結了一下。
一、問題描述:版本2.2.5和2.2.6都有此問題,為Magento2的系統bug。為產品設定special price,例如0.5元,這個產品按價格由低到高排序時,排在首位;然後刪去special price,保存,重建索引後,此產品顯示的價格是正確的,但是即使有顯示的價格比它還低的,按價格排序這個產品依然排在第一位。
二、問題定位:
1、價格排序有問題,一定是資料保存有問題。先在資料庫裡找與價格有關的資料表,catalog_product_index_price 和 catalog_category_entity_decimal 放在一起看,發現有問題的產品中,final_price max_price min_price的值都為0,改為和price的值一樣時,價格排序正確。有此確定有問題的表出在 catalog_product_index_price。
2、確定產品保存時 catalog_product_index_price 這張表的final price的值為什麼會被儲存為0。產品保存與 vendor/magento/module-catalog/Controller/Adminhtml/Product/Save.php 這個檔案有關,斷點調試未能發現保存 catalog_product_index_price 的操作。後經同事提醒,產品保存後會進行 reindex 的操作,簡單測試發現 catalog_product_index_price 表確實是 reindex 時保存。
3、reindex時,斷點偵錯取得最終插入資料表的 SQL語句。只要分析SQL語句,就能確定問題的來源。 reindex的起始點在檔案 vendor/magento/module-indexer/Console/Command/IndexerReindexCommand.php,但是indexer有很多,要準確找到價格的reindex操作,需要花費很大的努力和耐心。最後找到檔案vendor/magento/module-catalog/Model/ResourceModel/Product/Indexer/Price/SimpleProductPrice.php ,從中可以取得插入臨時表的SQL語句$query 變量,複製SQL語句,放到Navicat中執行,可以發現要插入的資料中,final_price為0,以下主要分析這個SQL語句。
4、如下面所展示的SQL語句所示,這個語句很長而且很複雜,要分析清楚它內部的邏輯結構,一是沒有對應的分析複雜SQL語句的經驗而很是犯難,二是非常耗費時間。後經同事示範和提醒,發現分析這個SQL語句也沒有想像中那麼難,因為再複雜的語句都是由基本語句組合而成,不過其中加上了多層嵌套,if語句的判斷讓它看起來非常複雜罷了,基本的分析方法還是:“分而治之,各個擊破”,這句中國的成語包含了豐富的哲理智慧,我發現現實生活中的問題都可以用這套理論去解決。以下詳細說明如何“分而治之”,又如何“各個擊破”。
分而治之,就是把這段SQL語句中無關的東西撇去,只專注在核心的資料。 final_price有問題,我們就看它的final_price是怎麼查出來的,看黃色背景的部分。它最外面是一層IFNULL判斷,它的意思是第一個參數如果為真,那麼返回它本身,否則返回第二個參數,final_price現在為0,那麼就可以判斷,第一個參數一定為FALSE 。但是第一個參數是很長一大段,我們又來仔細分析它,因為嵌套很多,不容易看清楚,我們這時要引入外面的工具,把它格式化,層次結構分明一點,並且可以看到括弧的開頭和結束。 PHPStorm是一個很好用的工具,把這段程式碼都複製進去,並且格式化後再來分析。
各個擊破,因為程式碼中涉及到值的比較和運算,所以我們要學會將這些不同的值列印顯示出來,算出來後一個個拿來進行比較分析。列印(查詢)出來也不難,模仿它本身就好了,用IFNULL或IF語句,就可以查詢出來。
5、經過上面的分析,最後確定,問題出在一個叫 special_from_date 的產品屬性上。當儲存special_price 時,會將這個屬性的值也儲存起來,但是刪除 special_price 時卻沒有刪除,遺留的資料會影響上面SQL語句的判斷,進而導致final_price的值變成0。
6、定位了問題所在後,就是最終的解決。覆寫 vendor/magento/module-catalog/Observer/SetSpecialPriceStartDate.php 檔案的 execute方法,改為如下。它的功能是,當有 special_price時,就儲存special_from_date,沒有special_price時,就刪除speical_from_date。更新程式碼後,問題解決。
/** * 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')
以上是Magento 2.2.5與2.2.6的問題產品設定特價又刪除問題分析與解決的詳細內容。更多資訊請關注PHP中文網其他相關文章!