我刚刚阅读完所有的 Doctrine 2 文档,我开始了自己的沙箱,我了解了大部分原理,但是还有一个问题,我在文档中找不到任何完整的解释。
- 什么是
Proxy
类? - 我什么时候应该在实体上使用它们?
据我了解,代理类添加了一个层来让您向实体添加一些其他功能,但是为什么要使用代理而不是在实体类中实现方法本身呢?
我刚刚阅读完所有的 Doctrine 2 文档,我开始了自己的沙箱,我了解了大部分原理,但是还有一个问题,我在文档中找不到任何完整的解释。
Proxy
类?据我了解,代理类添加了一个层来让您向实体添加一些其他功能,但是为什么要使用代理而不是在实体类中实现方法本身呢?
更新
此答案包含有关代理对象和部分对象之间差异的错误信息。有关更多详细信息,请参阅@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 使用的内部对象。
为什么实体本身不能实现延迟加载?
从技术上讲,它可能是但看看一些随机代理对象的类。它充满了肮脏的代码,呃。很高兴在您的实体中有一个干净的代码。
你能给我一个用例吗?
您正在显示最新 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 代理只是一个扩展实体类的包装器,为它提供延迟加载。
默认情况下,当您向 Entity Manager 询问与另一个实体关联的实体时,关联的实体不会从数据库中加载,而是包装到代理对象中。当您的应用程序随后请求该代理实体的属性或调用方法时,Doctrine 将从数据库中加载该实体(除非您请求 ID,该 ID 始终为代理所知)。
由于代理扩展了您的实体类,这对您的应用程序完全透明。
如果您没有JOIN
在查询中使用它们或将获取模式设置为EAGER
.
现在我必须添加这个,因为我没有足够的声誉在任何地方发表评论:
不幸的是,克罗津的回答包含错误信息。
如果您执行 DQL 查询,例如
SELECT u.id, u.username FROM Entity\User u WHERE u.id = :id
你不会得到一个(代理的)实体对象,而是一个关联数组。所以不可能延迟加载任何额外的属性。
考虑到这一点,可以得出结论,用例示例也不起作用。DQL 必须更改为类似这样才能$article
作为对象访问:
SELECT a FROM Entity\Article a ORDER BY a.createdAt DESC LIMIT 25
并且返回的属性getContent()
必须是一个关联,才能不加载所有25 个实体的内容属性。
如果你想部分加载不是关联的实体属性,你必须明确地告诉这个 Doctrine:
SELECT partial u.{id, username} FROM Entity\User u WHERE u.id = :id
这为您提供了一个部分加载的实体对象。
但请注意,部分对象不是代理!延迟加载不适用于它们。因此,使用局部对象通常是危险的,应该避免。阅读更多:部分对象 — Doctrine 2 ORM 2 文档