让我尝试澄清什么是可能的、不可能的和计划中的,并且通过例子来说明。
手册中的引用基本上意味着您可以拥有以下自定义实现类型:
use Doctrine\Common\Collections\Collection;
class MyCollection implements Collection {
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(不是MANAGED、DETACHED或REMOVED),你就可以自由地使用集合的具体实现类型,即使它不太美观。如果它不是NEW,你必须仅访问接口类型(最好在其上进行类型提示)。这意味着实现类型并不真正重要。当从数据库中检索到持久化的MyEntity实例时,它将不使用MyCollection(构造函数永远不会被Doctrine调用,因为Doctrine只重新构建已经存在/持久化的对象,它不会创建“新”的对象)。并且由于这样的实体是MANAGED,访问必须通过接口类型进行。
现在来看一下计划中的内容。拥有自定义集合的更美观方式是还要拥有自定义的接口类型,例如IMyCollection和MyCollection作为实现类型。然后,为了使其与Doctrine 2持久性服务完美配合,您需要实现自定义的PersistentCollection实现,例如MyPersistentCollection,它看起来像这样:
class MyPersistentCollection implements IMyCollection {
}
在映射中告诉Doctrine使用MyPersistentCollection包装那个集合(记住,PersistentCollection一个集合实现类型,实现相同的接口,以便在委托给基础集合实现类型之前/之后可以执行所有持久化工作)。
因此,自定义集合实现由3部分组成:
- 接口类型
- 实现类型(实现接口类型)
- 持久性包装类型(实现接口类型)
这不仅使编写与Doctrine 2 ORM无缝配合的自定义集合成为可能,而且还可以仅编写自定义持久性包装类型,例如优化特定集合的惰性加载/初始化行为以满足特定应用程序需求。
目前还不能这样做,但将来会有。这是编写和使用完全自定义集合的唯一真正优雅和完全功能的方法,它可以完美地集成Doctrine 2提供的透明持久化方案。