如何在Opencart控制器上改进getTotalProducts

时间:2013-09-01 00:58:53

标签: php performance caching opencart

我之前不知道,但Opencart官方发布中存在一个巨大的错误,当你有很多产品时,它会在商店中出现。类别,需要50秒!加载页面。 50秒! ,我看看代码和谷歌,并发现问题已有详细记录,因为几乎每个人都知道这一行导致了一切。 (从缓存中计算)

$product_total = $this->model_catalog_product->getTotalProducts($data);

解决方案发布的每个地方都在注释掉这一行,顺便说一下,至少对我而言,如果我只是将$ product_total设置为空,那么效果会更好..就像这样

//$product_total = $this->model_catalog_product->getTotalProducts($data);

                $product_total = "";

无论如何我的问题已经解决了(页面加载时间为3秒而不是50秒),但计数丢失了所以我一直在寻找,最后我发现这个解决方案非常多,(直到现在,仍在测试)最佳解决方案如果您的数据库处理了大量产品和类别.. http://ergopho.be/speeding-up-opencart-using-the-cache-class/

它的作用基本上是将整个代码段包装在if块中,并首先检查文件是否存在于缓存中。如果没有,我们像平常一样运行它,然后将其存储到缓存中。如果是,请改用缓存版本。

在Controller / Common / Header.php中你也可以找到这段代码(在文章中他对类别做了)这里的代码也是

$ this-> data ['categories'] = array();

