Русскоязычный форум, посвященный фреймворку Kohana

Все о фреймворке Kohana. Обсуждение уроков, документации.
Текущее время: 14 ноя 2018, 23:32

Часовой пояс: UTC + 4 часа [ Летнее время ]




Начать новую тему Ответить на тему  [ Сообщений: 11 ]  На страницу 1, 2  След.
Автор Сообщение
 Заголовок сообщения: ORM. Расширяем count_all() для DISTINCT
СообщениеДобавлено: 09 сен 2013, 01:41 
Не в сети
Бывалый
Аватара пользователя

Зарегистрирован: 05 июн 2012, 03:08
Сообщения: 213
В общем выходит такая штука, кто как решал или решил задачу?
Пример. Есть блог "много-ко-многим". Нужно выбрать все записи этой рубрики и записи из всех её подрубрик.
Допустим, получили массив id-шников нужных подрубрик включая текущую $ids = array('13', '14', '22')
Т.к. одна запись может быть в нескольких рубриках нужен запрс с DISTINCT
Выбираем записи в блоге из нескольких рубрик
Код:
$posts = ORM::factory('Blog_Post')
    ->distinct(TRUE)
    ->join(array('blog_posts_rubrics', 'bpr'))
    ->on('bpr.post_id', '=', 'blog_post.id')
    ->where('enabled', '=', 1)
    ->where('bpr.rubric_id', 'IN', $ids);
  
Теперь перед find_all() нужна постраничка, т.е. нужно вызвать count_all()->reset(FALSE) и тут проблемка. Будет сгенерирован запрос
Код:
SELECT DISTINCT COUNT(*) AS `records_found` 
FROM `blog_posts` AS `blog_post`
JOIN `blog_posts_rubrics` AS `bpr` ON (`bpr`.`post_id` = `blog_post`.`id`)
WHERE `enabled` = 1 AND `bpr`.`rubric_id` IN ('13', '14', '22')
т.е. этот DISTINCT ни начто не влияет и посчитает дубликаты, что нам не нужно.
Через билдер запрос строится так
Код:
...->select(array(DB::expr('COUNT(DISTINCT `id`)'), 'records_found'))  
Через ORM я так и не понял как это сделать, поэтому расширил стандартный метод и получился такой костыль:
Код:
class ORM extends Kohana_ORM
{
    /**
     * Count the number of records in the table.
     *
     * @param bool $reset    Pass FALSE to avoid resetting on the next call
     * @param bool $distinct used distinct?
     *
     * @return int
     */
    public function count_all($reset = TRUE, $distinct = FALSE)
    {
        $selects = array();

        foreach ($this->_db_pending as $key => $method)
        {
            if ($distinct)
            {
                if ($method['name'] == 'distinct')
                {
                    // Ignore any distinct method for now
                    $selects[] = $method;
                    unset($this->_db_pending[$key]);
                }
            }

            if ($method['name'] == 'select')
            {
                // Ignore any selected columns for now
                $selects[] = $method;
                unset($this->_db_pending[$key]);
            }
        }

        if ( ! empty($this->_load_with))
        {
            foreach ($this->_load_with as $alias)
            {
                // Bind relationship
                $this->with($alias);
            }
        }

        $this->_build(Database::SELECT);

        if ($distinct)
            $records = $this->_db_builder
                
->from(array($this->_table_name, $this->_object_name))
                ->select(array(DB::expr('COUNT(DISTINCT(`'.$this->_primary_key.'`))'), 'records_found'))
                ->execute($this->_db)
                ->get('records_found');
        else
            $records 
= $this->_db_builder
                
->from(array($this->_table_name, $this->_object_name))
                ->select(array(DB::expr('COUNT(*)'), 'records_found'))
                ->execute($this->_db)
                ->get('records_found');

        // Add back in selected columns
        $this->_db_pending += $selects;

        $this->reset($reset);

        // Return the total number of records in a table
        return $records;
    }
}
  
Теперь количество считает верно
Код:
$posts = ORM::factory('Blog_Post')
    ->distinct(TRUE)
    ->join(array('blog_posts_rubrics', 'bpr'))
    ->on('bpr.post_id', '=', 'blog_post.id')
    ->where('enabled', '=', 1)
    ->where('bpr.rubric_id', 'IN', $ids);

$count = $posts->count_all(FALSE, TRUE);
$posts = $posts->find_all();
  
Получаются запросы, которые нам нужны: количество уникальных записей для постранички и список уникальных записей для вывода в шаблоне (постраничку для простоты убрал)
Код:
SELECT COUNT(DISTINCT(`id`)) AS `records_found` 
FROM `blog_posts` AS `blog_post`
JOIN `blog_posts_rubrics` AS `bpr` ON (`bpr`.`post_id` = `blog_post`.`id`)
WHERE `enabled` = 1 AND `bpr`.`rubric_id` IN ('13', '14', '22')
Код:
SELECT DISTINCT `blog_post`.`id` AS `id`, `blog_post`.`title` AS `title`
FROM `blog_posts` AS `blog_post`
JOIN `blog_posts_rubrics` AS `bpr` ON (`bpr`.`post_id` = `blog_post`.`id`)
WHERE `enabled` = 1 AND `bpr`.`rubric_id` IN ('13', '14', '22')

