使用 'choice_list' 将表单更新为 Symfony >= 2.8
Update Form with 'choice_list' to Symfony >= 2.8
我想将表单 class 更新到 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 选择列表:
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);
}
}
class ExampleChoiceList 生成我想要的选择列表,直到现在它都有效。但是属性 choice_list
不再被支持,我的问题是 "how do I get this converted without too much work?"。我读到我应该使用简单的 choice
但是我如何在 Symfony 2.8 中得到我想要的(数据库中的特定标签)。我希望有人能帮助我。
是的,'choice_list' 在 SYmfony 2.8 中已被弃用,但您可以改用 'choices',它也接受数组。来自 documentation:
The choices option is an array, where the array key is the item's
label and the array value is the item's value.
你一定要注意,在Symfony 3.0中,keys和values是颠倒的,而在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"。要将 "Last Name, First Name" 显示为标签,请使用 'choice_label' 选项。假设您的实体名为 'Person':
$builder->add('list', EntityType::class, array(
'class' => 'AppBundle:Person',
'choice_label' => function ($person) {
return $person->getLastName() . ', ' . $person->getFirstName();
}
));
使用 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,
));
}
// ...
}
我想将表单 class 更新到 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 选择列表:
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);
}
}
class ExampleChoiceList 生成我想要的选择列表,直到现在它都有效。但是属性 choice_list
不再被支持,我的问题是 "how do I get this converted without too much work?"。我读到我应该使用简单的 choice
但是我如何在 Symfony 2.8 中得到我想要的(数据库中的特定标签)。我希望有人能帮助我。
是的,'choice_list' 在 SYmfony 2.8 中已被弃用,但您可以改用 'choices',它也接受数组。来自 documentation:
The choices option is an array, where the array key is the item's label and the array value is the item's value.
你一定要注意,在Symfony 3.0中,keys和values是颠倒的,而在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"。要将 "Last Name, First Name" 显示为标签,请使用 'choice_label' 选项。假设您的实体名为 'Person':
$builder->add('list', EntityType::class, array(
'class' => 'AppBundle:Person',
'choice_label' => function ($person) {
return $person->getLastName() . ', ' . $person->getFirstName();
}
));
使用 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,
));
}
// ...
}