教义2中的代理是什么?


112

我刚读完所有《 Doctrine 2》文档,开始了自己的沙箱,了解了大多数原理,但是仍然有一个问题,我在文档中找不到任何完整的解释。

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

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

Answers:


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

正如你可以看到这个查询不返回firstnamelastname属性,因此,你不能创建User对象。创建不完整的实体可能会导致意外错误。

因此,Doctrine将创建UserProxy支持延迟加载的对象。当您尝试访问firstname属性(未加载)时,它将首先从数据库加载该值。


我的意思是为什么我应该使用代理?

您应该始终像完全不使用代理对象一样编写代码。它们可以被当作教义使用的内部对象。

为什么不能在实体本身中实现延迟加载?

从技术上讲可能是,但是请看一下一些随机代理对象的类。到处都是肮脏的代码,呃。在您的实体中编写干净的代码真是太好了。

你能给我一个用例吗?

您正在显示最新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;
    }
}

谢谢您的回答,与Partial Object有什么不同?我的意思是为什么我应该使用代理?为什么不能在Entitiy本身中实现延迟加载?你能给我一个用例吗?
杰里米

1
部分对象和代理对象是同一件事-可以将它们视为同义词。至于其余的问题,请查看我的最新答案。
Crozin 2011年

1
我不明白为什么如果学说只有一半的属性就不能创建它。在php中,即使我没有设置所有属性,我也可以创建一个对象。
桑德斯,

1
这是一个非常棒的答案,应该在文档中。
Jimbo

7
该答案包含对代理和部分对象的一些严重误解。查看我的答案以了解原因。
2013年

81

代理人

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

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

由于代理扩展了您的实体类,因此这对于您的应用程序是完全透明的。

如果您JOIN在查询中未将懒惰负载代理或将访存模式设置为,则默认情况下,Doctrine会将水合物关联作为延迟负载代理EAGER


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

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

如果您执行DQL查询,例如

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

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

考虑到这一点,可以得出结论,用例示例也不起作用。为了将DQL $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文档


1
谢谢,这比接受的答案提供了更多关于Doctrine如何使用代理和部分对象的信息!并且对文档的引用也很有帮助。
肖恩·比恩

1
同样作为参考,这是有关代理对象的文档部分:doctrine-orm.readthedocs.org/en/latest/reference/…–
肖恩·比恩

因此,在进行急切的加载时,它基本上只是添加结果集吗?
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.