【问题标题】:Update Form with 'choice_list' to Symfony >= 2.8将带有 'choice_list' 的表单更新为 Symfony >= 2.8
【发布时间】:2016-05-24 12:19:35
【问题描述】:

我想将一个表单类更新为 Symfony2.8(以及后来的 Symfony3)。现在,除了一个不再支持的属性 choice_list 之外,表单已被转换。而且我不知道该怎么做。

我有以下也被定义为服务的表单类型:

class ExampleType extends AbstractType
{

    /** @var Delegate */
    private $delegate;

    public function __construct(Delegate $delegate)
    {
        $this->delegate = $delegate;
    }

    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder->add('list', ChoiceType::class, array(
            'choice_list' => new ExampleChoiceList($this->delegate),
            'required'=>false)
        );
    }

    public function configureOptions(OptionsResolver $resolver)
    {
            $resolver->setDefaults(array(
                'data_class' => 'ExampleClass',
            ));
    }
}

我有以下选择列表的类:

class ExampleChoiceList extends LazyChoiceList
{

    /** @var Delegate  */
    private $delegate;

    public function __construct(Delegate $delegate)
    {
        $this->delegate = $delegate;
    }


    /**
     * Loads the choice list
     * Should be implemented by child classes.
     *
     * @return ChoiceListInterface The loaded choice list
     */
    protected function loadChoiceList()
    {
        $persons = $this->delegate->getAllPersonsFromDatabase();
        $personsList = array();
        foreach ($persons as $person) {
            $id = $person->getId();
            $personsList[$id] = (string) $person->getLastname().', '.$person->getFirstname();
        }
        return new ArrayChoiceList($personsList);
    }


}

ExampleChoiceList 类生成我想要的选择列表,直到现在它仍然有效。但是属性choice_list 不再受支持,我的问题是“如何在不做太多工作的情况下进行转换?”。我读到我应该使用简单的choice,但是我如何在 Symfony 2.8 中得到我想要的(来自数据库的特定标签)。我希望有人可以帮助我。

