ЧПУ (человеко-понятные-урлы) — важная часть SEO оптимизации. Я не спец по SEO поэтому рассуждения о том, работает это или нет в текущих алгоритмах ранжирования оставлю специалистам. Мы же поговорим о том, как работать с SEO-ссылками в Yii2.
За маршрутизацию в Yii отвечает компонент urlManager, который получает HTTP запрос и перенаправляет управление в нужный action нужного контроллера. Прелесть ситуации в том, что благодаря этому компоненту вы можете поменять схему url-ов приложения поменяв только конфигурацию urlManager-а и ничего не меняя в коде самого приложения.
RewriteEngine on RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteRule . index.php [L]
и модуль mod_rewrite активен в вашей конфигурации Apache, т.е все запросы должны перенаправляться на index.php
'urlManager' => [ 'enablePrettyUrl' => true, // использовать ЧПУ 'showScriptName' => false, // убирает имя файла (index.php) из url 'suffix' => '/', // все url будут заканчиваться на "/", url без / будут выдавать 404 , 'enableStrictParsing' => true // url должен строго соответствовать правилам, иначе 404 ]
'urlManager' => [ 'enablePrettyUrl' => true, 'enableStrictParsing' => true, 'showScriptName' => false, 'rules' => [ '/' => 'site/index', '<controller:\w+>/' => '<controller>/index', '<controller:\w+>/<action:(\w|-)+>/<id:\d+>' => '<controller>/<action>', '<module:\w+>/<controller:\w+>/<action:(\w|-)+>' => '<module>/<controller>/<action>', '<controller:\w+>/<action:(\w|-)+>' => '<controller>/<action>' ], ]
Обратите внимание, правила анализируются по порядку, поэтому их порядок очень важен.
Самое важное — понять как формируются правила и на что это влияет. Попробую объяснить на примере:
‘company’ => ‘site/company’
‘company/<mission:\w+>/<type:(good|best)>/<title>’ => ‘site/company’
Оба эти правила отвечают за вызов action Company контроллера site. Но один и тот-же код генерации url в коде вернет разные ссылки для каждого из этих правил:
echo Url::to(['/site/company/', 'mission' => 'sometexthere', 'type' => 'best', 'title' => 'my-title']); //для перого правила url будет таким: // company?mission=sometexthere&type=best&title=my-title // а для второго таким: // company/sometexthere/best/my-title
и по разному будут парсится эти ссылки (ссылка второго вида не будет работать для правила первого вида и наоборот). Т.к второе правило «знает», что строка после company — это параметр mission, а первое правило ничего о параметрах не знает.
Строка вида <name> называется именованным параметром. Через двоеточие можно написать любое регулярное выражение, которому должна соответствовать строка, чтобы быть присвоенной этому параметру. К примеру \d+ — любое не нулевое кол-во цифр, \w+ любое не нулевое кол-во букв, (a|b) a или b, ^cid\d{3} начинается с cid и дальше 3 цифры и тп. Повторюсь — любое регулярное выражение.В моем примере параметр title указан без фильтра, т.е с ним совпадет любое значение.
Таким образом, меняя правила в urlManager можно менять url любых страниц не меняя имен контроллеров/action, параметров и тп.
Для большинства задач достаточно правильно прописать правила в urlManager, но иногда этого мало. Например: в проекте для идентификации компании используется id-компании. Соотвественно ссылки на страницу компании генерируются как Url::to([‘/site/company/’, ‘id’ => $company->id]); Но заказчик хочет чтобы ссылка выглядела так: город/метро/тип/название компании.
Как вариант, можно прописать правило <city>/<subway>/<type>/<title>, но тогда придется дописывать параметры везде где создается url: Url::to([‘/site/company/’, ‘id’ => $company->id, ‘city’ => ‘…’ …]); Важным моментом тут является то, что эти параметры нужны исключительно для красоты SEO-ссылки и никак не влияют на работу приложения. А если завтра нужно будет убрать один параметр и добавить еще парочку тоже для красоты? Снова искать все места где создается ссылка? В таком случаи гораздо удобнее создать свое правило маршрутизации.
use yii\base\Object; use yii\web\UrlRuleInterface; class SEOUrl extends Object implements UrlRuleInterface { public function createUrl($manager, $route, $params){ if($route === 'site/company'){ if (isset($params['id'])) { $company = Company::findOne($params['id']); if(!empty($company)){ //получаем все нужные значения ... return $city.'/'.$type.'/'.$company->title; } } } return false; // this rule does not apply } public function parseRequest($manager, $request){ $pathInfo = $request->getPathInfo(); if(preg_match('%^(\w+)(?:/(\w+))(?:/(\w+))$%', $pathInfo, $matches)){ //парсим адрес и ищем компанию, ему соответствующую ... $params['id'] = $company->id; return [ 'site/company', $params ]; } return false; // this rule does not apply } }
Класс должен реализовывать UrlRuleInterface, т.е в нем должны быть два методы createUrl — с описанием правила для создания url и parseRequest для разбора url. Осталось только прописать наше новое правило в urlManager
rules' => [ ['class' => 'app\components\SEOUrl'], ... // другие правила ]
Если у вас возникли проблемы с ЧПУ в yii — пишите в комментариях и я на вашем примере постараюсь объяснить то, что осталось не понятным после прочтения данной статьи.
Такие правила формирования урл не слабо могут нагружать загрузку из-за большого количества запросов к БД, если на странице например есть таблица с ссылками.
Большое спасибо за материал!
Полдня потратил, чтобы понять как работает urlManager и подружиться с enableStrictParsing.