You need to enable JavaScript to run this app. Symfony 2.3升级2.8时调用DefaultChoiceListFactory未定义getChoices报错
优惠活动
大模型
产品
解决方案
定价
更多

Symfony 2.3升级2.8时调用DefaultChoiceListFactory未定义getChoices报错

Symfony 2.3 升级至2.8 ChoiceList适配问题

场景描述

  • 项目当前正从Symfony 2.3版本升级至2.8,提前适配后续升级3.0需要处理的废弃特性。
  • 参照官方2.7版本升级文档的指引,重写了Symfony\Component\Form\Extension\Core\ChoiceList\ChoiceList和Symfony\Component\Form\Extension\Core\ChoiceList\LazyChoiceList相关逻辑,开发过程中触发了如下报错。
  • 最初误以为是缺少getChoices()方法,于是在ChoiceLoader.php中补充了该方法,但报错依然存在,需要定位问题根因及解决方案。
  • 已知choice_list配置项后续会被废弃,已规划对这部分逻辑做整体重写。

报错信息

Attempted to call an undefined method named "getChoices" of class 
"Symfony\Component\Form\ChoiceList\Factory\DefaultChoiceListFactory"

原始代码片段

自定义ChoiceLoader实现(文件路径:ChoiceList/ChoiceLoader.php)

namespace Ahi\Sp\AdminBundle\Form\ChoiceList;
// 移除旧依赖
//use Symfony\Component\Form\Extension\Core\ChoiceList\ChoiceList;
//use Symfony\Component\Form\Extension\Core\ChoiceList\LazyChoiceList;
// 引入新组件依赖
use Symfony\Component\Form\ChoiceList\Factory\DefaultChoiceListFactory;
use Symfony\Component\Form\ChoiceList\Loader\ChoiceLoaderInterface;

class StaffChoiceLoader implements ChoiceLoaderInterface
{
    private $staffService;
    private $loginStaff;
    private $currentStaff;

    public function __construct($staffService, $loginStaff)
    {
        $this->staffService = $staffService;
        $this->loginStaff = $loginStaff;
    }

    public function loadChoiceList($value = null)
    {
        // 获取当前登录员工所属门店的所有在岗员工
        $staffs = $this->staffService->getStaffByShop($this->loginStaff->getShop());

        // 如果当前登录员工因调岗等原因不在获取到的员工列表中,就追加到列表末尾
        if ($this->currentStaff && !array_search($this->currentStaff, $staffs)) {
            $staffs[] = $this->currentStaff;
        }
        // 移除旧的实例化逻辑
        //return new ChoiceList($staffs, $staffs);
        // 原新逻辑(存在错误)
        return new DefaultChoiceListFactory($staffs, $staffs);
    }

    public function loadChoicesForValues(array $values, $value = null)
    {
        if (empty($choices)) {
            return array();
        }

        $values = array();
        foreach ($choices as $person) {
            $values[] = (string) $staff->getId();
        }

        return $values;
    }

    public function loadValuesForChoices(array $choices, $value= null)
    {
        if (empty($values)) {
            return array();
        }

        return $this->staffService->getStaffByShop($this->loginStaff->getShop());
    }

    // 原补充的空方法
    public function getChoices()
    {
    }
}

表单类型配置(文件路径:ArticleType.php)

// 移除旧实例化逻辑
//$authorChoiceList = new StaffChoiceLoader($this->staffService, $options['login_staff']);
// 原新逻辑(存在错误)
$factory = new DefaultChoiceListFactory();
$authorChoiceList = $factory->createListFromLoader(new StaffChoiceLoader($this->staffService, $options['login_staff']));

$builder->add("author", "entity", array(
    "required" => true,
    "class" => "AhiSpCommonBundle:Staff",
    "choice_list" => $authorChoiceList,
    "empty_value" => "Please select",
));

问题根因与修正方案

报错的核心原因是对新版本ChoiceList组件的API调用逻辑完全错位:

  • DefaultChoiceListFactory是用来生成ChoiceList实例的工厂类,本身没有选择项列表、也没有getChoices方法,不能被当作具体的ChoiceList实现类直接实例化传入列表参数。之前补在自定义Loader类上的getChoices()方法属于无效代码,和Factory类内部的调用逻辑没有关联,自然解决不了报错。
  • 多个接口实现方法存在变量名不匹配的低级错误,会在后续流程中触发更多未定义变量的报错。
  • 原代码中判断员工是否存在于列表的逻辑使用array_search有误:当当前员工是数组的第一个元素时,array_search会返回键值0,!0的判断结果为true,会重复追加同一个员工。

具体修正步骤

  1. 删掉自定义Loader类里空的getChoices()方法,该方法不属于ChoiceLoaderInterface的强制实现范围。
  2. 在loadChoiceList方法里,直接返回系统提供的ArrayChoiceList实例,不要手动实例化工厂类;给Loader类加一个缓存属性,避免每次调用都重新查询数据库加载员工列表。
  3. 修正两个接口实现方法的变量匹配问题,直接复用已加载的ChoiceList实例的匹配方法,不要重复写查询逻辑。
  4. 表单配置里不要手动实例化工厂生成choice_list,直接把自定义Loader传给choice_loader配置项即可,移除已经废弃的choice_list配置;把empty_value改成新组件规范的placeholder配置项,避免后续触发废弃告警。
  5. 把原列表判断逻辑改成in_array的严格匹配,避免重复追加员工。

修正后可直接使用的代码

修正后的StaffChoiceLoader

namespace Ahi\Sp\AdminBundle\Form\ChoiceList;

use Symfony\Component\Form\ChoiceList\Loader\ChoiceLoaderInterface;
use Symfony\Component\Form\ChoiceList\ArrayChoiceList;

class StaffChoiceLoader implements ChoiceLoaderInterface
{
    private $staffService;
    private $loginStaff;
    private $currentStaff;
    private $cachedChoiceList;

    public function __construct($staffService, $loginStaff)
    {
        $this->staffService = $staffService;
        $this->loginStaff = $loginStaff;
    }

    public function loadChoiceList($value = null)
    {
        // 命中缓存直接返回,避免重复查询
        if (null !== $this->cachedChoiceList) {
            return $this->cachedChoiceList;
        }

        $staffs = $this->staffService->getStaffByShop($this->loginStaff->getShop());

        // 严格判断是否存在,避免误判
        if ($this->currentStaff && !in_array($this->currentStaff, $staffs, true)) {
            $staffs[] = $this->currentStaff;
        }

        return $this->cachedChoiceList = new ArrayChoiceList($staffs);
    }

    public function loadChoicesForValues(array $values, $value = null)
    {
        if (empty($values)) {
            return [];
        }

        // 复用已加载列表的匹配逻辑
        return $this->loadChoiceList($value)->getChoicesForValues($values);
    }

    public function loadValuesForChoices(array $choices, $value = null)
    {
        if (empty($choices)) {
            return [];
        }

        // 复用已加载列表的匹配逻辑
        return $this->loadChoiceList($value)->getValuesForChoices($choices);
    }
}

修正后的ArticleType.php表单配置

$builder->add("author", "entity", array(
    "required" => true,
    "class" => "AhiSpCommonBundle:Staff",
    // 直接传入自定义Loader,无需手动生成choice_list
    "choice_loader" => new StaffChoiceLoader($this->staffService, $options['login_staff']),
    "placeholder" => "Please select",
));

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.11.01 15:06:03