Псевдонимы записей в Yii2. Создание и управление
Настройка ЧПУ – неотъемлемый атрибут работы по SEO-оптимизации любого сайта. Формировать URL только по id записи – не лучший вариант.
В данном уроке мы добавим псевдонимы записей (слаг/алиас) для постов блога.
Суть такова. В форме (создания, редактирования) поста есть текстовове поле, в которое мы можем прописать свой собственный slug и при этом оно должно валидироваться. Если мы оставим поле пустым, то Yii2 сам сделает эту работу методом Inflector::slug()
, куда мы передадим название поста (то есть, по умолчанию слаг будет транслитом названия поста). В базе данных поле slug
обязательно для заполнения (NOT NULL
) и является уникальным.
Добавим в базу данных поле slug
(если его не было изначально). Пример добавления поля через миграцию:
// Из консоли php yii migrate/create add_post_field_slug
В созданной миграции:
public function safeUp() { $this->addColumn('{{%post}}', 'slug', $this->string(255)->notNull()->after('status')->unique()); $this->createIndex('{{%idx-post-slug}}', '{{%post}}', 'slug', true); } public function safeDown() { $this->dropColumn('{{%post}}', 'slug'); }
В форме (backend\views\post\_form.php
) добавим поле:
<?= $form->field($model, 'slug')->textInput(['maxlength' => true]) ?>
В модель Post
добавляем следующий код:
private $_post; public function rules() { return [ [['name'], 'required'], // На стороне клиента "slug" необязательный [['name', 'slug'], 'string', 'max' => 255], // "slug" - ограниченная строка ['slug', SlugValidator::class], // "slug" валидируется классом SlugValidator // "name" и "slug" - уникальны (фильтр по ID поста при редактировании) [['name', 'slug'], 'unique', 'targetClass' => Post::class, 'filter' => $this->_post ? ['<>', 'id', $this->_post->id] : null] ]; }
Класс SlugValidator
:
<?php namespace common\models\validators; use yii\validators\RegularExpressionValidator; class SlugValidator extends RegularExpressionValidator { public $pattern = '#^[a-z0-9_-]*$#s'; public $message = 'Only [a-z0-9_-] symbols are allowed.'; }
Контроллер backend\controllers\PostController.php
:
public function actionCreate() { $model = new Post(); if ($model->load(Yii::$app->request->post())) { $model->slug = $model->slug ?: Inflector::slug($model->name); if($model->save()) { return $this->redirect(['view', 'id' => $model->id]); } } return $this->render('create', [ 'model' => $model, ]); }
Обновление записи происходит аналогично. В метод actionUpdate($id)
необходимо тоже добавить:
$model->slug = $model->slug ?: Inflector::slug($model->name);
Теперь ссылка на пост во фронтенде будет выглядеть так:
<a href="<?= Url::to(['/blog/post/view', 'slug' => $model->slug]) ?>"><?= Html::encode($model->name) ?></a> // Или <? $name = Html::encode($model->name); $url = Url::to(['/blog/post/view', 'slug' => $model->slug]); $options = [ 'title' => $name ]; echo Html::a($name, $url, $options); ?>
Метод, в котором получаем отдельный пост:
public function actionView($slug) { // Получаем пост по его слагу if (!$post = Post::find()->where(['slug' => $slug])->one()) { throw new NotFoundHttpException('Page not found!'); } return $this->render('view', [ 'post' => $post ]); }
Правило в urlManager
:
[ 'pattern' => 'blog/post/<slug:[\w-]+>', 'route' => 'blog/post/view', 'suffix' => '.html' ]
Теперь посты открываются по красивым адресам по типу:
http://site.ru/blog/post/primer-slaga.html
В прочем, если бы мы заострили на это внимание и оптимизировали бы каждую страницу тщательно, то было бы лучше прописать его самостоятельно. Например так:
http://site.ru/blog/post/slug-example.html
Это был, наверное, самый простой способ сделать слаги (алиасы, псевдонимы) у постов на фреймворке Yii2. Есть, конечно, готовые решения, типа SluggableBehavior
. Это поведение, которое будет производить всю валидацию и транслитерацию за вас. В примере, который я описал выше есть возможность управлять всем этим делом. Фишка заключается в том, что по большому счёту никакой автоматической генерации в принципе не нужно. В большинстве случаев имеет бОльший смысл прописать его самостоятельно. Ну, а если вам вдруг стало просто лень, то пусть себе транслитерирует и сохраняет в базу транслит. Это уже будет ЧПУ.