什么是原则2中的代理?

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

  1. 什么是 Proxy课程?
  2. 什么时候对实体使用它们?

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

35763 次浏览

更新

这个答案包含关于代理对象和部分对象之间差异的错误信息


当查询没有返回创建实体所需的所有数据时,将使用代理对象:

@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

As you can see this query doesn't return firstname and lastname properties, therefore you cannot create User object. Creation of incomplete entity could lead to unexpected errors.

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


我的意思是,我为什么要使用代理?

您应该始终编写代码,就好像根本没有使用代理对象一样。它们可以被视为“主义”所使用的内部对象。

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

Technically it could be but take a look at some random proxy object's class. It's full of dirty code, ugh. It's nice to have a clean code in your entities.

你能给我提供一个用例吗?

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

代理服务器

Doctrine 代理只是一个包装器,它扩展了一个实体类来为其提供 Lazy Loding。

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

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

如果在查询中没有 JOIN它们,或者没有将提取模式设置为 EAGER,那么 Doctrine 默认情况下将水合物关联作为延迟加载代理。


Now I must add this because I don't have enough reputation to comment everywhere:

不幸的是,克罗津的回答包含了错误的信息。

如果执行 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

这将为您提供一个部分加载的实体对象。

但是要注意,部分对象 不是代理!懒惰装载不适用于他们。因此,使用部分对象通常是危险的,应该避免。延伸阅读: 部分对象ーー原则2 ORM 2文档