Может я чего-то упустил или не знаю как это делается для ORM или это можно сделать изящнее.
Какие будут предложения?

_________________
http://de-en.info (работает на Kohana 3.3)


Вернуться к началу
 Профиль  
 
 Заголовок сообщения: Re: ORM. Расширяем count_all() для DISTINCT
СообщениеДобавлено: 09 сен 2013, 10:13 
Не в сети
Бывалый
Аватара пользователя

Зарегистрирован: 02 апр 2013, 16:26
Сообщения: 474
Откуда: Сергиев Посад
Код:
$posts = ORM::factory('Blog_Post')
   ->select(array(DB::expr('COUNT(DISTINCT `id`)'), 'records_found'))
    ->join(array('blog_posts_rubrics', 'bpr'))
    ->on('bpr.post_id', '=', 'blog_post.id')
    ->where('enabled', '=', 1)
    ->where('bpr.rubric_id', 'IN', $ids);

а так?

_________________
Майкл Джордан играет в баскетбол. Чарльз Мэнсон убивает людей. Я пишу код. У каждого свой талант.


Вернуться к началу
 Профиль  
 
 Заголовок сообщения: Re: ORM. Расширяем count_all() для DISTINCT
СообщениеДобавлено: 09 сен 2013, 10:28 
Не в сети
Бывалый
Аватара пользователя

Зарегистрирован: 05 июн 2012, 03:08
Сообщения: 213
Если вызывать ->count_all() он в любом случае влепит в запрос SELECT DISTINCT COUNT(*) AS `records_found`
а в ->find_all() SELECT DISTINCT COUNT(DISTINCT `id`) AS `records_found`

Выходит в коде if ($method['name'] == 'select') - unset делается
потом выполняется ->select(array(DB::expr('COUNT(*)'), 'records_found'))
а потом метод select возвращается $this->_db_pending += $selects;

Тут и в билдере то просто ->distinct() не проходит http://kohanaframework.org/3.3/guide/da ... -functions

_________________
http://de-en.info (работает на Kohana 3.3)


Вернуться к началу
 Профиль  
 
 Заголовок сообщения: Re: ORM. Расширяем count_all() для DISTINCT
СообщениеДобавлено: 09 сен 2013, 11:41 
Не в сети
Бывалый
Аватара пользователя

Зарегистрирован: 02 апр 2013, 16:26
Сообщения: 474
Откуда: Сергиев Посад
http://dev.kohanaframework.org/projects ... uery_id=65 посмотри исправления 3.3.1 3.4 может уже пофиксили, если нет в баг реквест заявку и свое решение кинь

_________________
Майкл Джордан играет в баскетбол. Чарльз Мэнсон убивает людей. Я пишу код. У каждого свой талант.


Вернуться к началу
 Профиль  
 
 Заголовок сообщения: Re: ORM. Расширяем count_all() для DISTINCT
СообщениеДобавлено: 09 сен 2013, 12:13 
Не в сети
Бывалый
Аватара пользователя

Зарегистрирован: 05 июн 2012, 03:08
Сообщения: 213
Знать бы еще как это делается

_________________
http://de-en.info (работает на Kohana 3.3)


Вернуться к началу
 Профиль  
 
 Заголовок сообщения: Re: ORM. Расширяем count_all() для DISTINCT
СообщениеДобавлено: 09 сен 2013, 12:17 
Не в сети
Бывалый
Аватара пользователя

Зарегистрирован: 05 июн 2012, 03:08
Сообщения: 213
В общем вот, может кому пригодится:
Код:
    /**
     * Count the number of records in the table.
     *
     * @param bool $reset Pass FALSE to avoid resetting on the next call
     *
     * @return int
     */
    public function count_all($reset = TRUE)
    {
        $is_distinct = FALSE;
        $selects = array();

        foreach ($this->_db_pending as $key => $method)
        {
            if ($method['name'] == 'distinct')
            {
                $is_distinct = TRUE;

                // Ignore any selected columns for now
                $selects[] = $method;
                unset($this->_db_pending[$key]);
            }

            if ($method['name'] == 'select')
            {
                // Ignore any selected columns for now
                $selects[] = $method;
                unset($this->_db_pending[$key]);
            }
        }

        if ( ! empty($this->_load_with))
        {
            foreach ($this->_load_with as $alias)
            {
                // Bind relationship
                $this->with($alias);
            }
        }

        $this->_build(Database::SELECT);

        $records = $this->_db_builder
            
->from(array($this->_table_name, $this->_object_name));

        if ($is_distinct)
        {
            $records->select(array(DB::expr('COUNT(DISTINCT(`'.$this->_primary_key.'`))'), 'records_found'));
        }
        else
        
{
            $records->select(array(DB::expr('COUNT(*)'), 'records_found'));
        }

        $records = $records->execute($this->_db)
            ->get('records_found');

        // Add back in selected columns
        $this->_db_pending += $selects;

        $this->reset($reset);

        // Return the total number of records in a table
        return $records;
    }
}
 

