Doctrine 2中的代理是什么?

Jér*_*émy 111 design-patterns proxy-classes doctrine-orm

我刚读完所有的Doctrine 2文档,我开始自己的沙盒,我理解了大部分的原理,但仍有一个问题,我在文档中找不到任何完整的解释.

  1. 什么是Proxy课程?
  2. 什么时候应该在实体上使用它们?

据我所知,代理类添加了一个层,让您可以向实体添加一些其他功能,但为什么要使用代理而不是在实体类中实现方法呢?

Cro*_*zin 160

只要您的查询未返回创建实体所需的所有数据,就会使用代理对象.想象一下以下场景:

@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
Run Code Online (Sandbox Code Playgroud)

正如您所看到的,此查询不返回firstnamelastname属性,因此您无法创建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;
    }
}
Run Code Online (Sandbox Code Playgroud)

UPDATE

在下面的注释部分中,有关代理对象和部分对象之间差异的错误信息.有关详细信息,请参阅@Kontrollfreak答案:https://stackoverflow.com/a/17787070/252591

  • 这个答案包含对代理和部分对象的一些严重误解.请参阅[我的回答](http://stackoverflow.com/questions/4923817/what-is-a-proxy-in-doctrine-2/17787070#17787070)以了解原因. (6认同)

Kon*_*eak 78

代理

Doctrine代理只是一个包装器,它扩展了一个实体类,为它提供了延迟加载.

默认情况下,当您向实体管理器询问与另一个实体关联的实体时,关联的实体将不会从数据库加载,而是包装到代理对象中.当您的应用程序然后请求属性或调用此代理实体的方法时,Doctrine将从数据库加载实体(除非您请求ID,代理始终知道该ID).

这种情况对您的应用程序完全透明,因为代理扩展了您的实体类.

默认情况下,如果您JOIN在查询中不使用它们或将获取模式设置为,则Doctrine会将协会水合作为延迟加载代理EAGER.


现在我必须添加这个,因为我没有足够的声誉在任何地方发表评论:

不幸的是,Crozin的答案包含错误的信息.

如果你执行像这样的DQL查询

SELECT u.id, u.username FROM Entity\User u WHERE u.id = :id
Run Code Online (Sandbox Code Playgroud)

你不会得到一个(代理的)实体对象,而是一个关联数组.因此,不可能延迟加载任何其他属性.

考虑到这一点,我们得出结论,用例示例也不起作用.必须将DQL更改为类似的内容才能$article作为对象访问:

SELECT a FROM Entity\Article a ORDER BY a.createdAt DESC LIMIT 25
Run Code Online (Sandbox Code Playgroud)

返回的属性getContent()必须是一个关联,以便不加载所有 25个实体的内容属性.


部分对象

如果要部分加载不是关联的实体属性,则必须明确告知此Doctrine:

SELECT partial u.{id, username} FROM Entity\User u WHERE u.id = :id
Run Code Online (Sandbox Code Playgroud)

这为您提供了部分加载的实体对象.

但要注意部分对象不是代理!延迟加载不适用于它们.因此,使用部分对象通常是危险的,应该避免.阅读更多:部分对象 - Doctrine 2 ORM 2文档