Помощь SEO url стандартный функционал, принцип работы контроллера с ЧПУ, оптимизация

Тема в разделе "Opencart", создана пользователем madmaximus85, 12 янв 2014.

Информация :
Внимание форумчане! При создании тем, или выкладывании какой-либо информации проверьте в какой ветке форума вы находитесь! Не путайте Opencart и Opencart2. При несоблюдении данного условия выносится соответствующее наказание! И потом не говорите что вас НЕ ПРЕДУПРЕЖДАЛИ! По возможности используйте обменники mail, yandex, google, dropbox, rghost Дабы избежать просьб перезалить и проблем с рекламой!
Модераторы: ZiX
  1. madmaximus85

    madmaximus85 Постоялец

    Регистр.:
    30 июл 2012
    Сообщения:
    98
    Симпатии:
    51
    Думаю многим будет полезно узнать по какому принципу происходит построение и взаимодействие SEO url в стандартном наборе.
    Сейчас столкнулся с одной проблемой, для которой нужно четкое понимание механизма работы этой части движка.

    Как я понимаю упрощенная схема выглядит так:

    1. Пользователь отправляет запрос на сервер
    2. Включается дальше в работу haccess а именно он дает команду на взаимодействие с mod_rewrite
    3. дальше идет поиск по базе в таблице url_alias, где ищется соответствие и если оно находится то
    4. по типу ссылки (поле query) происходит обращение к нужной модели и контроллеру

    так вот при переезде и смене шаблона возникла проблема, на 1.5.6 где стоит стандартный шаблон то фильтр OC Filter работает успешно, а на новой версии при обращении к фильтру выдает 403 ошибку
    по адресу /cell_phones&filter_params=4:11 а вот если заменить амперсанд на ? /cell_phones?filter_params=4:11 то все успешно работает, хотя на предыдущей версии успешно работало и при адресе вида /cell_phones&filter_params=4:11

    Какая часть контроллера отвечает за добавление параметров запроса к уже готовому чпу?
     
  2. madmaximus85

    madmaximus85 Постоялец

    Регистр.:
    30 июл 2012
    Сообщения:
    98
    Симпатии:
    51
    После анализа лога дебаггера на сборке в которой также установлен Advanced SEO URL выяснилась интересная вещь из 450 запросов к б.д. основную часть (~300 запросов) берет seo_url.php

    По умолчанию система формирует ссылки в стандартном виде и только в режиме SEO URL они преобразуются функцией rewrite. Выглядит это следующим образом:
    1. Контроллер формирует контент с сылками, которые форматируются в библиотеке ./system/library/url.php. Если режим SEO URL активен (см. config_seo_url), то после каждого форматирования ссылки она разбивается на составные части, которые сверяются с содержимым таблицы базы данных url_alias в поиске значения keyword. Это значение закрепляется пользователем при редактировании категории, продукта, статьи или производителя.
    2. При преходе пользователем по SEO-ссылке со страниц магазина или с результатов выдачи поисковой системы происходит обратное преобразование ссылки в стандартный формат. Она также разбивается на части (разделителем служит прямой слэш), которые сверяются с таблицой url_alias в поиске идентификаторов категории, продукта, статьи или производителя.
    Независимо от того, присвоили ли вы ключевое слово (keyword) категории, продукту, статье или производителю, обращения к базе в этом режиме будут постоянны. Чем глубже ссылка и чем больше их количество, тем медленнее будет работать ваш магазин.

    ОПТИМИЗАЦИЯ

    Последний момент усугубляется наличием циклов foreach, в котором для каждой части ссылки происходит обращение к базе данных. Логично было бы построить один запрос для всех частей ссылки:

    В файле seo_url.php часть содержимого функции index() после строки $parts = explode('/', rtrim($this->request->get['_route_'], '/')); и до
    1. if (isset($this->request->get['product_id'])) {
      $this->request->get['route'] = 'product/product';
      ...
      необходимо заменить на следующий код:
    if (count($parts)) {
    $parts = array_map(array($this->db, 'escape'), $parts);
    $db_query = $this->db->query("
    SELECT SUBSTRING_INDEX(query,'=',1) AS name,
    CONVERT(SUBSTRING_INDEX(query,'=',-1),UNSIGNED INTEGER) AS value
    FROM " . DB_PREFIX . "url_alias WHERE keyword IN('" . implode("','", $parts) . "') ORDER BY
    FIND_IN_SET(keyword, '" . implode(",", $parts) . "')
    ");
    if ($db_query->num_rows) {
    foreach($db_query->rows as $row) {
    if($row['name'] == 'category_id') {
    if (!isset($this->request->get['path'])) {
    $this->request->get['path'] = $row['value'];
    } else {
    $this->request->get['path'] .= '_' . $row['value'];
    }
    } else $this->request->get[$row['name']] = $row['value'];
    }
    } else $this->request->get['route'] = 'error/not_found';
    }

    В том же файле, но уже для функции rewrite() после строки parse_str($url_data['query'], $data); и до if ($url) {
    unset($data['route']);
    ...

    содержимое необходимо заменить на код ниже:

    $queries = array();
    foreach ($data as $key => $value) {
    if (isset($data['route'])) {
    if (($data['route'] == 'product/product' && $key == 'product_id') ||
    (($data['route'] == 'product/manufacturer/info' ||
    $data['route'] == 'product/product') && $key == 'manufacturer_id') ||
    ($data['route'] == 'information/information' && $key == 'information_id')) {
    $queries[] = $key . '=' . (int)$value;
    } elseif ($key == 'path') {
    $categories = explode('_', $value);
    foreach ($categories as $category) {
    $queries[] = "category_id=" . (int)$category;
    }
    }
    }
    }
    if (count($queries)) {
    $cache = md5(http_build_query($queries));
    $rows = $this->cache->get('url.alias.' . (int)$this->config->get('config_store_id') . $cache);
    if (!$rows) {
    $db_query = $this->db->query("SELECT * FROM " . DB_PREFIX . "url_alias
    WHERE `query` IN('" . implode("', '", $queries) . "')");
    $rows = $db_query->rows;
    $this->cache->set('url.alias.' . (int)$this->config->get('config_store_id') . $cache, $rows );
    }
    foreach ($queries as $query) {
    foreach ($rows as $row) {
    if($row['query'] == $query) {
    $url .= '/' . $row['keyword'];
    $query = explode('=', $query);
    $key = array_shift($query);
    $key = ($key=='category_id') ? 'path' : $key;
    unset($data[$key]);
    break;
    }
    }
    }
    }


    Обратите внимание, что в эту функцию добавлено также кэширование запросов к базе данных, это уже комплексное решение, которое сводит влияние режима SEO URL на производительность сервера к существенному минимуму (вместо 30-100 запросов всего 5-10).

    При использовании кеширования не забудьте его сбрасывать после изменения/создания категории, продукта, статьи или производителя. Для этого в модели этих объектов наравне с удалением собственного кэша добавьте строчку кода $this->cache->delete('url.alias');