原则 2 中的代理人是什么?

发布于 2024-10-16 01:02:57 字数 199 浏览 3 评论 0原文

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

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

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

I just finished reading all the Doctrine 2 documentation, I started my own sandbox, I understood most of the principes, but there is still a question and I couldn't find any complete explanation in the doc.

  1. What are Proxy classes?
  2. When should I use them over entities?

As far as I understand, proxy classes add a layer to let you add some other features to your entities, but why use a proxy instead of implementing the methods themselves in the entity class?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

呆° 2024-10-23 01:02:57

更新

此答案包含有关代理对象和部分对象之间差异的错误信息。有关更多详细信息,请参阅@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 属性(未加载)时,它将首先从数据库加载该值。


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

您应该始终像根本不使用代理对象一样编写代码。它们可以被视为 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;
    }
}

UPDATE

This answer contains wrong information about differences between proxy objects and partial objects. See @Kontrollfreak's answer for more details: https://stackoverflow.com/a/17787070/252591


Proxy objects are used whenever your query doesn't return all data required to create an entity. Imagine following scenario:

@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.

That's why Doctrine will create UserProxy object that supports lazy loading. When you'll try to access firstname property (which is not loaded) it will first load that value from database.


I mean why should I use a proxy ?

You should always write your code as if you didn't use proxy objects at all. They can be treated as internal objects used by Doctrine.

Why the lazy loading can't be implemented in the Entitiy itself?

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.

Can you provide me an use case?

You're displaying a list of latest 25 articles and you want to display a details of the first one. Each of them contain a large amount of text, so fetching all that data would be a waste of memory. That's why you don't fetch unnecessary data.

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;
    }
}
薄情伤 2024-10-23 01:02:57

代理

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

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

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

如果您没有在查询中JOIN或将获取模式设置为EAGER,Doctrine默认会将关联关联作为延迟加载代理。


现在我必须添加这一点,因为我没有足够的声誉来到处发表评论:

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

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

Proxies

A Doctrine proxy is just a wrapper that extends an entity class to provide Lazy Loading for it.

By default, when you ask the Entity Manager for an entity that is associated with another entity, the associated entity won't be loaded from the database, but wrapped into a proxy object. When your application then requests a property or calls a method of this proxied entity, Doctrine will load the entity from the database (except when you request the ID, which is always known to the proxy).

This happens fully transparent to your application due to the fact that the proxy extends your entity class.

Doctrine will by default hydrate associations as lazy load proxies if you don't JOIN them in your query or set the fetch mode to EAGER.


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

Unfortunately, Crozin's answer contains misinformation.

If you execute a DQL query like

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

you won't get a (proxied) entity object, but an associative array. So it's not possible to lazy load any additional properties.

With this in mind, one comes to the conclusion that the use case example won't work either.
The DQL would have to be changed to something like this in order to access $article as object:

SELECT a FROM Entity\Article a ORDER BY a.createdAt DESC LIMIT 25

And the property returned by getContent() would have to be an association in order not to load the content properties of all 25 entities.


Partial Objects

If you want to partially load entity properties that are not associations, you have to tell this Doctrine explicitly:

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

This gives you a partially loaded entity object.

But beware that partial objects are not proxies! Lazy Loading doesn't apply to them. Therefore, using partial objects is generally dangerous and should be avoided. Read more: Partial Objects — Doctrine 2 ORM 2 documentation

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文