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

Symfony7.3+Doctrine3.5多实体管理器下自定义函数编译失败

Symfony多实体管理器环境下自定义Doctrine DQL函数被误判为未使用服务移除

我自定义了BizDaysInclusive MySQL函数用于计算两个日期之间的工作日天数,按照官方文档在config/packages/doctrine.yaml中与多实体管理器一同声明。首次使用正常,但Symfony编译系统缓存后出现错误提示:

Attempted to load class 'BizDaysInclusive' from namespace 'App\Query\MySql'. Did you forget a 'use' statement for 'App\Query\Mysql\BizDaysInclusive'?

该提示并不准确,首次使用时类引用有效。问题仅在开发环境缓存编译后出现,生产环境始终正常;移除额外实体管理器声明后,自定义函数可正常工作。通过缓存检查发现,Symfony容器将App\Query\Mysql\BizDaysInclusive标记为未使用服务并移除。


测试案例

配置文件 (config/packages/doctrine.yaml)

orm:
  default_entity_manager: default
  entity_managers:
    default:
      connection: default
      dql:
        string_functions:
          REPLACE: DoctrineExtensions\Query\Mysql\Replace
          MONTH: DoctrineExtensions\Query\Mysql\Month
          YEAR: DoctrineExtensions\Query\Mysql\Year
          NOW: DoctrineExtensions\Query\Mysql\Now
          DATE: DoctrineExtensions\Query\Mysql\Date
          DAY: DoctrineExtensions\Query\Mysql\Day
          WEEKOFYEAR: DoctrineExtensions\Query\Mysql\WeekOfYear
          GROUP_CONCAT: DoctrineExtensions\Query\Mysql\GroupConcat
          CAST: DoctrineExtensions\Query\Mysql\Cast
          FLOOR: DoctrineExtensions\Query\Mysql\Floor
          FIRST: App\Doctrine\FirstFunction
          IF: DoctrineExtensions\Query\Mysql\IfElse
          CONCAT: Doctrine\ORM\Query\AST\Functions\ConcatFunction
          ROUND: DoctrineExtensions\Query\Mysql\Round
          JSON_EXTRACT: Scienta\DoctrineJsonFunctions\Query\AST\Functions\Mysql\JsonExtract
          JSON_SEARCH: Scienta\DoctrineJsonFunctions\Query\AST\Functions\Mysql\JsonSearch
          JSON_UNQUOTE: Scienta\DoctrineJsonFunctions\Query\AST\Functions\Mysql\JsonUnquote
        numeric_functions:
          BIZDAYSINCLUSIVE: App\Query\MySql\BizDaysInclusive
          FERIADOSENTREFECHAS: App\Query\MySql\FeriadosEntreFechas
          ANIOS_MESES: App\Query\MySql\AniosMeses
      mappings:
        Main:
          type: attribute
          is_bundle: false
          dir: '%kernel.project_dir%/src/Entity/Main'
          prefix: App\Entity\Main
          alias: Main
    meta4:
      connection: meta4
      mappings:
        Meta4:
          type: attribute
          is_bundle: false
          dir: '%kernel.project_dir%/src/Entity/Meta4'
          prefix: App\Entity\Meta4
          alias: Meta4
  controller_resolver:
    auto_mapping: false

自定义函数类 (src/Query/Mysql/BizDaysInclusive.php)

<?php

namespace App\Query\Mysql;

use Doctrine\ORM\Query\AST\Functions\FunctionNode;
use Doctrine\ORM\Query\Parser;
use Doctrine\ORM\Query\SqlWalker;
use Doctrine\ORM\Query\TokenType;

class BizDaysInclusive extends FunctionNode
{
    public $firstDateExpression = null;
    public $secondDateExpression = null;

    public function parse(Parser $parser): void
    {
        $parser->match(TokenType::T_IDENTIFIER);
        $parser->match(TokenType::T_OPEN_PARENTHESIS);
        $this->firstDateExpression = $parser->ArithmeticPrimary();
        $parser->match(TokenType::T_COMMA);
        $this->secondDateExpression = $parser->ArithmeticPrimary();
        $parser->match(TokenType::T_CLOSE_PARENTHESIS);
    }

    public function getSql(SqlWalker $sqlWalker): string
    {
        return 'BizDaysInclusive(' .
            $this->firstDateExpression->dispatch($sqlWalker) . ', ' .
            $this->secondDateExpression->dispatch($sqlWalker) .
        ')';
    }
}

缓存检查日志

在var/目录执行grep命令后,得到Symfony编译日志:

Symfony\Component\DependencyInjection\Compiler\RemoveUnusedDefinitionsPass:
Removed service "App\Query\Mysql\BizDaysInclusive"; reason: unused


解决方案

原因

Symfony的RemoveUnusedDefinitionsPass编译流程会自动移除未被容器识别为有依赖关系的服务。在多实体管理器场景下,Doctrine的DQL函数注册逻辑未被Symfony容器正确识别为对自定义函数类的依赖,导致开发环境缓存编译时被误判为“未使用”而移除。开发环境的debug模式会更激进地执行这个优化,而生产环境缓存策略不同,因此不会出现该问题。

解决方法

1. 用服务标签显式关联Doctrine实体管理器(推荐)

在config/services.yaml中为自定义函数添加Doctrine DQL函数标签,让容器明确识别其依赖关系:

services:
  App\Query\Mysql\BizDaysInclusive:
    tags:
      - { name: doctrine.dql_function, entity_manager: default, type: numeric, alias: BIZDAYSINCLUSIVE }
  App\Query\Mysql\FeriadosEntreFechas:
    tags:
      - { name: doctrine.dql_function, entity_manager: default, type: numeric, alias: FERIADOSENTREFECHAS }
  App\Query\Mysql\AniosMeses:
    tags:
      - { name: doctrine.dql_function, entity_manager: default, type: numeric, alias: ANIOS_MESES }

添加标签后,可以移除doctrine.yaml中dql.numeric_functions下对应的手动配置,由标签自动注册。

2. 标记服务为公共服务

如果不想用标签,也可以直接在config/services.yaml中将自定义函数类标记为公共服务,避免被移除:

services:
  App\Query\Mysql\BizDaysInclusive:
    public: true
  App\Query\Mysql\FeriadosEntreFechas:
    public: true
  App\Query\Mysql\AniosMeses:
    public: true

3. 关闭开发环境的未用服务移除(不推荐)

在config/packages/dev/framework.yaml中关闭未用服务移除功能,会影响整体缓存优化,仅作为临时方案:

framework:
  dependency_injection:
    unused_definitions: false

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.11 19:34:54