Помогает реализовать управление урлами страниц системы как в CMS WordPress, с управлением из админпанели таблицей маршрутизации и редиректами.
Создать таблицу для хранения роутинга (применить миграцию)
Создать свой экземпляр модели UrlRoute
Настроить UrlManager
В нужной AR модели
Подключить ActualityUrlBehavior
Создаем CRUD для управления роутами
php yii migrate/create create_table__url_route
Унаследуйте созданный класс миграции от \voskobovich\seo\migrations\create_table__url_route.
Например:
class <ClassName> extends \voskobovich\seo\migrations\create_table__url_route
{
}
Применяем миграцию
php yii migrate
Yii2 SEO toolkit не знает с какими моделями и роутами ему придется работать.
Чтобы это исправить, нужно унаследоваться от модели UrlRoute из пакета и реализовать 2 метода из UrlRouteInterface.
Вот пример из моего проекта.
class UrlRoute extends \voskobovich\seo\models\UrlRoute
{
const OBJECT_CATEGORY = 'category';
const OBJECT_POST = 'post';
const OBJECT_TAG = 'tag';
const OBJECT_USER = 'user';
/**
* List objects
* @return array;
*/
public static function objectItems()
{
return [
static::OBJECT_CATEGORY => 'Category',
static::OBJECT_POST => 'Post',
static::OBJECT_TAG => 'Tag',
static::OBJECT_USER => 'User',
];
}
/**
* Route map for objects
* @return array;
*/
public static function routeMap()
{
return [
static::OBJECT_CATEGORY => [
static::ACTION_INDEX => 'category/index',
static::ACTION_VIEW => 'category/view',
],
static::OBJECT_POST => [
static::ACTION_INDEX => 'post/index',
static::ACTION_VIEW => 'post/view',
],
static::OBJECT_TAG => [
static::ACTION_INDEX => 'tag/index',
static::ACTION_VIEW => 'tag/view',
],
static::OBJECT_USER => [
static::ACTION_INDEX => 'user/index',
static::ACTION_VIEW => 'user/view',
],
];
}
}
Из примера видно, что роутинг будет работать с 4-я объектами.
Для каждого объекта сконфигурирован список стандартных действий "показать все" (index) и "показать один" (view).
Действия нужны для того, чтобы различать логику обработки роута.
Например, мы можем сделать вот так:
Роут /foo это действие index для объекта post. В итоге, перейдя по /foo мы получим список всех постов (в yii роутинге это равняется переходу на post/index).
Роут /bar это действие view для объекта post c id=3. В итоге, перейдя по /bar мы увидим запись с id=5 (в yii роутинге это равняется переходу на post/view).
Список действий можно расширить, дополнив список в методе getActionItems.
В стандартном классе UrlManager реализовано кеширование роутов которое помогает ускорить процесс генерации ссылок в стандартном роутинге Yii2. Суть кеширования в том, чтобы запоминать для каких роутов нет правил в конфиге и больше не пытаться построить ссылку для этого роута. То есть, если для роута post/view нет правила, то больше для этого роута правил искаться не будет, что и логично. Но для нас это беда, и вот почему. Для роута post/view id=4 может быть правило в таблице маршрутизации, а для post/view id=5 может не быть. Если не отключить кеширование, то при генерации ссылок на посты если первой будет сгенерирована ссылка для post/view id=5 то UrlManager запомнит, что правила для этого роута нет и больше не будет его обрабатывать. В итоге мы не получим наших красивых ссылок для остальных постов. Итак, подключаем класс UrlManager из пакета.
'urlManager' => [
'class' => 'voskobovich\seo\web\UrlManager',
'cacheable' => false,
'rules' => [
'' => 'post/index',
['class' => '\voskobovich\seo\web\ClearUrlRule'],
['class' => '\voskobovich\seo\web\UrlRule', 'modelClass' => 'app\models\UrlRoute'],
// Default
'<controller:\w+>/<id:\d+>' => '<controller>/view',
'<controller:\w+>/<action:[a-zA-Z-]*>/<id:\d+>' => '<controller>/<action>',
'<controller:\w+>/<action:[a-zA-Z-]*>' => '<controller>/<action>',
]
],
Правило ClearUrlRule умеет:
Заменить множество слэшей на один
Удалить слэш в конце ссылки
Буквы верхнего регистра перевести в нижний
Внимание! В целях оптимизации рекомендую настроить эти правила на уровне вашего веб-сервера.
Правило UrlRule отвечает за наш волшебный роутинг.
В атрибут modelClass передается модель UrlRoute которая была создана на прошлом шаге.
Чтобы для новых моделей ссылки автоматом попадили в таблицу роутинга, нужно подключить CreateUrlBehavior и реализовать интерфейс SeoModelInterface.
Привожу пример модели Post из моего проекта.
class Post extends BaseActiveRecord implements SeoModelInterface
{
// ...
/**
* @return array
*/
public function behaviors()
{
return [
'createUrlBehavior' => [
'class' => CreateUrlBehavior::className(),
'modelClass' => UrlRoute::className(),
'objectKey' => UrlRoute::OBJECT_POST
],
'actualityUrlBehavior' => [
'class' => ActualityUrlBehavior::className(),
'modelClass' => UrlRoute::className(),
'objectKey' => UrlRoute::OBJECT_POST
]
];
}
/**
* Build Seo Path
* @return null|string
*/
public function getSeoPath()
{
/** @var Category|TreeInterface $mainCategory */
$mainCategory = $this->mainCategory;
if ($mainCategory) {
return $mainCategory->path . '/' . $this->slug;
}
return null;
}
// ...
}
Метод getSeoPath() должен возвращать путь, по которому будет доступна запись поста.
У меня этот роут состоит из пути к главной категории поста и короткого имени самого поста (/cat/subcat/post-slug).
Поведению нужно передать нашу модель UrlRoute и сообщить каким объектом является наша AR модель используя ранее созданные константы в модели UrlRoute.
После настройки всего пользователь все еще может перейти по старой ссылке и увидеть страницу.
Например, мы для /post/view?id=6 создали красивый урл /best-post. Но пользователь все еще может перейти по старой ссылке и получить страницу, хотя в идеале его нужно отправить на новый урл с 301-м редиректом.
Вот пример обработчика запроса из моего проекта.
class PostController extends Controller
{
// ...
public function actionView($id)
{
/** @var Post $model */
$model = Post::find()
->andWhere(['id' => $id])
->one();
$model->trigger(ActualityUrlBehavior::EVENT_CHECK_URL);
return $this->render('view', [
'model' => $model
]);
}
// ...
}
Говорю сразу, подключать поведение на весь контролер не нужно. Это поведение нужно только для view экшена.
Этому поведению так же нужно передать класс нашей созданной модели UrlRoute и название объекта из константы.
Во вьюхах есть небольшая логика, так что привожу примеры основных кусков кода из своего проекта.
Файл create.php
<?php $form = ActiveForm::begin() ?>
<?= $form->field($model, 'path') ?>
<?= $form->field($model, 'action_key')->dropDownList($model::getActionItems()) ?>
<button type="submit" >Save</button>
<?php ActiveForm::end() ?>
Файл update.php
<?php $form = ActiveForm::begin() ?>
<?= $form->field($model, 'path') ?>
<?php if ($model->checkAction($model::ACTION_INDEX)): ?>
<?= $form->field($model, 'object_key')->dropDownList($model::getObjectItems()) ?>
<?php elseif ($model->checkAction($model::ACTION_VIEW)): ?>
<?= $form->field($model, 'object_key')->dropDownList($model::getObjectItems()) ?>
<?= $form->field($model, 'object_id') ?>
<?php elseif ($model->checkAction($model::ACTION_REDIRECT)): ?>
<?= $form->field($model, 'url_to') ?>
<?php endif; ?>
<?= $form->field($model, 'http_code') ?>
<button type="submit"</button>
<?php ActiveForm::end() ?>
Файл index.php
<?= GridView::widget([
'dataProvider' => $dataProvider,
'filterModel' => $model,
'columns' => [
[
'attribute' => 'path',
'format' => 'raw',
'value' => function ($model) {
/** @var UrlRoute $model */
return Html::a($model->path, ['update', 'id' => $model->id], ['data-pjax' => 0]);
}
],
[
'attribute' => 'object_key',
'filter' => $model::getObjectItems(),
'value' => function ($model) {
/** @var UrlRoute $model */
return $model->getObject();
},
'visible' => array_search($action, [UrlRoute::ACTION_INDEX, UrlRoute::ACTION_VIEW]) !== false
],
[
'attribute' => 'object_id',
'visible' => array_search($action, [UrlRoute::ACTION_INDEX, UrlRoute::ACTION_VIEW]) !== false
],
[
'attribute' => 'url_to',
'visible' => $action == UrlRoute::ACTION_REDIRECT
],
[
'attribute' => 'http_code',
'visible' => $action == UrlRoute::ACTION_REDIRECT
],
[
'class' => 'voskobovich\grid\advanced\columns\ActionColumn',
'template' => '{view} {update} {delete}',
'options' => [
'width' => '160px'
],
'buttons' => [
'view' => function ($url, $model, $key) {
$options = [
'title' => Yii::t('yii', 'View'),
'aria-label' => Yii::t('yii', 'View'),
'data-pjax' => '0',
'class' => 'btn btn-default btn-xs',
'target' => '_blank',
];
/** @var UrlRoute $model */
$url = $model->viewUrl();
return Html::a(Yii::t('yii', 'View'), $url, $options);
}
]
],
],
]) ?>
The preferred way to install this extension is through composer.
Either run
php composer.phar require --prefer-dist voskobovich/yii2-seo-toolkit "^1.0"
or add
"voskobovich/yii2-seo-toolkit": "^1.0"
to the require section of your composer.json
file.
Comments