Поиск по связанным моделям

Общие вопросы по использованию второй версии фреймворка. Если не знаете как что-то сделать и это про Yii 2, вам сюда.
Ответить
Аватара пользователя
hippik
Сообщения: 57
Зарегистрирован: 2011.12.11, 23:33
Откуда: Москва

Поиск по связанным моделям

Сообщение hippik »

Помогите пожалуйста, что-то запутался.
Мне нужно сделать поиск по связанным моделям.

Связь в модели поставлена:

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

public function getAuthor()
    {
        return $this->hasOne('app\models\User', ['id' => 'created_by']);
    } 
Безопасный параметр поставил:

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

[['author'], 'safe'] 
Мой view:

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

<?php echo GridView::widget([
        'dataProvider' => $dataProvider,
        'filterModel' => $searchModel,
        'columns' => [
            ['class' => 'yii\grid\SerialColumn'],
            'id',
            [
                'attribute' => 'author',
                'value' => function($data) { return $data->author->name; },
            ],
        
            ['class' => 'yii\grid\ActionColumn'],
        ],
    ]); ?>
Фрагмент модели для поиска:

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

$this->addCondition($query, 'id');        
        $query->with=['author'];
        return $dataProvider;
понятно, что не хватает andWhere для связанного поля author, но никак не пойму как его прописать.
Что например не правильно в таком запросе, если он не может найти поле 'author.id' ? Post::find()->with('author')->andWhere(['author.id'=>1])->one();

Аватара пользователя
vova07
Сообщения: 1004
Зарегистрирован: 2012.11.29, 14:52
Откуда: Chisinau, Moldova

Re: Поиск по связанным моделям

Сообщение vova07 »

Почему не:

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

$this->addCondition($query, 'created_by'); 
?

Аватара пользователя
hippik
Сообщения: 57
Зарегистрирован: 2011.12.11, 23:33
Откуда: Москва

Re: Поиск по связанным моделям

Сообщение hippik »

Не понял. Мне же поиск нужен по имени связанной записи , а не по ее id.
да и $this->addCondition($query, 'created_by'); тоже присутсвует в модели, я просто указал не весь код.

Аватара пользователя
slavcodev
Сообщения: 3134
Зарегистрирован: 2009.04.02, 21:42
Откуда: Valencia
Контактная информация:

Re: Поиск по связанным моделям

Сообщение slavcodev »

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

Post::find()
  ->with([
    'author' => function($query) {
        $query->andWhere(['author.id'=>1]);
    }
  ])
  ->one(); 
Жду Yii 3!

Аватара пользователя
vova07
Сообщения: 1004
Зарегистрирован: 2012.11.29, 14:52
Откуда: Chisinau, Moldova

Re: Поиск по связанным моделям

Сообщение vova07 »

Извиняюсь, я не так понял задачу.

Аватара пользователя
hippik
Сообщения: 57
Зарегистрирован: 2011.12.11, 23:33
Откуда: Москва

Re: Поиск по связанным моделям

Сообщение hippik »

Да . я видел такой пример в коде:

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

Post::find()
            ->with([
                'author' => function($query) {
                        $query->andWhere(['author.id'=>1]);
                    }
            ])
            ->all();
Ошибка: SQLSTATE[42S22]: Column not found: 1054 Unknown column 'author.id' in 'where clause'
The SQL being executed was: SELECT * FROM `user` WHERE (`author`.`id`=1) AND (`id` IN ('1', '2'))

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

Post::find()
            ->with([
                'author' => function($query) {
                        $query->andWhere(['id'=>1]);
                    }
            ])
            ->all();
И какой бы я ID пользователя не поставил, запрос все равно выводит все записи.

не пойму как строятся запросы :?:

Аватара пользователя
SiZE
Сообщения: 2700
Зарегистрирован: 2011.09.21, 12:39
Откуда: Perm
Контактная информация:

Re: Поиск по связанным моделям

Сообщение SiZE »

together => true
в поиске работы

Аватара пользователя
futbolim
Сообщения: 2051
Зарегистрирован: 2012.07.08, 19:28

Re: Поиск по связанным моделям

Сообщение futbolim »

hippik писал(а):И какой бы я ID пользователя не поставил, запрос все равно выводит все записи.
Чтобы выбрать один Post, вместо ->all() пишем ->one()

kmtz
Сообщения: 26
Зарегистрирован: 2011.04.23, 11:18

Re: Поиск по связанным моделям

Сообщение kmtz »

hippik писал(а):Да . я видел такой пример в коде:

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

Post::find()
            ->with([
                'author' => function($query) {
                        $query->andWhere(['author.id'=>1]);
                    }
            ])
            ->all(); 
Ошибка: SQLSTATE[42S22]: Column not found: 1054 Unknown column 'author.id' in 'where clause'
The SQL being executed was: SELECT * FROM `user` WHERE (`author`.`id`=1) AND (`id` IN ('1', '2'))

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

Post::find()
            ->with([
                'author' => function($query) {
                        $query->andWhere(['id'=>1]);
                    }
            ])
            ->all(); 
И какой бы я ID пользователя не поставил, запрос все равно выводит все записи.

не пойму как строятся запросы :?:
Честно говоря, не знаю, актуально ли (май 2013), но qiang здесь: http://www.yiiframework.com/forum/index ... _p__206363
даёт кое-какие объяснения. С inner_join получилось сделать поиск в grid_view.

kmtz
Сообщения: 26
Зарегистрирован: 2011.04.23, 11:18

Re: Поиск по связанным моделям

Сообщение kmtz »

Судя по всему актуально...
SiZE, в yii2 нет together

Аватара пользователя
hippik
Сообщения: 57
Зарегистрирован: 2011.12.11, 23:33
Откуда: Москва

Re: Поиск по связанным моделям

Сообщение hippik »

kmtz, благодарю. Я это видел, буду разбираться...
Просто все эти Join,т мне кажется, это уже больше наверное DAO, чем AR.
Вот например,если

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

$post = POST::find()->all(); 
и в модели есть геттер author, то я так понимаю выводить данные Post по автору можно, а это запрос отсортировать по нему нельзя?

А есть у кого-то может тогда собственно по сабжу пример?
Скиньте пожалуйста код модели с поиском по связанным таблицам.

kmtz
Сообщения: 26
Зарегистрирован: 2011.04.23, 11:18

Re: Поиск по связанным моделям

Сообщение kmtz »

Да, мне это тоже показалось не очень логичным (хотя, может это привычка с 1 версии). Получается, при поиске мы вообще не используем отношение, прописанное в модели. Но по-другому заставить работать поиск у меня не получилось.

kmtz
Сообщения: 26
Зарегистрирован: 2011.04.23, 11:18

Re: Поиск по связанным моделям

Сообщение kmtz »

hippik писал(а): А есть у кого-то может тогда собственно по сабжу пример?
Скиньте пожалуйста код модели с поиском по связанным таблицам.
Пример корявый (только разбираюсь с новым AR), но рабочий:

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

    public function search($params)
    {
        $query = Material::find()->with('person')->innerJoin('employee', 'material.person_id=employee.id');
        $dataProvider = new ActiveDataProvider([
            'query' => $query,
            'sort' => [
                'attributes' => [
                    'person' => [
                        'asc' => ['employee.name' => SORT_ASC ],
                        'desc'=> ['employee.name' => SORT_DESC]
                    ],
                    'name',
                    'brand',
                    'model',
                    'inv_number'
                ]
            ]
        ]);

        if (!($this->load($params) && $this->validate())) {
            return $dataProvider;
        }

        $this->addCondition($query, 'name', true);
        $this->addCondition($query, 'brand', true);
        $this->addCondition($query, 'model', true);
        $this->addCondition($query, 'serial', true);
        $this->addCondition($query, 'inv_number', true);
        $this->addCondition($query, 'comment', true);
        $this->addWithCondition($query, 'person', true);
        return $dataProvider;
    }


    protected function addWithCondition($query, $attribute, $partialMatch = false)
    {
        $value = $this->$attribute;
        if (trim($value) === '') {
            return;
        }
        if ($partialMatch) {
            $value = '%' . strtr($value, ['%'=>'\%', '_'=>'\_', '\\'=>'\\\\']) . '%';
            $query->andWhere(['like', 'employee.name', $value]);
        } else {
            $query->andWhere(['employee.name' => $value]);
        }
    }
 

