You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Symfony编译过程中自动加载Doctrine实体映射?

问题描述

我尝试基于目录结构自动加载实体映射,自定义加载器返回如下格式的数组,目标是在应用初始化时找到所有“capabilities”,并将其domain目录映射到Doctrine ORM实体映射。

[
  "UploadsDomain" => [
    "alias" => "SomeNameDomain",
    "dir" => "/home/someName/projects/video-service/src/Capability/SomeName/Domain",
    "is_bundle" => false,
    "prefix" => "App\Capability\SomeName\Domain",
    "type" => "attribute",
   ]
 ]

编译过程代码(已在Kernel.php中通过$container->addCompilerPass(new DoctrineMappingPass());调用):

namespace App\DependencyInjection\Compiler;

use App\Doctrine\CapabilityMappingLoader;
use Doctrine\ORM\Mapping\Driver\AttributeDriver;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;

class DoctrineMappingPass implements CompilerPassInterface
{
    public function process(ContainerBuilder $container): void
    {
        if (!$container->hasDefinition('doctrine.orm.default_configuration')) {
            return;
        }

        $definition = $container->getDefinition('doctrine.orm.default_configuration');

        // Instantiate the mapping loader
        $loader = new CapabilityMappingLoader();
        $mappings = $loader->loadMappings();

        foreach ($mappings as $alias => $mapping) {
            $definition->addMethodCall('addEntityNamespace', [$alias, $mapping['prefix']]);
            $definition->addMethodCall('setMetadataDriverImpl', [
                new AttributeDriver([$mapping['dir']]),
            ]);
        }
    }
}

出现错误:

Symfony\Component\DependencyInjection\Exception\RuntimeException: Unable to dump a service container if a parameter is an object or a resource, got "Doctrine\ORM\Mapping\Driver\AttributeDriver".

解决方案

错误核心是编译阶段直接实例化AttributeDriver对象,Symfony服务容器无法序列化对象类型参数,不能直接把实例传给addMethodCall。以下是两种可行修复方案:

方案一:用addDriver替代setMetadataDriverImpl

Doctrine的Configuration类提供addDriver方法,可添加多组驱动对应不同实体前缀,且需用**服务定义(Definition)**描述驱动创建逻辑,而非直接实例化:

修改process方法如下:

public function process(ContainerBuilder $container): void
{
    if (!$container->hasDefinition('doctrine.orm.default_configuration')) {
        return;
    }

    $definition = $container->getDefinition('doctrine.orm.default_configuration');
    $loader = new CapabilityMappingLoader();
    $mappings = $loader->loadMappings();

    foreach ($mappings as $alias => $mapping) {
        // 添加实体命名空间
        $definition->addMethodCall('addEntityNamespace', [$alias, $mapping['prefix']]);
        
        // 创建AttributeDriver的服务定义
        $driverDefinition = new \Symfony\Component\DependencyInjection\Definition(
            AttributeDriver::class,
            [$mapping['dir']]
        );
        
        // 调用addDriver关联驱动与实体前缀
        $definition->addMethodCall('addDriver', [$driverDefinition, $mapping['prefix']]);
    }
}

方案二:合并目录到现有AttributeDriver

如果应用原本就使用AttributeDriver,可获取现有驱动的目录列表,合并新目录后再设置:

public function process(ContainerBuilder $container): void
{
    if (!$container->hasDefinition('doctrine.orm.default_configuration')) {
        return;
    }

    $definition = $container->getDefinition('doctrine.orm.default_configuration');
    $loader = new CapabilityMappingLoader();
    $mappings = $loader->loadMappings();

    // 收集所有待添加的实体目录
    $allDirs = [];
    foreach ($mappings as $alias => $mapping) {
        $definition->addMethodCall('addEntityNamespace', [$alias, $mapping['prefix']]);
        $allDirs[] = $mapping['dir'];
    }

    // 查找现有驱动定义
    $driverCalls = array_filter(
        $definition->getMethodCalls(),
        fn($call) => $call[0] === 'setMetadataDriverImpl'
    );

    if (!empty($driverCalls)) {
        $existingDriver = reset($driverCalls)[1][0];
        // 确认现有驱动是AttributeDriver
        if ($existingDriver instanceof \Symfony\Component\DependencyInjection\Definition 
            && $existingDriver->getClass() === AttributeDriver::class) {
            // 合并新旧目录
            $existingDirs = $existingDriver->getArgument(0);
            $existingDriver->replaceArgument(0, array_merge($existingDirs, $allDirs));
        }
    } else {
        // 无现有驱动时,创建新的AttributeDriver定义
        $driverDefinition = new \Symfony\Component\DependencyInjection\Definition(
            AttributeDriver::class,
            [$allDirs]
        );
        $definition->addMethodCall('setMetadataDriverImpl', [$driverDefinition]);
    }
}

关键注意点

  • 编译阶段绝对不能直接实例化对象传给服务方法,必须用Definition描述对象创建逻辑,确保Symfony可序列化容器配置。
  • setMetadataDriverImpl会覆盖原有驱动,而addDriver支持多驱动多前缀,更适配多模块场景。

内容的提问来源于stack exchange,提问作者floriank

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.15 15:35:58