@Iossarian

Как сортировать кастомное поле с ActiveDataProvider?

Добрый день. Есть таблица Profile со связанными полисами
public function getPolicy() {
        return $this->hasMany(Policy::class, ['user_id' => 'user_id'])
            ->where(['status' => 'payed']);
    }

На сайте устроена система куратор-агент-оператор. Надо, отталкиваясь от куратора получать проданные полисы всей сети, выводить в GridView и сортировать по этому полю. Просто с выводом проблем нет, но с сортировкой есть, т.к. поле скалькулированное.
Механизм поиска всех полисов сети следующий:
public function getCount() {
        if (!empty(Yii::$app->request->get('period'))) {
            $period = Yii::$app->request->get('period');
            if (strpos($period, ' - ') !== false) {
                [$start_date, $end_date] = explode(' - ', $period);
                $start_date = date('Y.m.d', strtotime('midnight', strtotime($start_date)));
                $end_date = date('Y.m.d H:i:s', strtotime('tomorrow', strtotime($end_date)) - 1);
            }
        }

        $agents = User::getChildrenSQL($this->user_id);
        $usersChild = ArrayHelper::getColumn($agents, function ($element) {
            return (int)$element['id'];
        });
        $usersChild[] = $this->user_id;
        if (empty(Yii::$app->request->get('period'))) {
            $policies = Policy::find()->where(['IN', 'user_id', $usersChild])
                ->andWhere(['status' => 'payed'])->all();
        } else {
            $policies = Policy::find()->where(['IN', 'user_id', $usersChild])
                ->andWhere(['status' => 'payed'])->andWhere(['between', 'payed_date', $start_date, $end_date])->all();
        }
        return count($policies);
    }


В searhModel
public $count;
[ [ 'count' ], 'safe' ],
$dataProvider->setSort([
            'attributes'=>[
                'count' => [
                    'asc'=>['count'=>SORT_ASC,],
                    'desc'=>['count'=>SORT_DESC],
                    'default' => SORT_DESC
                ],
            ]
        ]);

Поле вывести получается, но сортировки, естественно нет. Много примеров перебрал. Сортировать получается только при использовании ArrayDataProvider, но мне бы желательно все-таки Active. Подскажите в каком направлении двигаться? Спасибо.
  • Вопрос задан
  • 339 просмотров
Решения вопроса 1
myks92
@myks92 Куратор тега Yii
Нашёл решение — пометь вопрос ответом!
Мне кажется вам стоит лучше работать с массивом и SQL запросами по двум причинам:
1. Вы не загадите сущности дополнительной, не нужной логикой и связями.
2. Запросы на простых массивах выдают данные на много быстрее чем на объектах AR.

Если же вы все таки хотите осуществить вашу затею, то идите по подходу денормализации данных. Создайте дополнительное поле-счётчик, которое будете изменять при создании и редактировании. Таким образом вывод и сортировка этого поля не будет нагружать базу, создавать кучу запросов при каждом изменении сортировочных и фильтруемых параметров, так как это будет поле вашей таблице. На которое ещё и можно повесить индекс для ускорения.
Ответ написан
Комментировать
Пригласить эксперта
Ответы на вопрос 1
slo_nik
@slo_nik Куратор тега Yii
Вам надо добавить в модель Profile публичное свойство для вывода количества проданных полисов.
У Вас оно сейчас называется $count.
Так же есть связь с моделью Policy.
Значит запрос может выглядеть так:
$query = Profile::find()
   /* получаем все поля из таблицы профиля и считаем записи в таблице полисов */
   ->select(['{{%profile}}.*', 'count' => 'COUNT({{%policy}}.id)'])
   /* подгружаем "не жадно" необходимые данные через связь getPolicy() */
  ->joinWith('policy', false)
 /* группируем записи по id профиля */
  ->groupBy('{{%profile}}.id')
/* сортируем по количеству проданных полисов в порядке убывания */
  ->orderBy(['count' => SORT_DESC]);

$dataProvider = new ActiveDataProvider([
   'query' => $query
]);
Ответ написан
Ваш ответ на вопрос

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

Войти через центр авторизации
Похожие вопросы