Пост рассказывает для чего необходимо использовать «жадную загрузку», вместо «ленивой загрузки», не претендует на идеал. И вряд ли заинтересует профессионалов, больше подходит для начинающих в изучении YII2.

Используем жадную загрузку в своем приложении. Допустим у нас имеется две таблицы с постами и категориями. У каждого поста возможна одна категория, у категорий 1 или более постов.

bd - mediarise.ru


Допустим мы скрудили модели, контроллеры, представления по этим таблицам. Заполним список постов. Обратите внимание, в колонке категорий отображаются сами названия категорий, а не id категорий.

bd - mediarise.ru

Отобразить название категорий можно в представлении: «views/post/index.php» изменив GridView таким образом:

<?php ...
use app\models\Category;
?>

 <?= GridView::widget([
        'dataProvider' => $dataProvider,
        'filterModel' => $searchModel,
        'columns' => [
            ['class' => 'yii\grid\SerialColumn'],

            'id',
            [
                'attribute' => 'category_id',
                'filter' => Category::find()->select(['name', 'id'])->indexBy('id')->column(),
                'value' => 'category.name',
            ],
            'name',
            'content:ntext',

            ['class' => 'yii\grid\ActionColumn'],
        ],
    ]); ?>

  //    'attribute' => 'category_id', к атрибуту category_id, добавляем значение 'value' => 'category.name', где name как раз после с названием категории
  //    'filter' => Category::find()->select(['name', 'id'])->indexBy('id')->column(), -- дает фильтрацию по название категорий
  //    'value' => 'category.name',



Вот как раз, здесь нам и пригодится «жадная загрузка». Дело в том, что если мы посмотрим sql — запросы нашего приложения.

bd - mediarise.ru

Мы увидим множество запросов из таблицы category, такого вида
SELECT * FROM `sb_category` WHERE `id`=x


Где x — id из постов — category_id. Если у нас будет выводится на странице 100, 500, 1000 постов, будет и столько же выборок в таблице post. В данном случае, если посмотреть по времени выполнения скрипта, запросы в БД выполняются быстро. Но если мы можем сэкономить ресурсы, почему бы этим не воспользоваться. Для этого создана «жадная загрузка». Жадная загрузка, это загрузка с избыточными данными, как бы с присоединенной таблицей.

В модели app\models\PostSearc, в методе search, добавим жадную загрузку через метод with

    public function search($params)
    {
        $query = Post::find();


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


После этого смотрим на результат:

bd - mediarise.ru

Сейчас мы видим, вместо многочисленных запросов в БД, мы получаем всего 2. Выборку данных из таблиц psot и category.
Метод with собирает все поля category_id из таблицы post, и делает один запрос в БД, извлекая все категории по условию WHERE `id` IN (1, 2, 3, 4, 5). Т.е в where перечисляются все id, которые есть в первой выборке post
SELECT * FROM `sb_post` LIMIT 20


Для того что бы работала фильтрация, сортировка в GridView. Необходимо вместо with(), использовать joinWith().
joinWith() выполняет жадную загрузку и присоединяет дополнительную таблицу с помощью join и это дает нам возможность добавлять условия, сортировку.
Поделиться с друзьями
-->

Комментарии (21)


  1. Invision70
    22.07.2016 17:47

    Для Ruby On Rails есть gem bullet определяющий N+1 запросы и выдающий стек-трейс где это исправить.
    Интересно, подобная реализации для Yii2 присутствует?

    P.S Загрузите изображения в habrastorage.org


  1. symbix
    22.07.2016 17:49
    -1

    Не сразу понял, о чем речь. Все же «жадный» — это greedy (например, greedy regex quantifier). Eager — разве что в переносном смысле, вообще это скорее «жаждущий».


    1. Lisio
      23.07.2016 00:54
      +1

      Жадная загрузка — это общепринятое понятие, обозначаемое в английском языке как eager loading. И переводится оно именно так. И если перейти к вашему дословному переводу слова eager (вернее, одному из возможных переводов), то человек, испытывающий жажду, пьет с жадностью, отсюда и понятие.


      1. symbix
        23.07.2016 01:14

        Погуглил. Да, это явно общепринятый перевод, вы правы.
        В оправдание скажу, что я, кроме хабра, на русском языке ничего связанного с IT/CS не читаю.


  1. ilyaplot
    22.07.2016 18:10

    У меня на работе VK заблокирован. Почему Вы не выложили картинки на habrastorage? Перезалейте, пожалуйста. Вообще использовать vk как хостинг картинок совершенно неправильно.


    1. ablai
      24.07.2016 16:59

      Попробуйте Opera Developer со включенным VPN или заходите в вк через другой VPN)


      1. ilyaplot
        25.07.2016 10:14

        GRE закрыт.


  1. MediaRise
    22.07.2016 18:22
    +1

    Извините дорогие читатели. Сейчас исправлю изображения.


  1. NorthDakota
    23.07.2016 00:17
    -2

    Може ленивая а не жадная?


    1. sumanai
      23.07.2016 02:42

      Как раз по умолчанию ленивая, а автор повествует о том, как сделать жадную.


    1. vKreker
      23.07.2016 11:48

      Была изначально ленивая, сделали жадную.


  1. stdenis
    23.07.2016 11:48

    Это как раз антонимы.


  1. kawabanga
    23.07.2016 11:48
    +4

    Извините пожалуйста, но о этом знаком практически каждый человек, хоть немного работающий в yii2.
    Статья была бы интересней, если бы вы рассказали о жадной загрузке в подгруженных жадных загрузках. Либо варианты доставать данные через 2-3 загрузки.


    1. stdenis
      23.07.2016 12:34
      +2

      На хабре периодически появляются подобные статьи, поражающие своей очевидностью.


  1. Evsign
    23.07.2016 11:49

    Прост в шоке… Какой чудесный недокументированный метод… А какой прекрасный подход. Спасибо, что сообщили о своей находке. Обязательно возьму на вооружение, а то эти 100500 запросов при моих постоянных выборках для вывода по 500-1000 элементов на страницу уже задрали.


    1. spudro
      23.07.2016 16:08

      Да уж! Учите матчасть.


      1. Evsign
        24.07.2016 09:50
        +2

        Вы похоже такой же капитан, как и автор статьи)


  1. pro100master
    23.07.2016 16:08
    -3

    Я не знаю Yii, но проще, быстрее и вообще, было сделать в основном запросе сразу сделать сразу выборку названий категорий JOIN-ном или SELECT-вом двух таблиц — не имеет значения. Вы берете ORM и потом героический боретесь с ним и, в данном случае, особенностями виджетов GridView.


    1. Vdm17
      24.07.2016 16:59

      Сказали просто чтобы сказать? AR тут дает необходимые возможности. Если вы не в теме Yii, то смысл вашего комментария?


    1. maximkozhin
      24.07.2016 16:59

      Это не борьба с orm, а использование его возможностей.
      Автор как раз это и сделал, использовав with. А раз ему и join нужен был — joinWith(). Это и так в документации очень хорошо написано, автор в данном случае показал, как это к грид применить.


  1. yvm
    24.07.2016 10:40

    Жадность не исключает ленивость, кстати ) collection.load('relation')