教义中的自定义集合2

2022-08-30 22:15:42

刚刚开始使用 Doctrine2,我想知道如何/是否可以使用自定义集合类。搜索将我指向文档的这一部分

集合值的持久性字段和属性必须根据接口进行定义。应用程序可以使用集合实现类型在实体成为持久性之前初始化字段或属性。实体变为托管(或分离)后,后续访问必须通过接口类型进行。Doctrine\Common\Collections\Collection

虽然我确信这对某人来说非常清楚,但我对此有点模糊。

如果我设置我的实体将集合变量初始化(例如)到实现正确接口的类 - Doctrine2 会继续使用该类作为集合吗?我是否正确理解了这一点?__construct()

更新:此外,我从各种线程中收集到,延迟加载中使用的占位符对象可能会影响自定义集合的使用方式。


答案 1

让我试着用例子来澄清什么是可能的,不可能的和计划的。

手册中的引用基本上意味着您可以拥有以下自定义实现类型:

use Doctrine\Common\Collections\Collection;

// MyCollection is the "implementation type"
class MyCollection implements Collection {
    // ... interface implementation

    // This is not on the Collection interface
    public function myCustomMethod() { ... }
}

现在,您可以按如下方式使用它:

class MyEntity {
    private $items;
    public function __construct() {
        $this->items = new MyCollection;
    }
    // ... accessors/mutators ...
}

$e = new MyEntity;
$e->getItems()->add(new Item);
$e->getItems()->add(new Item);
$e->getItems()->myCustomMethod(); // calling method on implementation type

// $em instanceof EntityManager
$em->persist($e);

// from now on $e->getItems() may only be used through the interface type

换句话说,只要实体是新的(不是托管的,分离的或已删除的),你就可以自由使用集合的具体实现类型,即使它不漂亮。如果它不是 NEW,则必须仅访问接口类型(理想情况下,必须访问其上的类型提示)。这意味着实现类型并不重要。当从数据库中检索持久性 MyEntity 实例时,它不会使用 MyCollection(Doctrine 永远不会调用构造函数,因为 Doctrine 只会重构已经存在/持久的对象,它永远不会创建“新”对象)。由于此类实体是托管的,因此无论如何都必须通过接口类型进行访问。

现在来看看计划。拥有自定义集合的更美观的方法是也具有自定义接口类型,例如IMyCollection和MyCollection作为实现类型。然后,要使其与 Doctrine 2 持久性服务完美配合,您需要实现自定义的 PersistentCollection 实现,例如 MyPersistentCollection,如下所示:

class MyPersistentCollection implements IMyCollection {
    // ...
}

然后,您将告诉映射中的 Doctrine 对该集合使用 MyPersistentCollection 包装器(请记住,PersistentCollection 包装了集合实现类型,实现了相同的接口,以便它可以在委派给基础集合实现类型之前/之后完成所有持久性工作)。

因此,自定义集合实现将包含 3 个部分:

  1. 接口类型
  2. 实现类型(实现接口类型)
  3. 持久包装类型(实现接口类型)

这不仅可以编写与 Doctrine 2 ORM 无缝工作的自定义集合,还可以只编写自定义持久包装器类型,例如,根据特定的应用程序需求优化特定集合的延迟加载/初始化行为。

目前还不可能做到这一点,但会这样做。这是编写和使用完全自定义集合的唯一真正优雅且功能齐全的方式,这些集合完美地集成到原则 2 提供的透明持久性方案中。


答案 2

不,每当 Doctrine 返回 Doctrine\Common\Collections\Collection 接口的实现时,它将是 Doctrine\ORM\PersistentCollection 实例。不能在集合上放置更多自定义逻辑。然而,这甚至没有必要。

假设您有一个实体(订单有许多 OrderItems),则计算订单总和的方法不应位于集合上,而应位于“订单”项上。由于这是总和在域模型中具有意义的位置:

class Order
{
    private $items;

    public function getTotalSum()
    {
        $total = 0;
        foreach ($this->items AS $item) {
            $total += $item->getSum();
        }
        return $total;
    }
}

然而,集合只是ORM的技术部分,它们有助于实现和管理对象之间的引用,仅此而已。


推荐