让我试着用例子来澄清什么是可能的,不可能的和计划的。
手册中的引用基本上意味着您可以拥有以下自定义实现类型:
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 个部分:
- 接口类型
- 实现类型(实现接口类型)
- 持久包装类型(实现接口类型)
这不仅可以编写与 Doctrine 2 ORM 无缝工作的自定义集合,还可以只编写自定义持久包装器类型,例如,根据特定的应用程序需求优化特定集合的延迟加载/初始化行为。
目前还不可能做到这一点,但会这样做。这是编写和使用完全自定义集合的唯一真正优雅且功能齐全的方式,这些集合完美地集成到原则 2 提供的透明持久性方案中。