$categories = $this->model_catalog_category->getCategories(0);



        foreach ($categories as $category) {

            if ($category['top']) {

                // Level 2

                $children_data = array();



                $children = $this->model_catalog_category->getCategories($category['category_id']);



                foreach ($children as $child) {

                    $data = array(

                        'filter_category_id'  => $child['category_id'],

                        'filter_sub_category' => true

                    );



    $product_total = $this->model_catalog_product->getTotalProducts($data);


                    $children_data[] = array(

                        'name'  => $child['name'] . ($this->config->get('config_product_count') ? ' (' . $product_total . ')' : ''),

                        'href'  => $this->url->link('product/category', 'path=' . $category['category_id'] . '_' . $child['category_id'])

                    );                      

                }


Yo have to wrap all this code into this lines

$this->data['categories'] = $this->cache->get('categories');
if(!count($this->data['categories'])) { 

<!--Here goes the above code-->

$this->cache->set('categories', $this->data['categories']);
}

到目前为止它的工作正常,我希望这能帮助其他人,也请注意,如果你有更好的方法这样做,我知道有很多不是高级用户正在寻找这个,所以如果你可以分享它和我们一起,会很棒。

¿任何人都可以找到更好的解决这个可怕的慢速错误的方法吗?

感谢并希望下一版本的Opencart能够解决这类问题。

希望这会有所帮助。和平

1 个答案:

答案 0 :(得分:1)

这应该进入模型而不是控制器。

我的网站拥有超过15,000种有效产品。我的方法看起来像这样:

public function getTotalProducts($data = array()) {
    if ($this->customer->isLogged()):
        $customer_group_id = $this->customer->getCustomerGroupId();
    else:
        $customer_group_id = $this->config->get('config_customer_group_id');
    endif;  

    $sql = "SELECT COUNT(DISTINCT p.product_id) AS total"; 

    if (!empty($data['filter_category_id'])):
        if (!empty($data['filter_sub_category'])):
            $sql .= " FROM {$this->prefix}category_path cp 
                      LEFT JOIN {$this->prefix}product_to_category p2c 
                        ON (cp.category_id = p2c.category_id)";         
        else:
            $sql .= " FROM {$this->prefix}product_to_category p2c";
        endif;

        if (!empty($data['filter_filter'])):
            $sql .= " LEFT JOIN {$this->prefix}product_filter pf 
                        ON (p2c.product_id = pf.product_id) 
                      LEFT JOIN {$this->prefix}product p 
                        ON (pf.product_id = p.product_id)";
        else:
            $sql .= " LEFT JOIN {$this->prefix}product p 
                        ON (p2c.product_id = p.product_id)";
        endif;
    else:
        $sql .= " FROM {$this->prefix}product p";
    endif;

    $sql .= " LEFT JOIN {$this->prefix}product_description pd 
                ON (p.product_id = pd.product_id) 
              LEFT JOIN {$this->prefix}product_to_store p2s 
                ON (p.product_id = p2s.product_id) 
              WHERE pd.language_id = '" . (int)$this->config->get('config_language_id') . "' 
              AND p.status = '1' 
              AND p.date_available <= NOW() 
              AND p2s.store_id = '" . (int)$this->config->get('config_store_id') . "'";

    if (!empty($data['filter_category_id'])):
        if (!empty($data['filter_sub_category'])):
            $sql .= " AND cp.path_id = '" . (int)$data['filter_category_id'] . "'"; 
        else:
            $sql .= " AND p2c.category_id = '" . (int)$data['filter_category_id'] . "'";            
        endif;  

        if (!empty($data['filter_filter'])):
            $implode = array();

            $filters = explode(',', $data['filter_filter']);

            foreach ($filters as $filter_id):
                $implode[] = (int)$filter_id;
            endforeach;

            $sql .= " AND pf.filter_id IN (" . implode(',', $implode) . ")";                
        endif;
    endif;

    if (!empty($data['filter_name']) || !empty($data['filter_tag'])):
        $sql .= " AND (";

        if (!empty($data['filter_name'])):
            $implode = array();

            $words = explode(' ', trim(preg_replace('/\s\s+/', ' ', $data['filter_name'])));

            foreach ($words as $word):
                $implode[] = "pd.name LIKE '%" . $this->db->escape($word) . "%'";
            endforeach;

            if ($implode):
                $sql .= " " . implode(" AND ", $implode) . "";
            endif;

            if (!empty($data['filter_description'])):
                $sql .= " OR pd.description LIKE '%" . $this->db->escape($data['filter_name']) . "%'";
            endif;
        endif;

        if (!empty($data['filter_name']) && !empty($data['filter_tag'])):
            $sql .= " OR ";
        endif;

        if (!empty($data['filter_tag'])):
            $sql .= "pd.tag LIKE '%" . $this->db->escape(utf8_strtolower($data['filter_tag'])) . "%'";
        endif;

        if (!empty($data['filter_name'])):
            $sql .= " OR LCASE(p.model) = '" . $this->db->escape(utf8_strtolower($data['filter_name'])) . "'";
        endif;

        if (!empty($data['filter_name'])):
            $sql .= " OR LCASE(p.sku) = '" . $this->db->escape(utf8_strtolower($data['filter_name'])) . "'";
        endif;

        if (!empty($data['filter_name'])):
            $sql .= " OR LCASE(p.upc) = '" . $this->db->escape(utf8_strtolower($data['filter_name'])) . "'";
        endif;

        if (!empty($data['filter_name'])):
            $sql .= " OR LCASE(p.ean) = '" . $this->db->escape(utf8_strtolower($data['filter_name'])) . "'";
        endif;

        if (!empty($data['filter_name'])):
            $sql .= " OR LCASE(p.jan) = '" . $this->db->escape(utf8_strtolower($data['filter_name'])) . "'";
        endif;

        if (!empty($data['filter_name'])):
            $sql .= " OR LCASE(p.isbn) = '" . $this->db->escape(utf8_strtolower($data['filter_name'])) . "'";
        endif;

        if (!empty($data['filter_name'])):
            $sql .= " OR LCASE(p.mpn) = '" . $this->db->escape(utf8_strtolower($data['filter_name'])) . "'";
        endif;

        $sql .= ")";                
    endif;

    if (!empty($data['filter_manufacturer_id'])):
        $sql .= " AND p.manufacturer_id = '" . (int)$data['filter_manufacturer_id'] . "'";
    endif;

    $cache = md5 ((int)$customer_group_id . serialize ($data));

    $total = $this->cache->get('product.total.products.' . $cache);

    if (!$total):

        $query = $this->db->query($sql);
        $total = $query->row['total'];

        $this->cache->set('product.total.products.' . $cache, $total);

    endif;

    return $total;
}

你会注意到那里附近的缓存部分。

我还转换为memcached来缓存查询结果,而不是文件缓存。真正的问题出现在你为这么多产品使用seo_urls时,我不得不完全重写整个SeoUrl Controller和Url库,但它很快,而且我有很棒的网址:)