如何为若干Doctrine实现提供Model类
当你打造一个bundle,但它并不只使用Doctrine ORM,而是同时使用CouchDB ODM、Mongodb ODM或是PHPCR ODM的时候,你应该始终只写一个model类。Doctrine bunlde提供了一个compiler pass用于注册你的多个model类的映射。
对于毋需复用的bundle来说,最简单的选项是把你的model类存放在默认的位置:对于Doctrine ORM是Entity
,而Doctrine ODM则是Document
。但对于可复用的bundle,不要去复制model类(到文件夹下),而是获取自动映射,办法就是compiler pass。
在你的bundle类中,编写以下代码来注册compiler pass。下例是对CmfRoutingBundle而写就,因此部分代码可以根据你的需求来调整:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 |
use Doctrine\Bundle\DoctrineBundle\DependencyInjection\Compiler\DoctrineOrmMappingsPass;
use Doctrine\Bundle\MongoDBBundle\DependencyInjection\Compiler\DoctrineMongoDBMappingsPass;
use Doctrine\Bundle\CouchDBBundle\DependencyInjection\Compiler\DoctrineCouchDBMappingsPass;
use Doctrine\Bundle\PHPCRBundle\DependencyInjection\Compiler\DoctrinePhpcrMappingsPass;
class CmfRoutingBundle extends Bundle
{
public function build(ContainerBuilder $container)
{
parent::build($container);
// ...
$modelDir = realpath(__DIR__.'/Resources/config/doctrine/model');
$mappings = array(
$modelDir => 'Symfony\Cmf\RoutingBundle\Model',
);
$ormCompilerClass = 'Doctrine\Bundle\DoctrineBundle\DependencyInjection\Compiler\DoctrineOrmMappingsPass';
if (class_exists($ormCompilerClass)) {
$container->addCompilerPass(
DoctrineOrmMappingsPass::createXmlMappingDriver(
$mappings,
array('cmf_routing.model_manager_name'),
'cmf_routing.backend_type_orm',
array('CmfRoutingBundle' => 'Symfony\Cmf\RoutingBundle\Model')
));
}
$mongoCompilerClass = 'Doctrine\Bundle\MongoDBBundle\DependencyInjection\Compiler\DoctrineMongoDBMappingsPass';
if (class_exists($mongoCompilerClass)) {
$container->addCompilerPass(
DoctrineMongoDBMappingsPass::createXmlMappingDriver(
$mappings,
array('cmf_routing.model_manager_name'),
'cmf_routing.backend_type_mongodb',
array('CmfRoutingBundle' => 'Symfony\Cmf\RoutingBundle\Model')
));
}
$couchCompilerClass = 'Doctrine\Bundle\CouchDBBundle\DependencyInjection\Compiler\DoctrineCouchDBMappingsPass';
if (class_exists($couchCompilerClass)) {
$container->addCompilerPass(
DoctrineCouchDBMappingsPass::createXmlMappingDriver(
$mappings,
array('cmf_routing.model_manager_name'),
'cmf_routing.backend_type_couchdb',
array('CmfRoutingBundle' => 'Symfony\Cmf\RoutingBundle\Model')
));
}
$phpcrCompilerClass = 'Doctrine\Bundle\PHPCRBundle\DependencyInjection\Compiler\DoctrinePhpcrMappingsPass';
if (class_exists($phpcrCompilerClass)) {
$container->addCompilerPass(
DoctrinePhpcrMappingsPass::createXmlMappingDriver(
$mappings,
array('cmf_routing.model_manager_name'),
'cmf_routing.backend_type_phpcr',
array('CmfRoutingBundle' => 'Symfony\Cmf\RoutingBundle\Model')
));
}
}
} |
注意class_exists
检查。这是十分关键的,因为你不希望自己的bundle对全部Doctrine bundles有某个强制依赖,而是让用户来选择使用哪个。
compiler pass(编译器传递)对于Doctrine所提供的全部驱动:Annotations、XML、Yaml、PHP以及StaticPHP都提供了工厂方法。该方法的参数如下:
目录绝对路径的一组map(映射)/hash,映射到namespace;
一个容器参数(container parameter)的数组,供你的bundle在指定“需要使用的Doctrine Manager”的名字时使用。在上例中,CmfRoutingBundle存储的将被使用的manager名字,是在
cmf_routing.model_manager_name
参数之下。compiler pass将附带这个Doctrine要用来“指定默认manager名字”的参数。找到的第一个参数将被使用,映射(mapping)将被注册到(那个名字所对应的)manager。一个可选的容器参数之名称,它会被compiler pass用到,来决定“是否此种类型的Doctrine将被使用”。如果你的用户安装了一种以上的Doctrine bundle,那么这个参数就有意义了,只不过你的bundle只会用到Doctrine的某一种类型;
一组假名的map/hash,映射到namespace。它在命名约定上应该与Doctrine auto-mapping(自动映射)的相同。在上例中,此参数允许用户调用
$om->getRepositary('CmfRoutingBundle:Route')
。
工厂方法使用的是Doctrine的SymfonyFileLocator
,意味着它只能找到XML和YAML映射文件,如果这些文件在名称里没有包含完整命名空间的话。这在设计上即是如此:SymfonyFileLocator
把事情简化的办法就是假定这些文件都是类的“快捷版本”,因为它们把类名作为文件名(如BlogPost.orm.xml
)。
如果你需要映射基类,你应该像下面这样,注册compiler pass时使用DefaultFileLocator
。这段代码来自DoctrineOrmMappingsPass
,并修正为使用DefaultFileLocator
来替换SymofnyFileLocator
:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
private function buildMappingCompilerPass()
{
$arguments = array(array(realpath(__DIR__ . '/Resources/config/doctrine-base')), '.orm.xml');
$locator = new Definition('Doctrine\Common\Persistence\Mapping\Driver\DefaultFileLocator', $arguments);
$driver = new Definition('Doctrine\ORM\Mapping\Driver\XmlDriver', array($locator));
return new DoctrineOrmMappingsPass(
$driver,
array('Full\Namespace'),
array('your_bundle.manager_name'),
'your_bundle.orm_enabled'
);
} |
注意毋须提供命名空间的假名,除非你的用户被预期为向Doctrine请求那个基类。
现在把你的映射文件放到/Resources/config/doctrine-base
下,并使用FQCN类名,以.
来分隔而不是\
,例如
Other.Namespace.Model.Name.orm.xml
。你不可以混用两种分隔符,否则SymofnyFileLocator
会混淆。
相对应地继续调整其他的Doctrine implementations即可。