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

madmaximus85

Постоялец
Регистрация
30 Июл 2012
Сообщения
96
Реакции
54
Думаю многим будет полезно узнать по какому принципу происходит построение и взаимодействие 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

Какая часть контроллера отвечает за добавление параметров запроса к уже готовому чпу?
 
После анализа лога дебаггера на сборке в которой также установлен 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');
 
Назад
Сверху