Настройка ЧПУ – неотъемлемый атрибут работы по 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
. Это поведение, которое будет производить всю валидацию и транслитерацию за вас. В примере, который я описал выше есть возможность управлять всем этим делом. Фишка заключается в том, что по большому счёту никакой автоматической генерации в принципе не нужно. В большинстве случаев имеет бОльший смысл прописать его самостоятельно. Ну, а если вам вдруг стало просто лень, то пусть себе транслитерирует и сохраняет в базу транслит. Это уже будет ЧПУ.