MaximWeber
@MaximWeber
Junior

Как в yii2 разделить массив полей в Model::fields() для разных случаев?

Задача
Предположим, есть REST-сервис, представленный двумя ресурсами: список записей, информация о записи. Нужно, чтобы список записей содержал только поля id и title, а информация о записи: id, title, text, image, author.

Как мне решить мою задачу наиболее красивым способом? Что посоветуете?

Как я пытаюсь это решить (ниже можно особо не читать)
Я знаю, что в url могу использовать get-параметр fields и в нем перечислить те поля, что меня интересуют. Но мне такой подход не нравится тем, что url становится не таким лаконичным, как бы мне хотелось.

В моделях Yii2 есть fields(), который используется сериализатором в контроллере и определяет список полей, которые следует вернуть клиенту. Для разделения списка полей для разных сценариев использования я решил воспользоваться полем scenario у модели. Это прекрасно работает ровно до того момента, пока я не использую ActiveDataProvider(), в который никаким способом этот сценарий я передать не могу.

В коде это выглядит как-то так:
class Post 
{
    const SCENARIO_INDEX = 'index';
    const SCENARIO_VIEW = 'view';

    public function fields()
    {
        $fields = parent::fields();

        if ($this->scenario === self::SCENARIO_INDEX) {
            $fields = ['id', 'title'];
        }
        if ($this->scenario === self::SCENARIO_VIEW) {
            $fields = ['id', 'title', 'text', 'image', 'author'];
        }

        return $fields;
    }
}

class PostController extends \yii\rest\Controller
{
    public function actionIndex()
    {
        return new ActiveDataProvider([
            'query' => Post::find(),
            // 'scenario' => Post::SCENARIO_INDEX, // Это так не работает :(
        ]);
    }
    
    public function actionView($id)
    {
        $post = Post::findOne((int) $id);
        if (!$post) {
            throw new NotFoundHttpException();
        }
        $post->scenario = Post::SCENARIO_VIEW;

        return $post;
    }
}


На ум мне пришли три решения:
1. Собственная реализация ActiveDataProvider с поддержкой сценариев.
2. Не использовать сценарии. Создать наследников Post (например, PostForIndex, PostForView), в которых переопределять fields() под мои нужды.
3. Не использовать сценарии. В контроллере задать get-параметры:
\Yii::$app->request->setQueryParams(['fields' => 'id, title']);
  • Вопрос задан
  • 1609 просмотров
Пригласить эксперта
Ответы на вопрос 2
syamskoy
@syamskoy
Сценарий можно задать, но выглядит это так себе:
$activeDataProvider = new ActiveDataProvider([
            'query' => Post::find(),
            // 'scenario' => Post::SCENARIO_INDEX, // Это так не работает :(
        ]);
foreach($activeDataProvider->getModels() as $model) {
    $model->scenario = Post::SCENARIO_GUEST;
}


Если отдача нужных полей - это не часть безопасности, а просто удобство, то все же лучше через url нужные запрашивать. Если это разделение доступа, то вместо сценариев можно использовать правила RBAC для отдачи нужных полей. Если это тоже не подходит - то уже наследование и переопределение. Но есть еще один вариант, правда с сценариями никак не связан: можно в селекте указать нужные поля: Post::find()->select(['id']).
Ответ написан
webinar
@webinar Куратор тега Yii
Учим yii: https://youtu.be/-WRMlGHLgRg
1. Я бы сделал 2 модели, а не сценарии. Если есть общие методы - можно иметь общего предка. Соответственно будет два контролера у каждого своя модель и не будет путаницы.
2. Можно переопределить prepareDataProvider:
https://www.yiiframework.com/doc/api/2.0/yii-rest-...
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы