Вывод ListView по связанным моделям и дополнительным параметрам

Общие вопросы по использованию второй версии фреймворка. Если не знаете как что-то сделать и это про Yii 2, вам сюда.
Ответить
X-Loading
Сообщения: 26
Зарегистрирован: 2011.11.15, 14:55

Вывод ListView по связанным моделям и дополнительным параметрам

Сообщение X-Loading »

Добрый день!
Есть следующая модель данных:
Снимок экрана 2015-09-24 в 17.10.27.png
Снимок экрана 2015-09-24 в 17.10.27.png (46.9 КБ) 1560 просмотров
В модели Tour определены следующие relations:

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

class Tour extends \yii\db\ActiveRecord
{
    ...

    /**
     * @return \yii\db\ActiveQuery
     */
    public function getStartLocation()
    {
        return $this->hasOne(Location::className(), ['id' => 'start_location_id']);
    }

    /**
     * @return \yii\db\ActiveQuery
     */
    public function getTourLocations()
    {
        return $this->hasMany(TourLocation::className(), ['tour_id' => 'id']);
    }

    /**
     * @return \yii\db\ActiveQuery
     */
    public function getLocations()
    {
        return $this->hasMany(Location::className(), ['id' => 'location_id'])->viaTable('tour_location', ['tour_id' => 'id']);
    }

    /**
     * @return \yii\db\ActiveQuery
     */
    public function getTourSights()
    {
        return $this->hasMany(TourSight::className(), ['tour_id' => 'id']);
    }

    /**
     * @return \yii\db\ActiveQuery
     */
    public function getSights()
    {
        return $this->hasMany(Sight::className(), ['id' => 'sightseeing_id'])->viaTable('tour_sightseeing', ['tour_id' => 'id']);
    }

    ...
}
И есть задача выводить список туров (модель Tour) в ListView в зависимости от location или sightseeing, переданных в запросе.
Сначала я пошёл по пути использования search-модели и для поиска по start_location_id такой подход вполне успешно сработал:

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

class TourController extends Controller
{
    ...
    
    /**
     * Displays all Tours for single location
     * @param string $slug
     * @return mixed
     */
    public function actionByLocation($slug)
    {
        $location = Location::findBySlug($slug);

        $searchModel = new SearchTour(['start_location_id' => $location->id]);
        $toursDataProvider = $searchModel->search(Yii::$app->request->queryParams);

        //$model->getBehavior('hit')->touch();

        return $this->render('by-location', [
            'searchModel' => $searchModel,
            'dataProvider' => $toursDataProvider,
            'location' => $location,
        ]);
    }
 
Однако не могу придумать изящного и оптимального с точки зрения производительности пути для вывода в actionByLocation не только туров по заданному start_location_id, но и если переданный location есть среди привязанных через tour_location.
При этом следующим этапом абсолютно точно понадобится "умный фильтр", из которого в этот же экшн могут передаваться ключи одного или нескольких sightseeing, и ListView также необходимо будет перестраивать под это дополнительное условие.

Я уже начал думать об SqlDataProvider, однако решил предварительно спросить у знатоков - наверняка подобные задачи уже кем-то решались.
Подскажите пожалуйста, как оптимальнее всего реализовывать такой функционал?

Заранее благодарен за ответы!

PS: SearchTour пока что стандартный, сгенерированный Gii.
X-Loading
Сообщения: 26
Зарегистрирован: 2011.11.15, 14:55

Re: Вывод ListView по связанным моделям и дополнительным параметрам

Сообщение X-Loading »

Основную идею подсказали на англоязычном форуме, дальше уже развил самостоятельно.

Ввёл в SearchTour новые свойства: location_id и sightseeing_id.
И дополнил метод search() следующим образом:

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

        if (!is_null($this->location_id)) {
            $query->andFilterWhere(['or', ['start_location_id' => $this->location_id], ['tour_location.location_id' => $this->location_id]]);
        }

        if (!is_null($this->sightseeing_id)) {
            if(is_array($this->sightseeing_id)) {
                $condition = ['or'];
                foreach ($this->sightseeing_id as $sightseeing_id) {
                    $condition[] = $sightseeing_id;
                }
                $query->andFilterWhere($condition);
            }
            else {
                $query->andFilterWhere(['tour_sightseeing' => $this->sightseeing_id]);
            }
        }
Ответить