Yii2 валидация полей по определенным сценариям

Yii2 валидация полей по определенным сценариям

· Yii2 и Снипеты · 7 мин чтения

В 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