最近我们将应用程序升级到PHP8。
由于PHP8
引入了属性,而doctrine/orm
从版本2.9
开始就支持这些属性,因此利用此功能来逐步(即而不是所有实体同时更新(将实体元数据更新为属性格式似乎是个好主意。
为了做到这一点,我需要以某种方式注册DoctrineORMMappingDriverAnnotationDriver
和DoctrineORMMappingDriverAttributeDriver
来解析元数据。
棘手的部分是为一组使用注释或属性装饰的实体注册两个解析器。从DoctrineORMConfiguration
的角度来看,我所需要的似乎是不可能的。
我是正确的吗(假设这不能合理地实现(,还是可以用一些不太粗鲁的方式来实现?
条令本身并没有提供这种可能性。但是我们可以实现一个自定义映射驱动程序来实现这一点。
实际的实现可能是这样的:
<?php
namespace UtilsDoctrine;
use DoctrineORMMappingDriverAnnotationDriver;
use DoctrineORMMappingDriverAttributeDriver;
use DoctrineORMMappingMappingException;
use DoctrinePersistenceMappingClassMetadata;
use DoctrinePersistenceMappingDriverAnnotationDriver 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
,以防被检查的类未被评估为有效实体 - 在扩展
DoctrinePersistenceMappingDriverAnnotationDriver
类之后,为了符合DoctrinePersistenceMappingDriverMappingDriver
接口,只需要实现2种方法 - 从示例实现中可以看出,这两种方法都考虑了元数据映射驱动程序
- 通过解析消息来区分各种类型的
MappingException
s一点也不优雅,但没有更好的属性可以区分;每个映射错误案例都有不同的异常子类型或一些唯一的代码,这将有助于区分映射错误的各个原因
HybridMappingDriver
可以连接到EntityManagerFactory
中,如下所示:
<?php
namespace AppServicesDoctrine;
use DoctrineORMToolsSetup;
use DoctrineORMEntityManager;
use DoctrineCommonAnnotationsAnnotationRegistry;
use DoctrineCommonProxyAbstractProxyFactory as APF;
use DoctrinePersistenceMappingDriverMappingDriver;
use UtilsDoctrineNullCache;
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 DoctrineCommonCacheCache
);
$config->setMetadataDriverImpl($mappingDriver); // <= this is the actual hook-up
if (!$devMode) {
$config->setAutoGenerateProxyClasses(APF::AUTOGENERATE_FILE_NOT_EXISTS);
}
return EntityManager::create($params['database'], $config);
}
}
我不确定是否可以做到,但你可以看看Rector,一次自动升级所有实体。似乎已经对此进行了配置。
https://github.com/rectorphp/rector
https://github.com/rectorphp/rector-doctrine/blob/4bbeb676e9ec8c146a81617f6362be4cafbdf3b3/config/sets/doctrine-orm-29.php