Подскажите как вернуть модель связную с другой в ActiveDataProvider

Общие вопросы по использованию второй версии фреймворка. Если не знаете как что-то сделать и это про Yii 2, вам сюда.
Ответить
Greengo86
Сообщения: 58
Зарегистрирован: 2016.08.31, 22:04

Подскажите как вернуть модель связную с другой в ActiveDataProvider

Сообщение Greengo86 »

Господа, никак не могу разобраться как мне в prepareDataProvider() в ViewAction вернуть модель автора и связные с ним книги :shock:

Код: Выделить всё

    public function actions()
    {
....
            'view' => [
                'class' => ViewAction::class,
                'modelClass' => $this->modelClass,
                'checkAccess' => [$this, 'checkAccess'],
            ],
        ];
...
    }
Если делаю так, то возвращается всего один лишь автор без книг и ->with('books') не помагает.

Код: Выделить всё

class ViewAction extends \yii\rest\Action
{
    public function run()
    {
        if ($this->checkAccess) {
            call_user_func($this->checkAccess, $this->id);
        }

        return $this->prepareDataProvider();
    }
    protected function prepareDataProvider()
    {
        $requestParams = Yii::$app->getRequest()->getBodyParams();
        if (empty($requestParams)) {
            $requestParams = Yii::$app->getRequest()->getQueryParams();
        }

        /* @var $modelClass \yii\db\BaseActiveRecord */
        $modelClass = $this->modelClass;

        $query = $modelClass::find()->with('books');
        $query->andWhere(['id' => '1']);

        return Yii::createObject([
            'class' => ActiveDataProvider::className(),
            'query' => $query,
            'sort' => [
                'params' => $requestParams,
            ],
        ]);
    }
}
    

Если так, то вернутся лишь книги -

Код: Выделить всё

        $query = $modelClass::findOne($requestParams);
        $books = $query->getBooks();

        return Yii::createObject([
            'class' => ActiveDataProvider::className(),
            'query' => $books,
            'sort' => [
                'params' => $requestParams,
            ],
        ]);

Код: Выделить всё

    public function getBooks()
    {
        return $this->hasMany(Book::class, ['author_id' => 'id']);
    }
Как мне вернуть автора и в его теле его ответа его книги? Использовать другой провайдер? Пытался вынести в контроллер и отдавать через echo json_encode() ругается на то, что нет метода ->setHeader() по этой инструкции - https://www.yiiframework.com/wiki/748/b ... -in-yii2-0
Аватара пользователя
ElisDN
Сообщения: 5845
Зарегистрирован: 2012.10.07, 10:24
Контактная информация:

Re: Подскажите как вернуть модель связную с другой в ActiveDataProvider

Сообщение ElisDN »

Код: Выделить всё

public function getBooks()
{
    return $this->hasMany(Book::class, ['author_id' => 'id']);
}

public function extraFields()
{
    return ['books'];
}
Greengo86
Сообщения: 58
Зарегистрирован: 2016.08.31, 22:04

Re: Подскажите как вернуть модель связную с другой в ActiveDataProvider

Сообщение Greengo86 »

ElisDN писал(а): 2021.11.26, 19:36
Дмитрий, по прежнему только автор на выходе :shock:

Код: Выделить всё

public function getBooks()
    protected function prepareDataProvider()
    {
        $requestParams = Yii::$app->getRequest()->getBodyParams();
        if (empty($requestParams)) {
            $requestParams = Yii::$app->getRequest()->getQueryParams();
        }

        /* @var $modelClass \yii\db\BaseActiveRecord */
        $modelClass = $this->modelClass;

        $query = $modelClass::find()->with('books');
        $query->andWhere(['id' => '1']);

        return Yii::createObject([
            'class' => ActiveDataProvider::className(),
            'query' => $query,
            'sort' => [
                'params' => $requestParams,
            ],
        ]);
    }

Код: Выделить всё

class Author extends \yii\mongodb\ActiveRecord
{
    public function behaviors()
    {
        return [
            [
                'class' => TimestampBehavior::class,
                'createdAtAttribute' => 'created_at',
                'updatedAtAttribute' => 'updated_at',
                'value' => new Expression('NOW()'),
            ],
        ];
    }

    public static function primaryKey()
    {
        return ['id'];
    }

    public function attributes()
    {
        return ['_id', 'id', 'name', 'surname', 'birthday', 'bio', 'created_at', 'updated_at'];
    }

    public function rules()
    {
        return [
            [['created_at', 'updated_at'], 'integer'],
            [['name', 'surname', 'birthday'], 'required'],
        ];
    }

    public function getBooks()
    {
        return $this->hasMany(Book::class, ['author_id' => 'id']);
    }

    public function extraFields()
    {
        return ['books'];
    }

    public static function tableName()
    {
        return 'author';
    }

