Ленивая загрузка ассоциаций (Lazy Loading)


Рубрика: CakePHP 3

9K


Штатная, используемая в CakePHP «жадная» загрузка ассоциаций (Eager Loading)

Для начала давайте рассмотрим пример встроенной в CakePHP «жадной» загрузки связанных с объектом ассоциаций при помощи метода contains(). Как уже говорилось, при такой загрузке, данные ассоциаций будут запрашиваться из базы и передаваться в шаблон вида в любом случае, независимо от того были они по факту вами использованы или нет:

//например в методе view контроллера UsersController
//получаем пользователя по его id
$user = $this->Users->get($id, [
     'contain' => [
           'Articles' //дополнительно запрашиваем связанные с пользователем статьи
      ]
]);

//далее можно обратиться к статьям пользователя:
 $user->articles

Использование «ленивой загрузки» (Lazzy Loading)

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

//получаем пользователя по его id
$user = $this->Users->get($id);

//обращаемся к статьям пользователя
$user->articles

Можно ли использовать ленивую (Lazy) и жадную (Eager) загрузки совместно?

Да, можно. Ленивая загрузка не будет перезаписывать результаты, которые были сформированы ранее при помощи жадной загрузки. Вы можете продолжать писать сложные условия получения ассоциативных данных и по-прежнему использовать ленивый загрузчик:

//получаем пользователя по его id
$user = $this->Users->get($id, [
     'contain' => [
           'Articles' //запрашиваем связанные с пользователем статьи
      ]
]);

//обращаемся к статьям пользователя, полученным при помощи жадной загрузки:
$user->articles
//для первой в массиве статьи получаем связанные комментарии при помощи ленивой загрузки:
$user->articles[0]->comments

Как реализовать ленивую загрузку в приложении CakePHP 3?

Изначально ленивая загрузка не включена в ORM CakePHP, для этого вы можете использовать один из плагинов сообщества кейка. На официальном сайте фреймворка рекомендован плагин LazyLoad. Плагин устанавливается и запускается в работу очень просто.

Шаг 1 Находясь в корневой папке проекта из командной строки установите в свое приложение плагин:

$ composer require jeremyharris/cakephp-lazyload

Шаг 2 Если у вас есть базовый объект, добавьте необходимый код в него, чтобы получить ленивую загрузку по всем объектам потомкам. Или разместите код в конкретном объекте, если будете применять ленивую загрузку только для него:

<?php
namespace App\Model\Entity;

use Cake\ORM\Entity;
use JeremyHarris\LazyLoad\ORM\LazyLoadEntityTrait;

class User extends Entity //ленивая загрузка будет применяться только для объекта User
{
    use LazyLoadEntityTrait;
}

Теперь все ассоциации, относящиеся к объекту User можно «лениво» загружать!

Поддерживаемые методы

  • Получение ассоциации: $user->get('articles'), $user->articles;
  • Проверка на существование ассоциации: $user->has('articles);
  • Отключение ассоциации: $user->unsetProperty('articles');

При отключении свойства через Entity::unsetProperty() ассоциация будет исключена из ленивой загрузки в будущем для этого объекта. Если вы хотите повторно вернуть ассоциацию, то можно использовать Table::loadInto, как это предусмотрено в ORM:

<?php
$user= $this->Users->get(1); получаем пользователя с id = 1

// ленивой загрузкой получаем статьи пользователя
$user->articles;
// удаляем статьи из сущности 
$user->unsetProperty('articles');
// теперь проверка на существование связанных с пользователем статей вернет false:
$user->has('articles');
// если мы хотим снова получить доступ к статьям, мы можем вручную загрузить их
$user = $this->Users->loadInto($user, ['Articles']);

Дополнительно, для тех кто предпочитает видеоуроки, я записал видео по данной теме:

На этом у меня всё, надеюсь полученная информация будет для вас полезной. Если не хотите в будущем пропускать появления нового на блоге, подписывайтесь на RSS ленту и вступайте в группу Вконтакте.