_________________
http://de-en.info (работает на Kohana 3.3)


Вернуться к началу
 Профиль  
 
 Заголовок сообщения: Re: ORM. Расширяем count_all() для DISTINCT
СообщениеДобавлено: 09 сен 2013, 12:34 
Не в сети
Бывалый
Аватара пользователя

Зарегистрирован: 02 апр 2013, 16:26
Сообщения: 474
Откуда: Сергиев Посад
AmberLEX есть такой момент что count(*) работает быстрее count(id) и если запрос без условий он автоматически заменяется на другой еще более быстрый(не вспомню синтаксис сейчас, но можешь в профилере посмотреть) т.ч. лучше сделать эту фишку опциональной

_________________
Майкл Джордан играет в баскетбол. Чарльз Мэнсон убивает людей. Я пишу код. У каждого свой талант.


Вернуться к началу
 Профиль  
 
 Заголовок сообщения: Re: ORM. Расширяем count_all() для DISTINCT
СообщениеДобавлено: 09 сен 2013, 12:41 
Не в сети
Бывалый
Аватара пользователя

Зарегистрирован: 05 июн 2012, 03:08
Сообщения: 213
Так у нас тут если нет ->distinct(TRUE) то и будет count(*)
А для COUNT(DISTINCT(...)) по любому какое-то поле нужно, так же не напишешь: COUNT(DISTINCT(*))
Просто $this->_primary_key может не быть или еще какие-то нюансы с другой СУБД

_________________
http://de-en.info (работает на Kohana 3.3)


Вернуться к началу
 Профиль  
 
 Заголовок сообщения: Re: ORM. Расширяем count_all() для DISTINCT
СообщениеДобавлено: 09 сен 2013, 12:55 
Не в сети
Бывалый
Аватара пользователя

Зарегистрирован: 05 июн 2012, 03:08
Сообщения: 213
Я убрал параметр. Вот как примерно у меня в коде идет:
Код:
// Если показываем записи только текущей рубрики
if ($rubric->show_posts)
{
    $posts = $rubric->posts
        
->where('enabled', '=', 1);
}
// Показываем записи текущей рубрики и записи из всех подрубрик
else
{
    // Получеам id-шники подрубрик учитывая и текущую
    $ids = $rubric->descendants_ids(TRUE);

    $posts = ORM::factory('Blog_Post')
        ->distinct(TRUE)
        ->join(array('blog_posts_rubrics', 'bpr'))
        ->on('bpr.post_id', '=', 'blog_post.id')
        ->where('enabled', '=', 1)
        ->where('bpr.rubric_id', 'IN', $ids);
}
$pagination = Pagination::get($posts->count_all(FALSE), Config::get('blog.per_page_frontend'));
$posts = $posts->find_per_page($pagination->offset, $pagination->items_per_page); // тут я твой вариант подмотрел)
 
Если сделать опционально как я делал вначале, то $pagination нужно будет прописать в двух условиях:
- без параметра $posts->count_all(FALSE)
- и с параметром $posts->count_all(FALSE, TRUE)
т.к. в одном случае он нужен, а в другом - нет.
Ну или в них считать $count_all = $posts->count_all(...), а pagination оставить за if...else

_________________
http://de-en.info (работает на Kohana 3.3)


Вернуться к началу
 Профиль  
 
 Заголовок сообщения: Re: ORM. Расширяем count_all() для DISTINCT
СообщениеДобавлено: 10 сен 2013, 18:58 
Не в сети
Бывалый
Аватара пользователя

Зарегистрирован: 02 апр 2013, 16:26
Сообщения: 474
Откуда: Сергиев Посад
a если использовать group by id ?

_________________
Майкл Джордан играет в баскетбол. Чарльз Мэнсон убивает людей. Я пишу код. У каждого свой талант.


Вернуться к началу
 Профиль  
 
Показать сообщения за:  Поле сортировки  
Начать новую тему Ответить на тему  [ Сообщений: 11 ]  На страницу 1, 2  След.

Часовой пояс: UTC + 4 часа [ Летнее время ]


Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 1


Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете добавлять вложения

Найти:
Перейти:  
cron
Все о фреймворке Kohana  | 
Powered by phpBB® Forum Software © phpBB Group