
http://oc1.loc/index.php?route=product/category&path=25_28
Приложение передает в адресной строке все нужные для выполнения и открытия указанной строки данные – файл index.php, который выполняется первым и является «точкой входа», далее в параметре route указывается метод контроллера, который нужно выполнить (если название метода отсутствует – выполняется метод по-умолчанию «index»). После route передаются другие данные, в нашем случае это идентификатор родительской и дочерней категории.
Данный вид URL плохо воспринимается поисковыми системами, кроме того человеку тоже трудно догадаться что именно должно отобразиться на странице.
В результате для OpenCart и прочих фреймворков и CMS разработаны решения, преобразующие обычный вид URL в ЧПУ (человеко-понятный). При этом, данная строка URL примет, например, такой вид:
http://oc1.loc/component/monitor/
где будет отсутствовать точка входа, параметр route и тд.
В данной статье рассмотрим, что же происходит при включении ЧПУ. Разработчикам это позволит лучше понять как работает OpenCart с URL и вносить в его работу свои изменения, совершенствовать работу ЧПУ, создавать необходимые дополнения.
Я использую ocStore - русскоязычную версию OpenCart с некоторыми улучшениями, такими как модуль ЧПУ - SeoPro. Данный модуль заменяет стандартный функционал формирования ЧПУ в движке OpenCart, прежде всего убирает дубли страниц и др. Далее буду описывать работу ЧПУ на основе этого модуля, хотя принцип работы общий. Если вы используете обычную версию OpenCart, а не ocStore - советую скачать бесплатный модуль SeoPro.
В админ-панели, ЧПУ можно включить НАСТРОЙКИ - Мой Магазин - редактировать - Сервер
- Включить SEO URL – Да.
- Тип ЧПУ – SeoPro
- ЧПУ товаров с категориями - Да
Итак, если параметр route в URL отсутствует (например когда выбрано ЧПУ в настройках), то в файле .htaccess выполняется строка
RewriteRule ^([^?]*) index.php?_route_=$1 [L,QSA]которая изменяет URL на
index.php?_route_=
а после знака равно, подставляются все элементы идущие после названия хоста в URL.
Таким образом, URL
http://oc1.loc/component/monitor/
передаст серверу
http://oc1.loc/index.php?_route_=component/monitor/
где скрипт должен будет перевести полученные данные в нужный для выполнения формат, в данном случае:
http://oc1.loc/index.php?route=product/category&path=25_28
то есть определить, что нужно выполнить метод index контроллера ControllerProductCategory.
Вот о том, как происходит разбор и выполнение действий согласно URL и напишу в данной статье.
В файле system\framework.php создается объект класса Front:
$controller = new Front($registry);Он сохраняет себе в свойстве addPreAction массив служебных действий, которые должны выполниться автоматически сразу после загрузки всех библиотек. Данный массив хранится в файле system\config\catalog.php или system\config\admin.php для админки. В версии для frontend он содержит такой массив
$_['action_pre_action'] = array( 'startup/session', 'startup/startup', 'startup/error', 'startup/event', 'startup/maintenance', 'startup/'.$seo_type );где последним элементом идет 'startup/'.$seo_type содержащий выбранный вариант организации ЧПУ (название нужного контроллера).
Далее в файле system\framework.php происходит вызов метода dispatch() Front Controllerа:
$controller->dispatch(new Action($config->get('action_router')), new Action($config->get('action_error')));который передает на выполнение действия (контроллеры), первым элементом из которых является ControllerStartupRouter, что передается из массива конфигурации командой $config->get('action_router'). Вторым аргументом передается действие выводящее страницу ошибок, если, например, запрашиваемая страница не была найдена.
В методе dispatch() Front Controllerа происходит поочередный вызов служебных действий, которые были переданы заранее и самым последним должен вызываться контроллер разбирающий стандартный URL содержащий параметр route.
public function dispatch(Action $action, Action $error) { $this->error = $error; foreach ($this->pre_action as $pre_action) { $result = $this->execute($pre_action); if ($result instanceof Action) { $action = $result; break; } } while ($action instanceof Action) { $action = $this->execute($action); } }
Но тут есть условие – если одно из действий вернуло результат своего выполнения и результат является объектом класса Action – данный результат перезапишет действие, которое было передано методу dispatch() в качестве первого аргумента (Action $action):
$action = $result;А выполнение контроллера обрабатывающего URL ЧПУ, как раз возвращает такой результат. Поэтому далее выполнение получает не аргумент $action (передающий на выполнение метод index контроллера startup/router), а контроллер/метод, возвращенный после разбора ЧПУ в контроллере ControllerStartupSeoPro или другом, указанном для работы с ЧПУ.
Рассмотрим стандартный способ создания ЧПУ, когда в настройках выбран вариант «SeoPro».
Вызывается контроллер ControllerStartupSeoPro из файла catalog\controller\startup\seo_pro.php
При создании данного объекта, в конструкторе с помощью строки
$this->cache_data = $this->cache->get('seo_pro');в свойство $cache_data сохраняется массив всех значений из таблицы БД url_alias. Алиасы представляют собой сокращенные фразы, по которым фреймворк будет искать контроллер/метод, которые нужно выполнить.
Выполняется метод index(), где в строке
if (!isset($this->request->get['_route_'])) {...}проверяется наличие в GET-параметрах «_route_». Если его нет – выполняется метод validate(), выводящий или главную страницу или страницу ошибок. Если есть – код выполняется дальше. А именно:
$route_ = $route = $this->request->get['_route_'];- значение _route_ из URL сохраняется в переменную $route_ и одновременно в $route с удалением данного параметра из Request Object.
Строкой
$parts = explode('/', trim(utf8_strtolower($route), '/'));параметры из _route_ разбиваются по разделителю на массив значений такого плана:
Array ( [0] => component [1] => monitor )и сохраняются в массив $parts.
Далее берется последний элемент из массива полученных GET-параметров и с помощью функции explode() скрипт пытается снова разбить его на массив значений по разделителю (точке). Результат присваивается переменной $last_part:
list($last_part) = explode('.', array_pop($parts));а потом содержащиеся в ней массивы строк добавляются к тому, что было в переменной $parts до удаления последнего элемента ее массива. В нашем примере с рубрикой component/monitor последние преобразования не изменят начальный массив $parts.
Далее в цикле формируется новый массив $rows, где ключами становятся элементы из параметры GET, а значениями – алиасы, которые соответствуют этим элементам из таблицы url_alias, которые были сохранены конструктором в свойстве $cache_data. Пример:
Array ( [0] => Array ( [keyword] => component [query] => category_id=25 ) [1] => Array ( [keyword] => monitor [query] => category_id=28 ) )
Далее проверяется наличие строки
component/monitor/
в переменной $route
if (isset($this->cache_data['keywords'][$route])){...}в таблице url_alias. В нашем случае она найдена не будет, иначе значение переменной $rows было бы переписано.
В любом случае, при существовании данных в таблице алиасов, что проверяется в следующем условии:
if (count($rows) == sizeof($parts)) {...}данные преобразуются. Сначала в цикле в вид
Array ( [component] => category_id=25 [monitor] => category_id=28 )а потом проверяется, содержит ли данный массив параметр category_id, что значит- страница определенной рубрики. Если содержит, то определяется свойство $this->request->get['path']. В нашем примере оно будет:
25_28Данный параметр будет использован в контроллере, который будет вызван (catalog\controller\product\category.php).
Далее следует блок проверок, формирующий параметр $this->request->get['route'] который будет состоять из контроллера, метод index() которого должен будет выполняться. В нашем примере это product/category.
Вызывается метод контроллера ControllerStartupSeoPro
$this->validate();в котором, в случае, если значение в параметре $this->request->get['route'] отсутствует, происходит вывод главной страницы или переадресация.
Если значение имеется, оно передается в объект Action для сохранения и вызова в дальнейшем:
return new Action($this->request->get['route']);
Ну а дальше происходит то, о чем я писал в начале - результат перезапишет вызов метода index() контроллера startup/router, который должен был включить разбор URL без ЧПУ содержащий параметр route.
Я зіткнулась із таким питанням, додавала товар, банери, категорії і модуль SeoPro справлявся із задачею формування ЧПУ, і думала усе у шоколаді, але сталося не так, як гадалося, почала додавати виробників, а воно видає інформацію у форматі
"http://oc1.loc/index.php?route=product/category&pa..."
Можливо підкажете, що де і як змінити, щоб отримати максимальний результат без шкоди для іншої частини коду....
Дякую!