Мне УДАЛОСЬ ИЗМЕНИТЬ СПИСОК ПОЛЕЙ в результате вывода
dataProvider (в т.ч. и поля для вложенных объектов, relations)
И так, как я писал в пред. сообщении:
pr_o писал(а):Мое решение: можно установить кастомное значение для свойства модели $scenario и создать по нему условие в методе fields.[/code]
Это решение оставим и для примера
создадим пару моделей:
Код: Выделить всё
namespace app\models;
/**
* Модель пользователя
*/
class User extends \yii\db\ActiveRecord {
const SCENARIO_LIGHT_FIELDS = 'lightFields';
..
public function scenarios()
{
return \yii\helpers\ArrayHelper::merge(parent::scenarios(), [
// Пустой массив означает, что отработают все rules данной модели при валидации для этого сценария
// см. валидаторы..
self::SCENARIO_LIGHT_FIELDS => [],
]);
}
..
public function fields()
{
$fields = ["id", "username", "active", "profile", "created_at", "updated_at"];
if (self::SCENARIO_LIGHT_FIELDS === $this->scenario) {
// ТОЛЬКО поля "id" и "profile" выведутся при сценарии "lightFields"
// При том, что поле "profile" - это relation hasOne к модели Profile
$fields = array_intersect($fields, ['id', 'profile']);
}
return $fields;
}
..
public function getProfile()
{
return $this->hasOne(Profile::className(), ['user_id' => 'id']);
}
..
}
Код: Выделить всё
namespace app\models;
/**
* Модель профиля пользователя
*/
class Profile extends \yii\db\ActiveRecord {
const SCENARIO_LIGHT_FIELDS = 'lightFields';
..
public function scenarios()
{
return \yii\helpers\ArrayHelper::merge(parent::scenarios(), [
self::SCENARIO_LIGHT_FIELDS => [],
]);
}
..
public function fields()
{
$fields = ["id", "user_id", "first_name", "last_name", "created_at", "updated_at"];
if (self::SCENARIO_LIGHT_FIELDS === $this->scenario) {
// ТОЛЬКО поля "first_name" и "last_name" выведутся при сценарии "lightFields"
$fields = array_intersect($fields, ['first_name', 'last_name']);
}
return $fields;
}
..
}
Заметим, что в классе модели свойство
$scenario уже предопределено.
Теперь переходим к самой интересной части (
rest и вывод данных в
json).
И так, по порядку, что за чем вызывается от выбора данных до вывода в response клиенту:
- ..
-
yii\rest\ActiveController определяет, кто отвечает за index action.
-
yii\rest\IndexAction По умолчанию отвечает за действие контроллера, указанное выше.
- Сам IndexAction в итоге возвращает
dataProvider
- Далее dataProvider попадает в
yii\rest\Serializer
- И только потом вступает в силу
response formatter
- ..
Для решения поставленной задачи нам нужно унаследовать класс yii\rest\IndexAction и вклиниться в метод
prepareDataProvider().
В dataProvider-ре есть замечательный метод
getModels(), который выбирает из БД нужные ему данные и создает по ним модели.
Нам всего-лишь надо будет перебрать все модели (в т.ч. и relations) и установить для каждой из них свойство $scenario, что бы сработало для них условие в методе fields. Следующие примеры кода это все демонстрируют:
Контроллер:
Код: Выделить всё
namespace app\controllers;
use app\models\User;
use app\rest\user\IndexAction;
controller UserController extends \yii\rest\ActiveController {
public $modelClass = User::class;
public function actions()
{
return \yii\helpers\ArrayHelper::merge(parent::actions(), [
'index' => [
'class' => IndexAction::class
],
]);
}
}
Действие контроллера (action) в отдельном классе:
Код: Выделить всё
namespace app\rest\user;
class IndexAction extends \yii\rest\IndexAction
{
/**
* Prepares the data provider that should return the requested collection of the models.
* @return ActiveDataProvider
*/
protected function prepareDataProvider()
{
/** @var yii\data\ActiveDataProvider */
$dataProvider = parent::prepareDataProvider();
/** @var yii\db\ActiveQuery */
$query = $dataProvider->query;
// Кастомизируем запрос, ЕСЛИ НУЖНО (чаще всего - да)
// $query->andWhere(['=', 'active', 1]);
// Теперь пришло время расставить точки над "и" и установить сценарии для моделей
array_map(function ($model) {
$model->setScenario($model::SCENARIO_LIGHT_FIELDS);
if ($model->profile) {
$model->profile->setScenario($model->profile::SCENARIO_LIGHT_FIELDS);
}
// Следующий кусок кода я комментирую, АВОСЬ ПРИГОДИТСЯ:
/*
array_map(function ($model) {
$model->setScenario($this->scenarion);
if ($model->from) {
$model->from->setScenario($this->scenarion);
}
}, $model->chatMessages);
*/
}, $dataProvider->getModels());
return $dataProvider;
}
}
Ну и для полного счастья привожу конфиг компонента urlManager-а:
Код: Выделить всё
return [
..
'components' => [
'request' => [
'enableCookieValidation' => false,
'enableCsrfValidation' => false,
'parsers' => [
'application/json' => 'yii\web\JsonParser',
],
],
'urlManager' => [
'enablePrettyUrl' => true,
'enableStrictParsing' => true,
'showScriptName' => false,
'rules' => [
['class' => 'yii\rest\UrlRule', 'controller' => 'user'],
],
],
],
..
];
Ссылка для результата
{ВАШ_YII2_САЙТ}/{baseUrl}/users
Вот и все, ребята! Удачи!