【问题讨论】:

    标签: php symfony symfony-forms


    【解决方案1】:

    通过使用ChoiceListInterface,您就快到了。

    我建议你将ExampleChoiceList改为实现Symfony\Component\Form\ChoiceList\Loader\ChoiceLoaderInterface,它需要你实现3个方法:

    <?php
    // src/AppBundle/Form/ChoiceList/Loader/ExampleChoiceLoader.php
    
    namespace AppBundle\Form\ChoiceList\Loader;
    
    use Acme\SomeBundle\Delegate;
    use Symfony\Component\Form\ArrayChoiceList;
    use Symfony\Component\Form\Loader\ChoiceLoaderInterface;
    
    class ExampleChoiceLoader implements ChoiceLoaderInterface
    {
        /** $var ArrayChoiceList */
        private $choiceList;
    
        /** @var Delegate  */
        private $delegate;
    
        public function __construct(Delegate $delegate)
        {
            $this->delegate = $delegate;
        }
    
        /**
         * Loads the choice list
         * 
         * $value is a callable set by "choice_name" option
         *
         * @return ArrayChoiceList The loaded choice list
         */
        public function loadChoiceList($value = null)
        {
            if (null !== $this->choiceList) {
                return $this->choiceList;
            }
    
            $persons = $this->delegate->getAllPersonsFromDatabase();
            $personsList = array();
            foreach ($persons as $person) {
                $label = (string) $person->getLastname().', '.$person->getFirstname();
                $personsList[$label] = (string) $person->getId();
                // So $label will be displayed and the id will be used as data
                // "value" will be ids as strings and used for post
                // this is just a suggestion though
            }
    
            return $this->choiceList = new ArrayChoiceList($personsList);
        }
    
        /**
         * {@inheritdoc}
         *
         * $choices are entities or the underlying data you use in the field
         */
        public function loadValuesForChoices(array $choices, $value = null)
        {
            // optimize when no data is preset
            if (empty($choices)) {
                return array();
            }
    
            $values = array();
            foreach ($choices as $person) {
                $values[] = (string) $person->getId();
            }
    
            return $values;
        }
    
        /**
         * {@inheritdoc}
         * 
         * $values are the submitted string ids
         *
         */
        public function loadChoicesForValues(array $values, $value)
        {
            // optimize when nothing is submitted
            if (empty($values)) {
                return array();
            }
    
            // get the entities from ids and return whatever data you need.
            // e.g return $this->delegate->getPersonsByIds($values);
        }
    }
    

    将加载器和类型都注册为服务,以便注入:

    # app/config/services.yml
    
    services:
        # ...
        app.delegate:
            class: Acme\SomeBundle\Delegate
    
        app.form.choice_loader.example:
            class: AppBundle\Form\ChoiceList\Loader\ExampleChoiceLoader
            arguments: ["@app.delegate"]
    
        app.form.type.example:
            class: AppBundle\Form\Type\ExampleType
            arguments: ["@app.form.choice_loader.example"]
    

    然后更改表单类型以使用加载器:

    <?php
    // src/AppBundle/Form/Type/ExampleType.php
    
    namespace AppBundle\Form\Type;
    
    use AppBundle\Form\ChoiceList\Loader\ExampleChoiceLoader;
    use Symfony\Component\Form\AbstractType;
    use Symfony\Component\Form\FormBuilderInterface;
    
    class ExampleType extends AbstractType
    {
        /** @var ExampleChoiceLoader */
        private $loader;
    
        public function __construct(ExampleChoiceLoader $loader)
        {
            $this->loader = $loader;
        }
    
        public function buildForm(FormBuilderInterface $builder, array $options = array())
        {
            $builder->add('list', ChoiceType::class, array(
                'choice_loader' => $this->loader,
                'required' => false,
            ));
        }
    
        // ...
    
    }
    

    【讨论】:

    • 感谢您的帮助。我不会为每种类型生成服务,我现在使用'choice_loader' =&gt; new ExampleChoiceLoader($this-&gt;delegate),。并且我为所有将要扩展的案例编写了一个类,因此我只需要实现loadChoiceList 方法。如果我有 10 个来自不同类型的选择列表(并且我想为所有人使用服务),那么我还必须为每个加载器实现并将它们全部放在构造函数中吗?因为在这种情况下,仅对于选择列表来说似乎有点过大。
    • 当你声明一个表单类型为服务时,你可以像往常一样简单地使用FQCN,注入由DependencyInjectionExtension处理。所以你不需要传递表单类型的构造实例。
    • 这样它会在所有类型中只使用一个加载器实例。也许你可以创建一个抽象类型,所有需要加载器的类型都可以扩展。
    • 是的,我已经这样做了。我写了一个抽象类,它实现了ChoiceLoaderInterface 并实现了loadValuesForChoicesloadChoicesForValues 方法,因此只返回id 而不是对象。在我的选择列表中,我将implements LazyChoiceList 更改为extends MyNewChoiceListHelper 并只编写loadChoiceList 方法。在这里,我只需要更改一些行(主要是更改数组标签和值)。所以我可以在没有太大变化的情况下转换它。
    【解决方案2】:

    是的,'choice_list' 在 SYmfony 2.8 中已被弃用,但你可以使用 'choices' 代替,它也接受一个数组。来自documentation

    choices 选项是一个数组,其中数组键是项目的 标签和数组值是项目的值。

    您必须注意,与 Symfony 3.0 相比,键和值是倒置的,而在 Symfony 2.8 中,推荐的方法是使用新的倒序,并指定 'choices_as_values' => true。

    所以,在表单类型中:

    $builder->add('list', ChoiceType::class, array(
                   'choices' => new ExampleChoiceList($this->delegate),
                   'choices_as_values' => true,
                   'required'=>false));
    

    在 ExampleChoiceList 中:

    protected function loadChoiceList()
        {
            $persons = $this->delegate->getAllPersonsFromDatabase();
            $personsList = array();
            foreach ($persons as $person) {
                $id = $person->getId();
                $personsList[(string) $person->getLastname().', '.$person->getFirstname()] = $id; // <== here
            }
            return new ArrayChoiceList($personsList);
        }
    

    更新:

    好的,所以我建议您根本不要使用 ChoiceType,而是使用 EntityType,因为您似乎从数据库中获取所有“Persons”。要将“姓氏,名字”显示为标签,请使用“choice_label”选项。假设您的实体被称为“人”:

    $builder->add('list', EntityType::class, array(
        'class' => 'AppBundle:Person',
        'choice_label' => function ($person) {
            return $person->getLastName() . ', ' . $person->getFirstName();
        }
    ));  
    

    【讨论】:

    • 抱歉,您的解决方案不起作用。现在我收到以下错误:The option "choices" with value ExampleChoiceList is expected to be of type "null" or "array" or "\Traversable", but is of type "ExampleChoiceList". 500 Internal Server Error - InvalidOptionsException
    • 抱歉,没有查看 ExampleChoiceList 的详细信息。能不能把LazyChoiceList的相关代码放上来,看看扩展了什么?
    • 它来自 Symfony:namespace Symfony\Component\Form\ChoiceList。我用它来获取choice_list 的数据,但现在似乎出现了问题。 LazyChoiceList 实现 ChoiceListInterface
    • 好的,所以我建议一些我认为更简单的方法:使用 EntityType 而不是 ChoiseType。请查看我的更新。
    • 如果我使用 Doctrine 进行数据库查询,那将是一个解决方案,但我不使用 Doctrine,我使用 Propel。所以在我的情况下,你的解决方案不起作用。不过谢谢你的帮助。
    猜你喜欢
    • 2018-09-29
    • 2021-11-17
    • 1970-01-01
    • 2023-04-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多