    public function attributeLabels()
    {
        return [
            'created_at' => 'Created At',
            'updated_at' => 'Updated At',
            'name' => 'First Name',
            'surname' => 'Surname Name',
            'birthday' => 'Birthday',
            'bio' => 'Bio',
        ];
    }
}
Greengo86
Сообщения: 58
Зарегистрирован: 2016.08.31, 22:04

Re: Подскажите как вернуть модель связную с другой в ActiveDataProvider

Сообщение Greengo86 »

Также попробовал добавить в рест модель -

Код: Выделить всё

class Author extends \common\models\Author
{
    public function fields()
    {
        $fields = parent::fields();
        unset($fields['id'], $fields['created_at'], $fields['updated_at'], $fields['bio']);

        return $fields;
    }

    public function extraFields()
    {
        return ['books'];
    }
}
Результат тот же (((
Greengo86
Сообщения: 58
Зарегистрирован: 2016.08.31, 22:04

Re: Подскажите как вернуть модель связную с другой в ActiveDataProvider

Сообщение Greengo86 »

Вышел из положения, если можно это так назвать таким образом -

Код: Выделить всё

class Author extends \common\models\Author
{
	public function fields(){
        $fields = parent::fields();
        $fields['books'] = function ($model) {
            return $model->books;
        };

        return $fields;
	}

    public function extraFields()
    {
        return ['books'];
    }
}
И выходит метод extraFields никакой роли не играет((( Оччч странно... Но что мне делать теперь с тем, что при каждом методе (этот GET) мне надо возвращать разный набор полей? В том числе и с books -

Код: Выделить всё

    public function fields()
    {
        $fields = parent::fields();
        unset($fields['id'], $fields['created_at'], $fields['updated_at'], $fields['author_id']);

        return $fields;
    }
Здесь ненужные мне поля заансетил, а как же мне тогда быть с книгами? Проверять в fields() какой именно метод из реквеста - оччч не хочется такого делать...
Но нет, конечно, это полная фигня, если не смогу управлять полями модели как мне нужно :!:
Аватара пользователя
ElisDN
Сообщения: 5845
Зарегистрирован: 2012.10.07, 10:24
Контактная информация:

Re: Подскажите как вернуть модель связную с другой в ActiveDataProvider

Сообщение ElisDN »

Greengo86 писал(а): 2021.11.27, 01:44 Но что мне делать теперь с тем, что при каждом методе (этот GET) мне надо возвращать разный набор полей?
Уберите fields() и указывайте нужные поля в GET-запросе ?expand=books
Последний раз редактировалось ElisDN 2021.11.27, 13:37, всего редактировалось 1 раз.
Greengo86
Сообщения: 58
Зарегистрирован: 2016.08.31, 22:04

Re: Подскажите как вернуть модель связную с другой в ActiveDataProvider

Сообщение Greengo86 »

ElisDN писал(а): 2021.11.27, 12:40
Greengo86 писал(а): 2021.11.27, 01:44 Но что мне делать теперь с тем, что при каждом методе (этот GET) мне надо возвращать разный набор полей?
Уберите fields() и указывайте нужные поля в GET-запросе ?filelds=id,name,books
Нет, к сожалению, в адресе, в урле могу указать указать только айдишник -
{stand}/authors/2

Есть вариант, да - удалить fields() и в select в query указать эти поля. Есть еще варианты?

Код: Выделить всё

        return Yii::createObject([
            'class' => ActiveDataProvider::className(),
            'query' => Author::find()
                ->select(['id', 'name','surname'])
                ->where(['id' => $requestParams['id']]),
            'sort' => [
                'params' => $requestParams,
            ],
        ]);
И я так понял что extraFields() работает только, если указан параметр в expand в урле. Как-то тоже это можно обойти? Не указывать, но чтобы связная модель загружалась...
Аватара пользователя
ElisDN
Сообщения: 5845
Зарегистрирован: 2012.10.07, 10:24
Контактная информация:

Re: Подскажите как вернуть модель связную с другой в ActiveDataProvider

Сообщение ElisDN »

Greengo86 писал(а): 2021.11.27, 13:19 Как-то тоже это можно обойти? Не указывать, но чтобы связная модель загружалась...
Либо указывать expand в адресе, либо для каждой выборки сделать отдельные классы-наследники со своим fields().
rak
Сообщения: 2181
Зарегистрирован: 2010.11.02, 23:40
Контактная информация:

Re: Подскажите как вернуть модель связную с другой в ActiveDataProvider

Сообщение rak »

Greengo86 писал(а): 2021.11.27, 13:19
ElisDN писал(а): 2021.11.27, 12:40
Greengo86 писал(а): 2021.11.27, 01:44 Но что мне делать теперь с тем, что при каждом методе (этот GET) мне надо возвращать разный набор полей?
Уберите fields() и указывайте нужные поля в GET-запросе ?filelds=id,name,books
Нет, к сожалению, в адресе, в урле могу указать указать только айдишник -
{stand}/authors/2

Есть вариант, да - удалить fields() и в select в query указать эти поля. Есть еще варианты?

Код: Выделить всё

        return Yii::createObject([
            'class' => ActiveDataProvider::className(),
            'query' => Author::find()
                ->select(['id', 'name','surname'])
                ->where(['id' => $requestParams['id']]),
            'sort' => [
                'params' => $requestParams,
            ],
        ]);
И я так понял что extraFields() работает только, если указан параметр в expand в урле. Как-то тоже это можно обойти? Не указывать, но чтобы связная модель загружалась...
есть вариант ещё такой костыль влепить :)

Код: Выделить всё

$_GET['expand'] = 'books';
Greengo86
Сообщения: 58
Зарегистрирован: 2016.08.31, 22:04

Re: Подскажите как вернуть модель связную с другой в ActiveDataProvider

Сообщение Greengo86 »

ElisDN писал(а): 2021.11.27, 13:39
Greengo86 писал(а): 2021.11.27, 13:19 Как-то тоже это можно обойти? Не указывать, но чтобы связная модель загружалась...
Либо указывать expand в адресе, либо для каждой выборки сделать отдельные классы-наследники со своим fields().
Что-то совсем печально :shock:
Как по Вашему, Дмитрий, что правильнее и изяшнее с точки зрения архитектуры указывать поля в ActiveDataProvider в ->select() или штамповать модельки? Или на вкус и цвет у всех могут быть разные мнения
Greengo86
Сообщения: 58
Зарегистрирован: 2016.08.31, 22:04

Re: Подскажите как вернуть модель связную с другой в ActiveDataProvider

Сообщение Greengo86 »

rak писал(а): 2021.11.27, 17:11

Код: Выделить всё

$_GET['expand'] = 'books';
Неужели для этого нет никакой настройки в urlManager? Ок, спасибо
rak
Сообщения: 2181
Зарегистрирован: 2010.11.02, 23:40
Контактная информация:

Re: Подскажите как вернуть модель связную с другой в ActiveDataProvider

Сообщение rak »

Greengo86 писал(а): 2021.11.27, 17:41
rak писал(а): 2021.11.27, 17:11

Код: Выделить всё

$_GET['expand'] = 'books';
Неужели для этого нет никакой настройки в urlManager? Ок, спасибо
https://www.yiiframework.com/doc/api/2. ... lts-detail
можно вот это попробовать
Greengo86
Сообщения: 58
Зарегистрирован: 2016.08.31, 22:04

Re: Подскажите как вернуть модель связную с другой в ActiveDataProvider

Сообщение Greengo86 »

ElisDN писал(а): 2021.11.27, 13:39
Greengo86 писал(а): 2021.11.27, 13:19 Как-то тоже это можно обойти? Не указывать, но чтобы связная модель загружалась...
Либо указывать expand в адресе, либо для каждой выборки сделать отдельные классы-наследники со своим fields().
Хм, ничего не понимаю! Сделал дочерние модели, но они не имеют нужного эффекта - приходит просто пустой массив - []

В контроллере

Код: Выделить всё

    public function actions()
    {
        $actions = [
            'index' => [
                'class' => IndexAction::class,
                'modelClass' => IndexAuthor::class,
                'checkAccess' => [$this, 'checkAccess'],
            ],
        ];
        return array_merge(parent::actions(), $actions);
    }
Та самая дочерняя рестовая модель -

Код: Выделить всё

<?php
namespace frontend\models\rest;

class IndexAuthor extends Author
{
    public function fields()
    {
        $fields = parent::fields();
//        unset($fields['id'], $fields['_id'], $fields['created_at'], $fields['updated_at'], $fields['bio']);
        $fields['books'] = function ($model) {
            return $model->books;
        };

        return $fields;
    }
}
Рестовая основная модель -

Код: Выделить всё

<?php

namespace frontend\models\rest;

class Author extends \common\models\Author
{
	public function fields(){
        	return parent::fields();
	}
}
Еcли изменить в actions modelClass на Author основную рестовую модель - данные возвращаются, а если IndexAuthor - ничего (( В дебаггере даже не доходит до брейпоинта до метода fields() класса IndexAuthor
Greengo86
Сообщения: 58
Зарегистрирован: 2016.08.31, 22:04

Re: Подскажите как вернуть модель связную с другой в ActiveDataProvider

Сообщение Greengo86 »

Докопался до истины. Если делать дочерние модели, то по умолчанию выборка данных происходит из таблицы исходя из полученного из -

Код: Выделить всё

Inflector::camel2id(StringHelper::basename(get_called_class()), '_')
То есть по умолчанию выбирали из таблицы index_author, так как класс IndexAuthor! Для этого в моделе нужно вставить статический метод -

Код: Выделить всё

    public static function collectionName()
    {
        return $model::tableName();
    }
Ответить