Всем привет. Недавно пришел заказ на backend-оптимизацию с описанием проблемы — тормозит страница редактирования товара. Так как на сайте используется много опций и аттрибутов, я стал грешить на них еще до проверки. Проверка xhprof дала понять, что задержка кроется именно в модели, в функциях получения опций и аттрибутов. Было принято решение сделать оптимизацию опций и аттрибутов, оптимизировать PHP/SQL код.
Аналогичная проблема была так же присуща страницам категорий/товаров/поиска/производителей витрины магазина и модулю экспорта CSV Product Export, потому что код везде один и тот же.
Решение
Из функций получения опция и аттрибутов убраны дополнительные SQL подзапросы, так как все необходимые данные можно получить в одном SQL запросе. Так же используются PHP указатели, что позволило обработать все данные в одном цикле. Этот вариант оптимизации подойдет для всех версий OpenCart.
Файл catalog/model/catalog/product.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
public function getProductAttributes($product_id) { $product_attribute_group_data = array(); $product_attribute_group_query = $this->db->query("SELECT a.attribute_id, ad.name as attribute_name, pa.text, ag.attribute_group_id, agd.name as attribute_group_name FROM " . DB_PREFIX . "product_attribute pa LEFT JOIN " . DB_PREFIX . "attribute a ON (pa.attribute_id = a.attribute_id) LEFT JOIN " . DB_PREFIX . "attribute_description ad ON (a.attribute_id = ad.attribute_id) LEFT JOIN " . DB_PREFIX . "attribute_group ag ON (a.attribute_group_id = ag.attribute_group_id) LEFT JOIN " . DB_PREFIX . "attribute_group_description agd ON (ag.attribute_group_id = agd.attribute_group_id) WHERE pa.product_id = " . (int)$product_id . " AND ad.language_id = agd.language_id AND pa.language_id = agd.language_id AND agd.language_id = " . (int)$this->config->get('config_language_id') . " ORDER BY ag.sort_order, agd.name, a.sort_order, ad.name"); $product_attribute_data = array(); foreach ($product_attribute_group_query->rows as $product_attribute) { $product_attribute_data[$product_attribute['attribute_group_id']][$product_attribute['attribute_id']] = array( 'attribute_id' => $product_attribute['attribute_id'], 'name' => $product_attribute['attribute_name'], 'text' => $product_attribute['text'] ); $product_attribute_group_data[$product_attribute['attribute_group_id']] = array( 'attribute_group_id' => $product_attribute['attribute_group_id'], 'name' => $product_attribute['attribute_group_name'], ); $product_attribute_group_data[$product_attribute['attribute_group_id']]['attribute'] =& $product_attribute_data[$product_attribute['attribute_group_id']]; } return $product_attribute_group_data; } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 |
public function getProductOptions($product_id) { $product_option_data = array(); $product_option_query = $this->db->query("SELECT pov.product_option_value_id, pov.option_value_id, ovd.name as product_option_value_name, od.name as option_name, ov.image, pov.quantity, pov.subtract, pov.price, pov.price_prefix, pov.points, pov.points_prefix, pov.weight, pov.weight_prefix, pov.product_option_id, pov.option_id, o.type, po.required FROM " . DB_PREFIX . "product_option_value pov LEFT JOIN " . DB_PREFIX . "option_value ov ON (pov.option_value_id = ov.option_value_id) LEFT JOIN " . DB_PREFIX . "option_value_description ovd ON (ov.option_value_id = ovd.option_value_id) LEFT JOIN " . DB_PREFIX . "product_option po ON (pov.product_option_id = po.product_option_id) LEFT JOIN `" . DB_PREFIX . "option` o ON (po.option_id = o.option_id) LEFT JOIN " . DB_PREFIX . "option_description od ON (o.option_id = od.option_id) WHERE po.product_id = " . (int)$product_id . " AND od.language_id = " . (int)$this->config->get('config_language_id') . " ORDER BY o.sort_order, ov.sort_order"); $product_option_value_data = array(); foreach ($product_option_query->rows as $product_option) { if (in_array($product_option['type'], array('select','radio','checkbox','image'))) { $product_option_value_data[$product_option['product_option_id']][] = array( 'product_option_value_id' => $product_option['product_option_value_id'], 'option_value_id' => $product_option['option_value_id'], 'name' => $product_option['product_option_value_name'], 'image' => $product_option['image'], 'quantity' => $product_option['quantity'], 'subtract' => $product_option['subtract'], 'price' => $product_option['price'], 'price_prefix' => $product_option['price_prefix'], 'points' => $product_option['points'], 'points_prefix' => $product_option['points_prefix'], 'weight' => $product_option['weight'], 'weight_prefix' => $product_option['weight_prefix'] ); $product_option_data[$product_option['option_id']] = array( 'product_option_id' => $product_option['product_option_id'], 'option_id' => $product_option['option_id'], 'name' => $product_option['option_name'], 'type' => $product_option['type'], 'required' => $product_option['required'] ); $product_option_data[$product_option['option_id']]['option_value'] =& $product_option_value_data[$product_option['product_option_id']]; }else{ $product_option_data[$product_option['option_id']] = array( 'product_option_id' => $product_option['product_option_id'], 'option_id' => $product_option['option_id'], 'name' => $product_option['option_name'], 'type' => $product_option['type'], 'option_value' => $product_option['option_value'], 'required' => $product_option['required'] ); } } return $product_option_data; } |
По аналогии поступил с админкой (только опции) и с модулем CSV Product Export. Будьте внимательны при использовании этого кода, он изменен, а значит и vqmod/ocmod модификации, которые работают с этим участком кода могут работать не так как ожидается.
Результат
- Страница административной панели стала грузиться на ~25 сек быстрей и занимала не более 1.5 секунд.
- Скорость загрузки страниц категорий/товаров/поиска/производителей снизилась на пару секунд.
- Модуль экспорта начал создавать файл с товарами всего за пару секунд.