php - Doctrine ORM 2.9 同时使用 AnnotationDriver 和 AttributeDriver 来解析实体元数据
问题描述
最近我们将应用程序升级到 PHP8。
由于PHP8
引入了属性并doctrine/orm
在版本中支持它们,因此利用此功能以增量方式(即不是一次所有实体)将实体元数据更新为属性格式2.9
似乎是一个好主意。
为此,我需要以某种方式注册两者Doctrine\ORM\Mapping\Driver\AnnotationDriver
并Doctrine\ORM\Mapping\Driver\AttributeDriver
解析元数据。
棘手的部分是为一组使用注释或属性装饰的实体注册两个解析器。从这点Doctrine\ORM\Configuration
看来,我所需要的似乎是不可能的。
我是正确的(假设这不能合理地实现)还是可以以某种不太骇人听闻的方式完成?
解决方案
教义本身并不提供这种可能性。但是我们可以实现一个自定义映射驱动程序来实现这一点。
实际的实现可能如下所示:
<?php
namespace Utils\Doctrine;
use Doctrine\ORM\Mapping\Driver\AnnotationDriver;
use Doctrine\ORM\Mapping\Driver\AttributeDriver;
use Doctrine\ORM\Mapping\MappingException;
use Doctrine\Persistence\Mapping\ClassMetadata;
use Doctrine\Persistence\Mapping\Driver\AnnotationDriver as AbstractAnnotationDriver;
class HybridMappingDriver extends AbstractAnnotationDriver
{
public function __construct(
private AnnotationDriver $annotationDriver,
private AttributeDriver $attributeDriver,
) {
}
public function loadMetadataForClass($className, ClassMetadata $metadata): void
{
try {
$this->attributeDriver->loadMetadataForClass($className, $metadata);
return;
} catch (MappingException $me) {
// Class X is not a valid entity, so try the other driver
if (!preg_match('/^Class(.)*$/', $me->getMessage())) {// meh
throw $me;
}
}
$this->annotationDriver->loadMetadataForClass($className, $metadata);
}
public function isTransient($className): bool
{
return $this->attributeDriver->isTransient($className)
|| $this->annotationDriver->isTransient($className);
}
}
简而言之:
- 驱动程序首先尝试使用
AttributeDriver
,然后回退到,AnnotationDriver
以防被检查的类未被评估为有效实体 Doctrine\Persistence\Mapping\Driver\MappingDriver
为了在扩展类后符合接口,Doctrine\Persistence\Mapping\Driver\AnnotationDriver
只需实现 2 个方法- 从示例实现中可以看出,两种方法都涉及元数据映射驱动程序
- 通过解析消息来区分各种
MappingException
s,一点都不优雅,但是没有更好的属性来区分;每个映射错误案例具有不同的异常子类型或一些唯一代码将有助于区分映射错误的各个原因
HybridMappingDriver
可以像这样连接EntityManagerFactory
:
<?php
namespace App\Services\Doctrine;
use Doctrine\ORM\Tools\Setup;
use Doctrine\ORM\EntityManager;
use Doctrine\Common\Annotations\AnnotationRegistry;
use Doctrine\Common\Proxy\AbstractProxyFactory as APF;
use Doctrine\Persistence\Mapping\Driver\MappingDriver;
use Utils\Doctrine\NullCache;
class EntityManagerFactory
{
public static function create(
array $params,
MappingDriver $mappingDriver,
bool $devMode,
): EntityManager {
AnnotationRegistry::registerLoader('class_exists');
$config = Setup::createConfiguration(
$devMode,
$params['proxy_dir'],
new NullCache(), // must be an instance of Doctrine\Common\Cache\Cache
);
$config->setMetadataDriverImpl($mappingDriver); // <= this is the actual hook-up
if (!$devMode) {
$config->setAutoGenerateProxyClasses(APF::AUTOGENERATE_FILE_NOT_EXISTS);
}
return EntityManager::create($params['database'], $config);
}
}
推荐阅读
- postgresql - 锁定行时防止竞争条件
- javascript - 数据排序方法在javascript中不起作用
- android-studio - Android Studio main_activity.xml
- c++ - 构建 MongoDB 时出现 boost::date_time 错误:winapi 不是成员
- jvm - Firrtl 用大输入耗尽堆内存
- c - 为什么我的程序不能正确接受另一个程序的管道输出?
- php - 如何正确解析递归括号?
- c# - 安装程序语言资源未生成 C#
- mercurial - 'hg add' 会忽略 '.hgignore' 吗?
- python - 使用 csv 文件创建嵌套字典