Сортировка по вычисляемому полю в GridView. Снова грабли (( (частично решено)

Общие вопросы по использованию второй версии фреймворка. Если не знаете как что-то сделать и это про Yii 2, вам сюда.
Аватара пользователя
girmate
Сообщения: 1533
Зарегистрирован: 2015.10.27, 12:52

Сортировка по вычисляемому полю в GridView. Снова грабли (( (частично решено)

Сообщение girmate » 2016.01.23, 21:46

Собственно, вопрос такой. Имеются две таблицы (Модели): Категория товаров и Товары (связь product.category_id = category.id). Пока ничего особенного.

Конечно же связь в модели Категория присутствует:

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

// связь - товары в категории
    public function getProducts() {
    return $this->hasMany(Product::className(), ['category_id' => 'id']);
    }
 
Собственно вопрос. Я хочу получить в гриде (Категории) вычисляемое поле - количество товаров в Категории. Само количество я получаю и вывожу в гриде без проблем:

грид:

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

 [
        'attribute' => 'countProducts',
        'headerOptions' => ['width' => '50px'],
        ],
Чтобы это работало создал геттер в модели Категории:

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

// вернуть количество товаров в категории 
  
    public function getCountProducts() {
    $products = $this->products;

    return ($products) ? count($products) : 0;
    }
 
Таким образом, пока что все отлично - я получаю в гриде либо ноль, либо количество товаров в категории числом.
Вы скажите, чтобы не париться можно создать поле-счетчик в модели Категория и обновлять его при добавлении/удалении товаров. Но я хочу получить сортировку именно по ВЫЧИСЛЯЕМОМУ полю, мало ли где еще пригодится.

Пробую в модели CategorySearch следующее:

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

// объявим переменную (на всякий случай))
public $countProducts;

    /**
     * @inheritdoc
     */
    public function rules() {
    return [
        [['id', 'parent_id'], 'integer'],
        [['title', 'countProducts'], 'safe'],
    ];
    }
Там же добавим сортировку:

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

$dataProvider->setSort([
        'attributes' => [
        'id',
        'parent_id',
        'title',
        'countProducts' 
        ]
    ]);
 
Остальное стандартно (тут добавляется жадная загрузка):

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

if (!($this->load($params) && $this->validate())) {
        /**
         * Жадная загрузка данных модели Страны
         * для работы сортировки.
         */
        $query->joinWith(['products']);

        // echo '<pre>' . var_dump($dataProvider) . '</pre>';
        return $dataProvider;
    }
 
Успехи есть - заголовок в гриде с нашим полем кликабелен - добро пожаловать - сортируй.

НО! Облом. Поскольку поле вычисляемое - конечно же его нету в модели (БД). И тут при клике на сортировке вылазит ошибка:

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

SQLSTATE[42S22]: Column not found: 1054 Unknown column 'countProducts' in 'order clause'
The SQL being executed was: SELECT `door_category`.* FROM `door_category` LEFT JOIN `door_product` ON `door_category`.`id` = `door_product`.`category_id` ORDER BY `countProducts` LIMIT 50
Что, согласитесь, логично! И что же мне теперь делать? Еще раз отмечу, что не хочу добавлять поле-счетчик в модельку. Как быть?

Кто-то отправит меня на http://nix-tips.ru/yii2-sortirovka-i-fi ... olyam.html. Но там сортировка работает, поскольку соответствующие поля есть в связанной таблице даже при вычисляемом поле (имя+ фамилия). А у меня же оно вообще не связано никак.
Да и плюс в примере по ссылке при попытке добавить строчку вроде этой

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

$this->addCondition($query, 'country_id');
 
вылазит ошибка что такого метода не существует (и в мануале я что-то не нашел его), может синтаксис поменялся, да и вообще не знаю нужно ли мне это((

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

Извините за больше количество кода и текста, много чего перерыл и не нашел (в Yii 1.1 столкнулся с такой же проблемой как-то). Мне кажется тут нужны более глубокие знания, чем у меня. (Мы же не ищем легкий путей, так?)
Последний раз редактировалось girmate 2016.01.31, 15:51, всего редактировалось 1 раз.
Осторожно! Вы общаетесь с новичком ;)


Аватара пользователя
rugabarbo
Сообщения: 1063
Зарегистрирован: 2015.06.21, 16:21
Контактная информация:

Re: Сортировка по вычисляемому полю в GridView. Снова грабли ((

Сообщение rugabarbo » 2016.01.24, 10:02

Йож, в твоих примерах поле вычисляется средствами SQL. Автору топика нужно без SQL.

Аватара пользователя
ElisDN
Сообщения: 5468
Зарегистрирован: 2012.10.07, 10:24
Контактная информация:

Re: Сортировка по вычисляемому полю в GridView. Снова грабли ((

Сообщение ElisDN » 2016.01.24, 11:42

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

$dataProvider = new ArrayDataProvider(['allModels' = $query->all()]);
И поместите все andFilterWhere перед ним.

Аватара пользователя
Йож
Сообщения: 572
Зарегистрирован: 2015.08.26, 03:05

Re: Сортировка по вычисляемому полю в GridView. Снова грабли ((

Сообщение Йож » 2016.01.24, 12:49

ElisDN писал(а):

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

$dataProvider = new ArrayDataProvider(['allModels' = $query->all()]); 
И поместите все andFilterWhere перед ним.
Спасибо. Сортировка идет, но поиск по колонке не работает, запросы в базу по несуществующему полю кидает (что ожидаемо)..

P.S. У меня было осуществлено через запрос. Но после коммента rugabarbo решил попробовать по-другому, как у ТС - для теста.

girmate, если нужен рабочий пример через запрос к базе (как в примерах по ссылкам) - скажите, скину сюда.. Т.к. там тоже не без ошибок гайды..

Аватара пользователя
girmate
Сообщения: 1533
Зарегистрирован: 2015.10.27, 12:52

Re: Сортировка по вычисляемому полю в GridView. Снова грабли ((

Сообщение girmate » 2016.01.24, 21:31

Спасибо всем, кто откликнулся.
Попробовал:

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

$dataProvider = new ArrayDataProvider(['allModels' = $query->all()]);
Все прекрасно, сортировка работает, но слетели фильтры. А фильтры еще важнее.
ElisDn подсказывает что надо куда-то фильтры переместить, я попробовал но ничего не вышло(

Тема по-прежнему осталась открытой(
Осторожно! Вы общаетесь с новичком ;)

Аватара пользователя
girmate
Сообщения: 1533
Зарегистрирован: 2015.10.27, 12:52

Re: Сортировка по вычисляемому полю в GridView. Снова грабли ((

Сообщение girmate » 2016.01.24, 21:34

Сейчас моя модель поиска выглядит так:

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

namespace app\models;

use Yii;
use yii\base\Model;
use yii\data\ActiveDataProvider;
use yii\data\ArrayDataProvider;
use app\models\Category;

/**
 * CategorySearch represents the model behind the search form about `app\models\Category`.
 */
class CategorySearch extends Category {

    public $countProducts;

    /**
     * @inheritdoc
     */
    public function rules() {
    return [
        [['id', 'parent_id'], 'integer'],
        [['title', 'countProducts'], 'safe'],
    ];
    }

    /**
     * @inheritdoc
     */
    public function scenarios() {
    // bypass scenarios() implementation in the parent class
    return Model::scenarios();
    }

    /**
     * Creates data provider instance with search query applied
     *
     * @param array $params
     *
     * @return ActiveDataProvider
     */
    public function search($params) {
    $query = Category::find(); //!! жадная загрузка

//    $dataProvider = new ActiveDataProvider([
//        'query' => $query,
//        'sort' => ['defaultOrder' => ['id' => SORT_ASC]],
//        'pagination' => [
//        'pagesize' => 50,
//        ],
//    ]);

    $dataProvider = new ArrayDataProvider(['allModels' => $query->all()]);
    
    $dataProvider->setSort([
        'attributes' => [
        'id',
        'parent_id',
        'title',
        'countProducts'
        ]
    ]);

    if (!($this->load($params) && $this->validate())) {
        /**
         * Жадная загрузка данных модели Страны
         * для работы сортировки.
         */
        $query->joinWith(['products']);

        // echo '<pre>' . var_dump($dataProvider) . '</pre>';
        return $dataProvider;
    }


    $query->andFilterWhere([
        'id' => $this->id,
        'parent_id' => $this->parent_id,
    ]);


    $query->andFilterWhere(['like', 'title', $this->title]);

    return $dataProvider;
    }

}

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

Может кто еще сталкивался с подобным случаем?
Осторожно! Вы общаетесь с новичком ;)

Аватара пользователя
ElisDN
Сообщения: 5468
Зарегистрирован: 2012.10.07, 10:24
Контактная информация:

Re: Сортировка по вычисляемому полю в GridView. Снова грабли ((

Сообщение ElisDN » 2016.01.24, 23:01

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

public function search($params)
{
    $query = Category::find()->with(['products']);

    if ($this->load($params) && $this->validate()) {
        $query->andFilterWhere([
            'id' => $this->id,
            'parent_id' => $this->parent_id,
        ])->andFilterWhere(['like', 'title', $this->title]);
    }

    return new ArrayDataProvider([
        'allModels' => $query->all(),
        'sort' => [
             'attributes' => [
                  'id',
                  'parent_id',
                  'title',
                  'countProducts',
             ],
        ],
    ]);
}

lynicidn
Сообщения: 2221
Зарегистрирован: 2014.05.24, 15:12

Re: Сортировка по вычисляемому полю в GridView. Снова грабли ((

Сообщение lynicidn » 2016.01.24, 23:11

ElisDN писал(а):

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

public function search($params)
{
    $query = Category::find()->with(['products']);

    if ($this->load($params) && $this->validate()) {
        $query->andFilterWhere([
            'id' => $this->id,
            'parent_id' => $this->parent_id,
        ])->andFilterWhere(['like', 'title', $this->title]);
    }

    return new ArrayDataProvider([
        'allModels' => $query->all(),
        'sort' => [
             'attributes' => [
                  'id',
                  'parent_id',
                  'title',
                  'countProducts',
             ],
        ],
    ]);
}
>if ($this->load($params) && $this->validate())
это очень урезает возможности модели
$search = new SearchModel(['attributes' => Yii::$app->request->get()]);
$search->search([]);

> $query->andFilterWhere([
'id' => $this->id,
'parent_id' => $this->parent_id,
])->andFilterWhere(['like', 'title', $this->title]);

это все упрощается $query->andFilterWhere(['and', [
'id' => $this->id,
'parent_id' => $this->parent_id,
], ['like', 'title', $this->title]]);

> return new ArrayDataProvider([
'allModels' => $query->all(),

и это в return new ActiveDataProvider(['query' => $query

Аватара пользователя
ElisDN
Сообщения: 5468
Зарегистрирован: 2012.10.07, 10:24
Контактная информация:

Re: Сортировка по вычисляемому полю в GridView. Снова грабли ((

Сообщение ElisDN » 2016.01.24, 23:15

lynicidn писал(а):это очень урезает возможности модели
Также урезает, как и стандартный:

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

if (!($this->load($params) && $this->validate())) {
    return $dataProvider;
} 
Это уже вопрос к Gii, а не ко мне.
lynicidn писал(а):и это в return new ActiveDataProvider(['query' => $query
Ну тогда и сортировку по вычисляемому полю к такому варианту допишите.

lynicidn
Сообщения: 2221
Зарегистрирован: 2014.05.24, 15:12

Re: Сортировка по вычисляемому полю в GridView. Снова грабли ((

Сообщение lynicidn » 2016.01.24, 23:22

ElisDN писал(а):
lynicidn писал(а):это очень урезает возможности модели
Также урезает, как и стандартный:

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

if (!($this->load($params) && $this->validate())) {
    return $dataProvider;
} 
Это уже вопрос к Gii, а не ко мне.
lynicidn писал(а):и это в return new ActiveDataProvider(['query' => $query
Ну тогда и сортировку к такому варианту допишите.
1.
$this->load($params);
if ($this->validate()) { ...

2. сортировка в сорт объекте датапровайдера, а вот в вашем случае необходимо было бы подумать об офсет и лимит

lynicidn
Сообщения: 2221
Зарегистрирован: 2014.05.24, 15:12

Re: Сортировка по вычисляемому полю в GridView. Снова грабли ((

Сообщение lynicidn » 2016.01.24, 23:25

да и сортировка у вас не правильно, примерно так надо

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

'sort' => [
                'attributes' => [
                    'virtualAttribute' => [
                        'asc' => ['alias.real_logic1' => SORT_ASC],
                        'desc' => ['alias.real_logic2' => SORT_DESC],
                        'default' => SORT_DESC,
                    ],
                ],
            ], 

Аватара пользователя
ElisDN
Сообщения: 5468
Зарегистрирован: 2012.10.07, 10:24
Контактная информация:

Re: Сортировка по вычисляемому полю в GridView. Снова грабли ((

Сообщение ElisDN » 2016.01.24, 23:31

lynicidn писал(а):2. сортировка в сорт объекте датапровайдера, а вот в вашем случае необходимо было бы подумать об офсет и лимит
Ну об этом надо было автору подумать, когда он свой геттер сочинял. А пока так.

Аватара пользователя
ElisDN
Сообщения: 5468
Зарегистрирован: 2012.10.07, 10:24
Контактная информация:

Re: Сортировка по вычисляемому полю в GridView. Снова грабли ((

Сообщение ElisDN » 2016.01.24, 23:37

lynicidn писал(а):да и сортировка у вас не правильно, примерно так надо

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

'sort' => [
    'attributes' => [
        'virtualAttribute' => [
             ...
         ],
     ],
],
В топике не об этом речь.

lynicidn
Сообщения: 2221
Зарегистрирован: 2014.05.24, 15:12

Re: Сортировка по вычисляемому полю в GridView. Снова грабли ((

Сообщение lynicidn » 2016.01.24, 23:57

ElisDN писал(а):
lynicidn писал(а):да и сортировка у вас не правильно, примерно так надо

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

'sort' => [
    'attributes' => [
        'virtualAttribute' => [
             ...
         ],
     ],
], 
В топике не об этом речь.
об этом =)

lynicidn
Сообщения: 2221
Зарегистрирован: 2014.05.24, 15:12

Re: Сортировка по вычисляемому полю в GridView. Снова грабли ((

Сообщение lynicidn » 2016.01.24, 23:58

вот у меня поля balance нет в таблице, а есть debit и credit

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

                    'balance' => [
                        'asc' => ['debit' => SORT_ASC, 'credit' => SORT_ASC],
                        'desc' => ['debit' => SORT_DESC, 'credit' => SORT_DESC],
                        'default' => SORT_DESC,
                    ], 

Аватара пользователя
girmate
Сообщения: 1533
Зарегистрирован: 2015.10.27, 12:52

Re: Сортировка по вычисляемому полю в GridView. Снова грабли ((

Сообщение girmate » 2016.01.25, 02:06

ElisDN писал(а):

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

public function search($params)
{
    $query = Category::find()->with(['products']);

    if ($this->load($params) && $this->validate()) {
        $query->andFilterWhere([
            'id' => $this->id,
            'parent_id' => $this->parent_id,
        ])->andFilterWhere(['like', 'title', $this->title]);
    }

    return new ArrayDataProvider([
        'allModels' => $query->all(),
        'sort' => [
             'attributes' => [
                  'id',
                  'parent_id',
                  'title',
                  'countProducts',
             ],
        ],
    ]);
}

Отлично! Это работает. Почти. Да, теперь работает фильтрация и сортировка. НО! Некорректно работает колонка "Действия" - та что с кнопочками CRUD (удалить, посмотреть, изменить). Я обратил внимание на то, что там подставляются не id на запись, а просто номера по-порядку начиная с 0. Тут походу надо свою колонку написать, подставляя именно id записи, а не порядковый номер. Наверное сказался тип дата провайдера - номера взяты как порядковые номера массива объектов, а не как их реальные id. Чуть чуть осталось и, можно сказать, задача решена.

Кажется, мне надо как-то это крутить по этому уроку http://nix-tips.ru/yii2-razbiraemsya-s-gridview.html ?
Последний раз редактировалось girmate 2016.01.25, 02:10, всего редактировалось 1 раз.
Осторожно! Вы общаетесь с новичком ;)

Аватара пользователя
girmate
Сообщения: 1533
Зарегистрирован: 2015.10.27, 12:52

Re: Сортировка по вычисляемому полю в GridView. Снова грабли ((

Сообщение girmate » 2016.01.25, 02:10

ElisDN писал(а):
lynicidn писал(а):это очень урезает возможности модели
Также урезает, как и стандартный:

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

if (!($this->load($params) && $this->validate())) {
    return $dataProvider;
}
Это уже вопрос к Gii, а не ко мне.
lynicidn писал(а):и это в return new ActiveDataProvider(['query' => $query
Ну тогда и сортировку по вычисляемому полю к такому варианту допишите.

Объясните, почему и что именно урезается здесь?
Осторожно! Вы общаетесь с новичком ;)

Аватара пользователя
girmate
Сообщения: 1533
Зарегистрирован: 2015.10.27, 12:52

Re: Сортировка по вычисляемому полю в GridView. Снова грабли ((

Сообщение girmate » 2016.01.25, 02:11

ElisDN писал(а):
lynicidn писал(а):2. сортировка в сорт объекте датапровайдера, а вот в вашем случае необходимо было бы подумать об офсет и лимит
Ну об этом надо было автору подумать, когда он свой геттер сочинял. А пока так.

Так ведь пагинация работает как положено, а что не так?
Осторожно! Вы общаетесь с новичком ;)

Аватара пользователя
girmate
Сообщения: 1533
Зарегистрирован: 2015.10.27, 12:52

Re: Сортировка по вычисляемому полю в GridView. Снова грабли ((

Сообщение girmate » 2016.01.25, 02:18

Ну и такой вопрос. Я понимаю, что поставил себе (и, не только себе, как оказалось но и вам пришлось поработать, за что огромное спасибо) задачу с сортировкой именно по ВЫЧИСЛЯЕМОМУ полю.

Но вот скажите мне откровенно. В данном конкретном случае, если есть категории товаров и товары и нужно админу показать колонку с количеством товаров в каждой категории товаров, то как бы Вы поступили в реальности:


1. Добавил в категории товаров поле Количество товаров и добавляли/отнимали при добавлении/удалении товаров по единичке?
2. Сделал вычисляемое поле.
3. Какой-то свой вариант (опишите кратенько).

Хотя я даже не могу предположить какие тут еще варианты.

Вот лично мне кажется, что 1 вариант на самом деле намного предпочтительнее в данном конкретном случае. Колонка не особо будет нагружать БД, Тем более что используется информация из этой колонки только в админке. Поделитесь опытом.

Еще раз спасибо всем, кто откликнулся!
Последний раз редактировалось girmate 2016.01.25, 03:44, всего редактировалось 1 раз.
Осторожно! Вы общаетесь с новичком ;)

Ответить