electricmaxxx / doctrine-orm-odm-adapter
在ORM上使用ODM的映射
Requires
- php: ^5.3,>=5.3.3
- doctrine/common: ^2.4
- jackalope/jackalope-doctrine-dbal: ^1.0
Requires (Dev)
- doctrine/doctrine-bundle: ^1.2
- doctrine/orm: ^2.4
- doctrine/phpcr-bundle: ^1.1
- doctrine/phpcr-odm: dev-uuid-on-references as 1.1.0
- liip/rmt: dev-master
- phpcr/phpcr: ^2.1
- phpcr/phpcr-implementation: ^2.1
- phpcr/phpcr-utils: ^1.1
- phpunit/phpunit: ^4.1
- symfony/yaml: ~2.0
Suggests
- symfony/yaml: ^2.0
This package is auto-updated.
Last update: 2024-09-12 03:28:28 UTC
README
我在一个客户项目中意识到,有一些用例需要在实体(即ORM)上引用文档(即从phpcr-odm)。引用是通过文档的uuid完成的。因此,我首先实现了一个监听器,该监听器根据持久化的uuid处理实体的文档的加载、持久化和删除。
由于这种处理可能会越来越复杂,我看到了创建映射的需求。因此,这个库就诞生了。目前,我正在尝试映射以下字段/属性
- uuid - 实体上uuid的字段
- document - 实体上文档的字段
- common-field - 应在文档和实体上同步的字段,例如标题,以便在仅显示时不加载整个文档
当前状态
- ClassMetadata + 测试
- ClassMetadataFactory + 测试
- XmlDriver + 测试
- AnnotationDriver + 测试
- YmlDriver + 测试
- DocumentAdapter + 测试
- 用于处理繁重工作的UnitOfWork类型
- 实现生命周期事件
- 使用该库的Bundle (https://github.com/ElectricMaxxx/DoctrineOrmPhpcrAdapterBundle)
用法
配置
要创建一个ObjectAdapterManager
的新实例,您需要设置一些简单的配置。该类可以不设置这些配置而工作,但您将没有任何管理器来持久化您引用的对象
use Doctrine\Common\Annotations\AnnotationReader; use Doctrine\Common\Cache\ArrayCache; use Doctrine\Common\EventManager; use Doctrine\ODM\PHPCR\DocumentManager; use Doctrine\ORM\EntityManager; use Doctrine\ORM\ODMAdapter\Mapping\Driver\AnnotationDriver; use Doctrine\ORM\ODMAdapter\ObjectAdapterManager; use Doctrine\Common\EventManager; // caching and annotation read $cache = new ArrayCache(); $reader = new AnnotationReader($cache); // AnnotationDriver as example, Yaml and Xml available too $annotationDriver = new AnnotationDriver($reader); $annotationDriver->addPaths(array(__DIR__ . "/Models")); // configuration for the manager $configuration = new Configuration(); $configuration->setManagers( array( 'reference-phpcr' => array( 'default' => $documentManager, ), 'reference-dbal-orm' => array( 'defaulKt' => $entityManager, ), ) ); $configuration->setClassMetadataFactoryName('Doctrine\ORM\ODMAdapter\Mapping\ClassMetadataFactory'); $configuration->setMetadataDriverImpl($annotationDriver); // create the ObjectAdapterManager with the configuration and optional event manager $objectAdapterManager = ObjectAdapterManager::create($configuration, new EventManager()); // makes it possible to use the library's own event subscribers for the managers lifecycle events $objectAdapterManager->addListenersToEventManagers();
该配置看起来非常类似于常见的doctrine配置,这也是目的所在。还有两个额外的映射驱动程序可用。一个是Yaml,另一个是Xml。有一个区别:您必须设置您想要使用的管理器,用于对象的引用以及引用对象。管理器按映射类型(reference-phpcr和reference-dbal-orm)和名称排序。
此库的主要目的是,以下示例可以在不针对引用对象进行任何操作的情况下工作
$entity = new ReferencingEntity(); $document = new ReferencedDocument(); $document->name = 'document-name'; $document->parentDocument = // create some parent document $entity->document = $this->referencedObject; $entityManager->persist($entity); $entityManager->flush(); $entityManager->clear(); $document = $documentManager->find(null, '/some-path/document-name'); // or even better $entity = $entityManager->find('Entity', $entity->id); $document = $entity->document;
一般来说,本文档讨论的对象引用了引用对象,但示例试图用引用文档的实体(或相反)来解释这一点。但这个示例应该如何工作?我们可以为每个用例进行一些自定义事件挂钩,或者只是手动持久化引用对象。
映射
但此库的映射应该有助于这种情况。您可以用Xml进行
<?xml version="1.0" encoding="UTF-8"?>
<doctrine-mapping
xmlns="http://doctrine-project.org/schemas/orm-odm-adapter/adapter-mapping"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://doctrine-project.org/schemas/orm-odm-adapter/adapter-mapping
https://github.com/ElectricMaxxx/DoctrineOrmOdmAdapter/blob/master/doctrine-orm-odm-adapter-mapping.xsd ">
<object-adapter name="Entity">
<reference-phpcr
target-object="Document"
referenced-by="uuid"
inversed-by="uuid"
name="document"
manager="default">
</reference-phpcr>
</object-adapter>
</doctrine-mapping>
Yaml
Doctrine\Tests\ORM\ODMAdapter\Mapping\Driver\Model\ReferenceMappingObject:
referencedField:
type: reference-phpcr
referenced-by: uuid
inversed-by: uuid
target-object: Document
或注解
use Doctrine\ORM\ODMAdapter\Mapping\Annotations as ODMAdapter; /** * @ODMAdapter\ObjectAdapter */ class Entity { public $uuid; /** * @ODMAdapter\ReferencePhpcr( * referencedBy="uuid", * inversedBy="uuid", * targetObject="Document", * manager="default" * ) */ public $document; }
引用映射由一个项(xml节点、注解等)包装,该项描述了引用的类型,即引用对象所在的doctrine。目前有两种不同的类型可用
- reference-phpcr -> 创建一个使用phpcr-odm持久化的文档的引用
- reference-dbal-orm -> 创建一个使用doctrines orm持久化的实体的引用
这些类型的主体具有相同的属性
- referenced-by -> 引用对象的标识符(
$manager->find(null, $id))
应该工作) - inversed-by -> 存储引用对象(referenced-by)的字段值的字段
- target-object -> 引用对象的FQCN
- name -> 查找引用对象的字段
- 通用字段 -> 在两个对象之间保持同步的字段(请参阅自身描述)
如果您为配置设置的经理提供了自己的事件经理,那么这已经足够了。该库能够将自定的EventSubscriber挂钩到持有引用的对象的UnitOfWork触发的事件周期中。否则,您需要构建自己的事件系统,并在ObjectAdapterManager
上调用相应的方法。只需以库自带的为例
<?php namespace Doctrine\ORM\ODMAdapter\Event; use Doctrine\Common\Persistence\Event\ManagerEventArgs; use Doctrine\ORM\Event\PreFlushEventArgs; use Doctrine\ORM\Event\LifecycleEventArgs; use Doctrine\ORM\Event\OnClearEventArgs; class OrmLifecycleListener extends AbstractListener { public function prePersist(LifecycleEventArgs $event) { $object = $event->getEntity(); if ($this->isReferenceable($object)) { $this->objectAdapterManager->persistReference($object); } } public function postLoad(LifecycleEventArgs $event) { $object = $event->getObject(); if ($this->isReferenceable($object)) { $this->objectAdapterManager->findReference($object); } } public function preRemove(LifecycleEventArgs $event) { $object = $event->getObject(); if ($this->isReferenceable($object)) { $this->objectAdapterManager->removeReference($object); } } public function onClear(OnClearEventArgs $event) { $this->objectAdapterManager->clear(); } public function preFlush(PreFlushEventArgs $event) { $this->objectAdapterManager->flushReference(); } }
通用字段
这些字段是应该在两个对象上具有相同内容的字段。包括引用者和被引用者。
这里有一个简单的例子说明为什么你需要这个:想象一下,在你的后端有产品选择,比如。这个选择只显示产品的标题。由于你想将所有文本信息持久化为ProductDocument
,你决定为所有与ORM一起持久化的关系数据创建一个ProductEntity
,并让它通过类型reference-phpcr
引用文档。当然,产品的标题是你的文档中的一个属性。当显示这个选择时,需要标题,并且需要对列表中的每个产品进行一次查询/请求。如果商店只销售3个产品,这将是微不足道的性能影响,但如果有大量产品,这将是一个巨大的性能影响。当然,这个包为引用对象执行懒加载。这意味着在加载产品实体时,只会创建一个文档的代理。但是,当获取标题时,代理会唤醒,并进行查询/请求。
这就是通用字段的原因。您可以通过映射属性来在对象和引用对象之间保持同步。这意味着提供冗余数据,但您将获得一些性能提升。您可以在引用内部执行这种映射,因为每个属性都会进行一次引用映射(顺便说一句:您可以在一个类中执行对多个Doctrine的多个引用)
<reference-phpcr
target-object="Document"
referenced-by="uuid"
inversed-by="uuid"
name="document">
<common-field referenced-by="title" inversed-by="title" sync-type="from-reference"/>
</reference-phpcr>
或者用Yaml表示
Doctrine\Tests\ORM\ODMAdapter\Mapping\Driver\Model\ReferenceMappingObject:
referencedField:
type: reference-phpcr
referenced-by: uuid
inversed-by: uuid
target-object: Document
common-fields:
- {referenced-by: title, inversed-by: title, sync-type: from-reference}
或者用xml表示
/** * @ODMAdapter\ReferencePhpcr( * referencedBy="uuid", * inversedBy="uuid", * targetObject="Document", * commonFields={ * @ODMAdapter\CommonField(referencedBy="docName", inversedBy="entityName", syncType="from-reference") * } * ) */ public $referencedField;
您可以为每个引用执行多个通用字段映射。只需将保持同步的字段设置为以下属性的值
- referenced-by -> 被引用对象的字段
- inversed-by -> 引用对象的字段
- sync-type -> 同步值的方式,可能的值是
from-reference
(默认)和to-reference