v3shin
@v3shin
Веб-шаман

Как в выборке yii2 работать с дополнительными полями и связанными моделями?

Есть модель MyClass и его дочерняя модель SubClass.
/**
 * @var int $id
 * @var string $a
 * @var string $b
*/
class MyClass extends ActiveRecord
{
    public function getParam()
    {
        return $this->a . $this->b;
    }
    public function getSubClass()
    {
        return $this->hasOne(SubClass::class, ['parent_id' => 'id']);
    }
}

/**
 * @var int $id
 * @var int $parent_id
 * @var string $c
 * @var string $d
*/
class SubClass extends ActiveRecord {}

Выборка выполняется кодом в контроллере:
Yii::$app->response->format = Response::FORMAT_JSON;
$models = MyClass::find()->where(...)->all();
return [
    'models' => $models,
];

Мне нужно, чтобы выборка была в виде:
[
    'a' => MyClass::a,
    'param' => MyClass::param,
    'child' => [
        'c' => SubClass::c,
    ],
]

Особенности:
- модель MyClass ограничивается полем a
- в дополнении к полям MyClass с помощью геттера получается поле param
- модель SubClass ограничивается полем c (хотя связь идет через parent_id)

Как мне красиво переписать код, чтобы получить такую выборку?
  • Вопрос задан
  • 119 просмотров
Решения вопроса 1
tomclancys
@tomclancys
$models = MyClass::find()->where(...)->with('sub_class')->asArray()->all();
Ответ написан
Пригласить эксперта
Ответы на вопрос 2
Arkham
@Arkham
Используйте `with` и `asArray`

https://www.yiiframework.com/doc/guide/2.0/ru/db-a...
Ответ написан
v3shin
@v3shin Автор вопроса
Веб-шаман
Так как задача была срочной, сделал свою кривую кастомку, позволяющую управлять выборками полей. Я воспользовался тем фактом, что при отдаче ответа в формате Response::FORMAT_JSON для каждой модели вызывается toArray() без параметров, что, в свою очередь, опирается на fields().
class MyActiveRecord extends \yii\db\ActiveRecord
{
    public static $usedExtraFields = [];
    public static function getExtraFields(): array
    {
        return [];
    }
    public static function useExtraFields(?array $keys = null): void
    {
        if (($keys[0] ?? null) === '*') $keys = null;
        static::$usedExtraFields[static::class] = $keys;
    }
    public function fields(): array
    {
        $extraFields = static::getExtraFields();
        if ((static::$usedExtraFields[static::class] ?? null) !== null) {
            $extraFields = ArrayHelper::filter($extraFields, static::$usedExtraFields[static::class]);
        }
        return array_merge(parent::fields(), $extraFields);
    }
}

// классы наследуются от MyActiveRecord и перегружают getExtraFields()
class MyClass extends MyActiveRecord
{
    public static function getExtraFields(): array
    {
        return [
            'param' => 'param',
            'child' => static function ($model) {
                return $model->getSubClass()->select('c')->one();
            },
        ];
    }
}

// используется это так:
Yii::$app->response->format = Response::FORMAT_JSON;
MyClass::useExtraFields([]); // запретить дополнительные поля
MyClass::useExtraFields(['param']); // ограничить дополнительные поля
MyClass::useExtraFields(['child']); // ограничить дополнительные поля
MyClass::useExtraFields(); // разрешить все дополнительные поля
$models = MyClass::find()->where(...)->all();
return [
    'models' => $models,
];

Но все еще в поиске элегантного решения.
Ответ написан
Комментировать
Ваш ответ на вопрос

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

Войти через центр авторизации
Похожие вопросы
YCLIENTS Москва
от 200 000 до 350 000 ₽
Ведисофт Екатеринбург
от 25 000 ₽
Бюро Цифровых Технологий Санкт-Петербург
от 120 000 до 180 000 ₽
03 мая 2024, в 14:40
1000 руб./за проект
03 мая 2024, в 14:39
500 руб./за проект