Аватара пользователя
hippik
Сообщения: 57
Зарегистрирован: 2011.12.11, 23:33
Откуда: Москва

Re: Поиск по связанным моделям

Сообщение hippik »

Благодарю!

ORey
Сообщения: 19
Зарегистрирован: 2013.07.30, 14:29

Re: Поиск по связанным моделям

Сообщение ORey »

Простите за подъем некропоста, но этот функционал уже появился.
См. joinWith и innerJoinWith.
Работает, правда, за два запроса: один с джойном (фильтрующий), второй уже непосредственно eager loading.

Аватара пользователя
hippik
Сообщения: 57
Зарегистрирован: 2011.12.11, 23:33
Откуда: Москва

Re: Поиск по связанным моделям

Сообщение hippik »

Спасибо! Посмотрю. Сейчас немного на другое отвлекся.

Аватара пользователя
hippik
Сообщения: 57
Зарегистрирован: 2011.12.11, 23:33
Откуда: Москва

Re: Поиск по связанным моделям

Сообщение hippik »

Изучил ветку https://github.com/yiisoft/yii2/issues/1581
Но мне немного не подходит. У меня в обоих таблицах название полей одинаковые, а в примере уникальные (last_name) и в итоге я даже safe атрибуты не могу в модели поиска поставить например 'author.name' - ошибка - "Unknown Property"

ORey
Сообщения: 19
Зарегистрирован: 2013.07.30, 14:29

Re: Поиск по связанным моделям

Сообщение ORey »

Потому что изучать нужно вот эту ветку :)
https://github.com/yiisoft/yii2/issues/1791

В общем, полная версия кода может выглядеть как-то так:
Отношение в модели:

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

    public function getBilling()
    {
        return
            $this->hasOne(BillingAggregate::className(), ['client_id' => 'id'])
                ->from(BillingAggregate::tableName() . ' billing')
                ->onCondition([
                    'billing.billing_year' => ...,
                    'billing.billing_month' => ...,
                ]);
    }
 
а потом, когда условие набиваете, уже можно использовать

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

$query->andWhere('billing.current_balance < 0'); 
В общем, все отлично работает: и joinWith через отношение, и альясинг, и поиск по условиям на связанную таблицу.

Аватара пользователя
hippik
Сообщения: 57
Зарегистрирован: 2011.12.11, 23:33
Откуда: Москва

Re: Поиск по связанным моделям

Сообщение hippik »

Вот может кому-то облегчит страдания :)

Все оказалось намного проще. Просто сам себя запутал..

1) В модели ставим связку

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

public function getSubject()
    {
        return $this->hasOne('app\models\Subject', ['id' => 'subject_id']);
    } 
Заодно и пишем ему лейбл для красоты

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

'subject' =>  \Yii::t("app", "Субъект"), 
2) В модели поиска добавляем свойство

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

public $subject; 
Ставим безопасный атрибут

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

[..'subject'], 'safe'], 
3) В методе search модели поиска прописываем JOIN

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

$query = MyTable::find()->joinWith('season'); 
ставим сортировку по этому атрибуту

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

$dataProvider->sort->attributes['subject'] = [
            'asc' => ['tbl_subject.name' => SORT_ASC],
            'desc' => ['tbl_subject.name' => SORT_DESC],
        ]; 
и добавляем правило поиска (тут на свое усмотрение)

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

$this->addWithCondition($query, 'season','tbl_subject',true);
 

protected function addWithCondition($query, $attribute,$table, $partialMatch = false)
    {
...
if ($partialMatch) {
            $query->andWhere(['like', $table.'.name', $value]);
        } else {
            $query->andWhere([$table.'.name' => $value]);
        }
...
}
 
Ну и View

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

<?php echo GridView::widget([
        'dataProvider' => $dataProvider,
        'filterModel' => $searchModel,
        'columns' => [
            ['class' => 'yii\grid\SerialColumn'],

            'id',  
            [
                'attribute' => 'subject',
                'value' => function($data) { return $data->subject->name; },
            ],
            ['class' => 'yii\grid\ActionColumn'],
        ],
    ]); ?>
было бы красивее добавить alias к таблице в условии, как написано выше, но это так для наглядности..

поиск готов ;)

Ответить