CActiveDataProvider и GROUP

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

CActiveDataProvider и GROUP

Сообщение ElisDN » 2012.11.02, 18:45

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

Создаём классические таблицы Юзера и Новости:

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

CREATE TABLE IF NOT EXISTS `test_new` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `user_id` int(11) NOT NULL,
  `title` varchar(255) NOT NULL,
  PRIMARY KEY (`id`),
  KEY `user_id` (`user_id`), 
  FOREIGN KEY (user_id)
    REFERENCES test_user(id)
) ENGINE=MyISAM;

INSERT INTO `test_new` (`id`, `user_id`, `title`) VALUES
(1, 3, 'new1'),
(2, 1, 'new2'),
(3, 1, 'new3'),
(4, 2, 'new4'),
(5, 3, 'new5'),
(6, 4, 'new6');

CREATE TABLE IF NOT EXISTS `test_user` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(255) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=MyISAM;

INSERT INTO `test_user` (`id`, `name`) VALUES
(1, 'user1'),
(2, 'user2'),
(3, 'user3'),
(4, 'user4');
Делаем связь от новости к автору:

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

class TestNew extends CActiveRecord
{
    public function relations()
    {
        return array(
            'user' => array(self::BELONGS_TO, 'TestUser', 'user_id'),
        );
    }
}
И для вывода ставим CListView в index.php

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

<?php $this->widget('zii.widgets.CListView', array(
    'dataProvider'=>$dataProvider,
    'itemView'=>'_view',
)); ?>

<p>TotalItemCount = <?php echo $dataProvider->totalItemCount; ?></p>
<p>ItemCount = <?php echo $dataProvider->itemCount; ?></p>
c _view.php

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

<fieldset>
    <h2><?php echo $data->title; ?></h2>
    <p>Автор: <?php echo $data->user->name; ?></p>
</fieldset>
Собираем провайдер с жадной загрузкой и группировкой по пользователю:

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

class TestController extends Controller
{
    public function actionIndex()
    {
        $criteria = new CDbCriteria();
        $criteria->with = array('user');        
        $criteria->group = 'user_id';

        $dataProvider = new CActiveDataProvider('TestNew', array(
            'criteria'=>$criteria,
        ));

        $this->render('index', array(
            'dataProvider'=>$dataProvider,
        ));
    }
}
и список исправно выводит 4 новости для четырёх пользователей, но показывает что выводит 4 из 6:

Изображение

Причём если убрать жадную загрузку, то есть строку

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

$criteria->with = array('user'); 

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

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

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

SELECT COUNT(*) FROM (SELECT * FROM `test_new` `t` GROUP BY user_id);
SELECT * FROM `test_new` `t` GROUP BY user_id LIMIT 10;
А вот запросы с жадной:

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

SELECT COUNT(DISTINCT `t`.`id`) FROM `test_new` `t` LEFT OUTER JOIN `test_user` `user` ON (`t`.`user_id`=`user`.`id`);
SELECT * FROM `test_new` `t` LEFT OUTER JOIN `test_user` `user` ON (`t`.`user_id`=`user`.`id`) GROUP BY user_id LIMIT 10;
Отсюда видно, что во втором случае группировка 'GROUP BY user_id' отсутствует при вычислении общего числа элементов. Именно поэтому totalItemCount в примере всегда показывает 6 независимо от содержимого $sriteria->group.

Добавив пагинацию

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

$dataProvider = new CActiveDataProvider('TestNew', array(
    'criteria'=>$criteria,
    'pagination'=>array(
        'pageSize'=>5
    )
));
получаем следствие из этого глюка:

Изображение

Элементов найдено 4, а все думают, что их 6 и на вторую страницу ещё хватит.

Как выход, можно не использовать жадную загрузку, но при группировке по полю связанной таблицы

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

$criteria->with = array('user');
$criteria->group = 'user.name';
без указания в with отношения 'user' поле 'user.name' использовать невозможно.
Последний раз редактировалось ElisDN 2012.11.15, 14:19, всего редактировалось 1 раз.

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

Re: CActiveDataProvider и GROUP

Сообщение ElisDN » 2012.11.04, 11:59

Оказалось, что багу 9 месяцев.

Аватара пользователя
Elfer
Сообщения: 128
Зарегистрирован: 2012.06.07, 16:09
Откуда: Беларусь, Лида
Контактная информация:

Re: CActiveDataProvider и GROUP

Сообщение Elfer » 2013.02.20, 22:28

Столкнулся точно с такой же проблемой. Я использую dataProvider в CGridView. Выводит элементы правильно, но показывает "Displaying 1-4 of 311 results" и выводит пейджинг. На самом деле только 4 результата.
Приколы: юмор, анекдоты - Zasmeshi.Ru сайт на Yii, великие возможности фреймворка.

Аватара пользователя
Elfer
Сообщения: 128
Зарегистрирован: 2012.06.07, 16:09
Откуда: Беларусь, Лида
Контактная информация:

Re: CActiveDataProvider и GROUP

Сообщение Elfer » 2013.02.20, 22:34

Нашел решение на странице:

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

'totalItemCount'=>count($this->model()->findAll($criteria)) 
Вроде работает.
Приколы: юмор, анекдоты - Zasmeshi.Ru сайт на Yii, великие возможности фреймворка.

Аватара пользователя
anton44eg
Сообщения: 2716
Зарегистрирован: 2012.01.25, 13:37
Откуда: Киев

Re: CActiveDataProvider и GROUP

Сообщение anton44eg » 2013.02.20, 23:33

Почему не

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

'totalItemCount'=>$this->model()->count($criteria)  
?
Зачем вам загружать все модели, чтобы узнать количество записей

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

Re: CActiveDataProvider и GROUP

Сообщение ElisDN » 2013.04.06, 10:05

Вроде бы вчера починили. Ждём 1.1.14.

Ответить