我刚刚完成了Doctrine 2的所有文档阅读,开始使用自己的沙盒实验,我理解了大部分原则,但仍有一个问题,我在文档中找不到完整的解释。
Proxy
类是什么?- 什么时候应该使用它们替代实体?
据我所知,代理类可以增加一层来添加一些其他特性到您的实体中,但为什么要使用代理类而不是在实体类中自己实现这些方法呢?
我刚刚完成了Doctrine 2的所有文档阅读,开始使用自己的沙盒实验,我理解了大部分原则,但仍有一个问题,我在文档中找不到完整的解释。
Proxy
类是什么?据我所知,代理类可以增加一层来添加一些其他特性到您的实体中,但为什么要使用代理类而不是在实体类中自己实现这些方法呢?
更新
这个答案关于代理对象和部分对象之间的差异包含错误信息。请参考 @Kontrollfreak 的回答以获取更多细节:https://dev59.com/fG445IYBdhLWcg3wUYrM#17787070
当你的查询没有返回创建一个实体所需的所有数据时,就会使用代理对象。想象以下情境:
@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
对象。创建不完整的实体可能会导致意外错误。UserProxy
对象。当您尝试访问未加载的firstname
属性时,它将首先从数据库中加载该值。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 代理是一个包装器,它扩展实体类以为其提供延迟加载。
默认情况下,当您要求实体管理器获取与另一个实体关联的实体时,相关联的实体不会从数据库中加载,而是被包装成代理对象。当您的应用程序请求此代理实体的属性或调用其方法时,Doctrine 将从数据库中加载实体(除非您请求 ID,因为代理始终知道 ID)。
由于这个代理扩展了您的实体类,所以这个过程对于您的应用程序来说是完全透明的。
如果您在查询中没有使用JOIN
语句或将提取模式设置为EAGER
,Doctrine 默认会将关联作为延迟加载代理进行填充。
现在我必须补充一下,因为我没有足够的声望在任何地方发表评论:
不幸的是,Crozin 的回答包含错误信息。
如果您执行像下面这样的 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
如果您想部分加载不是关联的实体属性,则必须明确告知Doctrine:
getContent()
返回的属性必须是一个关联,以便不加载所有 25个实体的内容属性。
SELECT partial u.{id, username} FROM Entity\User u WHERE u.id = :id
这将给您一个部分加载的实体对象。
但要注意,部分对象不是代理!惰性加载不适用于它们。因此,通常情况下应避免使用部分对象,因为这是危险的。了解更多信息: 部分对象 — Doctrine 2 ORM 2 文档