ЧПУ (человеко-понятные-урлы) — важная часть 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.