В Yii2 есть возможность добавления сценариев в модели, например для create()
и update()
. В этой записи, я покажу как создать сценарий и использовать одну и туже модель для валидации одной и той же формы в нескольких местах.
Где это может пригодиться?
Например, вы используете Yii2 advanced шаблон для разработки веб-приложения у которого должен быть админ центр для управления тем, что добавляют пользователя из frontend.
Представим, что вы хотите добавить возможность создания блог-записей на стороне обычного пользователя (frontend) и администратора (backend). Но скорее всего в backend будет больше полей для добавления, так как администратор должен иметь возможность добавлять и изменять все что угодно.
Если не использовать сценарии для модели, то в таком случае должно использоваться две модели. Одна в backend/models
, а другая в frontend/models
.
Но зачем себе утруждать жизнь, если можно создать сценарии и использовать одну модель, которая будет находиться в common/models
. Удобно, не правда ли?
Создаем ActiveRecord модель и правила
Создали обычную модель, назвали её Post
, она использовать методы из ActiveRecord
, которая в свою очередь помогает работать с базой данных.
С помощью метода tableName()
указываем название таблицы для этого класса, в нашем случае — это posts
.
Далее указываем правила для модели с помощью метода rules()
и метод authorExists()
помогает проверить существует id
автора в таблице пользователей.
В принципе, обычная модель, ничего сложного.
<?php namespace common\models; use yii\db\ActiveRecord; use common\models\User; /** * This is the Post model */ class Post extends ActiveRecord { /** * @inheritdoc */ public static function tableName() { return 'posts'; } /** * @inheritdoc */ public function rules() { return [ ['image', 'safe'], [['post_title', 'post_body', 'author_id'], 'required'], [['post_title', 'type'], 'string', 'max' => 80], [['post_body'], 'string', 'max' => 3000], [['author_id'], 'integer'], ['author_id', 'authorExists'] ]; } /** * Проверка на существование пользователь по User модели * @return mixed */ public function authorExists($attribute, $params) { if( !$this->hasErrors() ) { $userExists = User::find()->where(['id' => $this->author_id])->exists(); if( !$userExists ) { $this->addError($attribute, 'Ошибка, такого пользователя не существует.'); } } } }
Создаем сценарий
Теперь давайте разберемся со сценариями.
Сценарии я обозначил с помощью const
, они идут как правило самыми первыми, перед всеми методами и значениями модели.
Название может быть любое, но лучше всего начинать с SCENARIO_
и далее само название.
И конечно же нужно перезаписать метод scenarios()
, внутри которого мы добавили два новых значения — SCENARIO_ADMIN
и SCENARIO_USER
.
SCENARIO_ADMIN
будет использоваться в админке, а SCENARIO_USER
для обычного пользователя.
Разница в том, чтобы при создании поста, обычный пользователь не может изменить id
автора, потому что это значение будет поставлено за него исходя из значения текущий сессии, которая храниться в Yii::$app->user->identity
. (если конечно вы это используете, а лучше это делать :)).
Админ будет иметь возможность изменить автора поста, потому что теперь вы можете изменить сценарий и тем самым валидация полей будет происходит по разным правилам.
Теперь давайте посмотрим, что изменилось в классе после добавления всего что описано выше.
<?php namespace common\models; use yii\db\ActiveRecord; use common\models\User; /** * This is the Post model */ class Post extends ActiveRecord { const SCENARIO_ADMIN = 'admin'; const SCENARIO_USER = 'user'; /** * @inheritdoc */ public static function tableName() { return 'posts'; } /** * @inheritdoc */ public function rules() { return [ ['image', 'safe'], [['post_title', 'post_body', 'author_id'], 'required'], [['post_title', 'type'], 'string', 'max' => 80], [['post_body'], 'string', 'max' => 3000], [['author_id'], 'integer'], ['author_id', 'authorExists'] ]; } /** * Сценарии * @return array */ public function scenarios() { $scenarios = parent::scenarios(); $scenarios[static::SCENARIO_ADMIN] = ['post_title', 'post_body', 'author_id']; $scenarios[static::SCENARIO_USER] = ['post_title', 'post_body']; return $scenarios; } /** * Проверка на существование пользователь по User модели * @return mixed */ public function authorExists($attribute, $params) { if( !$this->hasErrors() ) { $userExists = User::find()->where(['id' => $this->author_id])->exists(); if( !$userExists ) { $this->addError($attribute, 'Ошибка, такого пользователя не существует.'); } } } }
Хочу еще обратить внимание на scenarios()
В этой сценарии вы не полностью перезаписываем его, потому что там есть SCENARIO_DEFAULT
, который используется как стандартный для валидации всех правил модели.
И чтобы его не сносить, вы используем parent::scenarios()
, то вызываем стандартный сценарий из ActiveRecord
и уже к нему добавляем новые.
Для наглядности, я вытащил этот метод из класса выше, чтобы вы могли посмотреть на этой еще раз:
/** * Сценарии * @return array */ public function scenarios() { $scenarios = parent::scenarios(); $scenarios[static::SCENARIO_ADMIN] = ['post_title', 'post_body', 'author_id']; $scenarios[static::SCENARIO_USER] = ['post_title', 'post_body']; return $scenarios; }
Как использовать сценарии в action’ах
Для создания нового поста, в action create вы можете добавить сценарий с помощью конструктора, то есть в следующей форме: new Post(['scenario' => Post::SCENARIO_USER])
.
Если вы изменяете пост, то это так же можно сделать добавив параметр $model->scenario = Post::SCENARIO_USER
.
Пример кода контроллера (controller) ниже:
<?php use yii\web\Controller; PostController extends Controller { ... public function actionCreate() { $model = new Post(['scenario' => Post::SCENARIO_USER]); ... } ... public function actionUpdate($id) { $model = $this->findModel($id); $model->scenario = Post::SCENARIO_USER; ... } protected function findModel($id) { if (($model = Post::findOne($id)) !== null) { return $model; } else { throw new NotFoundHttpException('Запрашиваемая страница не найдена'); } } }
https://stackoverflow.com/questions/30886437/yii2-required-validation-on-update