Что такое прокси в Доктрине 2?

113

Я только что закончил читать всю документацию Doctrine 2, я запустил свою собственную песочницу, я понял большинство принципов, но все еще есть вопрос, и я не смог найти полного объяснения в документе.

  1. Что такое Proxyклассы?
  2. Когда я должен использовать их вместо сущностей?

Насколько я понимаю, прокси-классы добавляют слой, позволяющий добавлять некоторые другие функции к вашим объектам, но зачем использовать прокси вместо реализации самих методов в классе объекта?

Джереми
источник

Ответы:

160

ОБНОВИТЬ

Этот ответ содержит неверную информацию о различиях между прокси-объектами и частичными объектами. Подробнее см. Ответ @ Kontrollfreak: https://stackoverflow.com/a/17787070/252591


Прокси-объекты используются всякий раз, когда ваш запрос не возвращает все данные, необходимые для создания объекта. Представьте себе следующий сценарий:

@Entity
class User {
     @Column protected $id;
     @Column protected $username;
     @Column protected $firstname;
     @Column protected $lastname;

     // bunch of setters/getters here
}

DQL query:

SELECT u.id, u.username FROM Entity\User u WHERE u.id = :id

Как видите, этот запрос не возвращает firstnameи lastnameсвойства, поэтому вы не можете создать Userобъект. Создание неполного объекта может привести к непредвиденным ошибкам.

Вот почему Doctrine создаст UserProxyобъект, поддерживающий отложенную загрузку. Когда вы попытаетесь получить доступ к firstnameсвойству (которое не загружено), оно сначала загрузит это значение из базы данных.


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

Вы всегда должны писать свой код так, как если бы вы вообще не использовали прокси-объекты. Их можно рассматривать как внутренние объекты, используемые Doctrine.

Почему ленивая загрузка не может быть реализована в самом Entitiy?

Технически это могло быть, но взгляните на какой-нибудь случайный класс прокси-объекта. Там полно грязного кода, тьфу. Приятно иметь чистый код в ваших сущностях.

Можете ли вы предоставить мне пример использования?

Вы отображаете список из 25 последних статей и хотите отобразить подробную информацию о первой. Каждый из них содержит большой объем текста, поэтому получение всех этих данных будет пустой тратой памяти. Вот почему вы не получаете ненужные данные.

SELECT a.title, a.createdAt
FROM Entity\Article a
ORDER BY a.createdAt DESC
LIMIT 25

$isFirst = true;
foreach ($articles as $article) {
    echo $article->getTitle();
    echo $article->getCreatedAt();

    if ($isFirst) {
        echo $article->getContent(); // Article::content is not loaded so it is transparently loaded 
                                     // for this single article.

        $isFirst = false;
    }
}
Crozin
источник
Спасибо за ответ, чем он отличается от Partial Object? Я имею ввиду, почему я должен использовать прокси? Почему ленивая загрузка не может быть реализована в самом Entitiy? Можете ли вы предоставить мне пример использования?
Джереми
1
Частичные объекты и прокси-объекты - это одно и то же - их можно рассматривать как синонимы. Что касается остальных вопросов, проверьте мой обновленный ответ.
Crozin
1
Я не понимаю, почему доктрина не может создать объект, если у него есть только половина свойств. В php я могу создать объект, даже если я не устанавливаю все свойства.
sanders
1
Это отличный ответ, и он должен быть в документации.
Джимбо
7
Этот ответ содержит несколько серьезных неправильных представлений о прокси и частичных объектах. Посмотрите мой ответ, чтобы понять почему.
Kontrollfreak
81

Доверенные

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

По умолчанию, когда вы запрашиваете у Entity Manager сущность, которая связана с другой сущностью, связанная сущность не загружается из базы данных, а упаковывается в прокси-объект. Когда ваше приложение затем запрашивает свойство или вызывает метод этой прокси-сущности, Doctrine загружает сущность из базы данных (кроме случаев, когда вы запрашиваете идентификатор, который всегда известен прокси).

Это происходит полностью прозрачно для вашего приложения из-за того, что прокси расширяет ваш класс сущности.

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


Теперь я должен добавить это, потому что у меня недостаточно репутации, чтобы комментировать везде:

К сожалению, ответ Крозина содержит дезинформацию.

Если вы выполните запрос DQL, например

SELECT u.id, u.username FROM Entity\User u WHERE u.id = :id

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

Имея это в виду, можно прийти к выводу, что пример варианта использования тоже не работает. Чтобы получить доступ $articleкак объект, необходимо изменить DQL на что-то вроде этого :

SELECT a FROM Entity\Article a ORDER BY a.createdAt DESC LIMIT 25

И возвращаемое свойство getContent()должно быть ассоциацией, чтобы не загружать свойства содержимого всех 25 сущностей.


Частичные объекты

Если вы хотите частично загрузить свойства сущности, которые не являются ассоциациями, вы должны явно указать этой Доктрине:

SELECT partial u.{id, username} FROM Entity\User u WHERE u.id = :id

Это дает вам частично загруженный объект сущности.

Но помните, что частичные объекты не являются прокси! Ленивая загрузка к ним не применяется. Следовательно, использование частичных объектов обычно опасно, и этого следует избегать. Подробнее: Частичные объекты - документация Doctrine 2 ORM 2

Kontrollfreak
источник
1
Спасибо, это дает гораздо больше подробностей о том, как Doctrine использует прокси и частичные объекты, чем принятый ответ! И ссылка на документы тоже полезна.
Шон Бин
1
Также для справки, вот раздел документации о прокси-объектах: doctrine-orm.readthedocs.org/en/latest/reference/…
Шон Бин
Итак, при активной загрузке это просто добавление наборов результатов?