是否可以在表单集合中动态生成选择?
情况:
项目实体
属性实体
这些实体可以通过Item Form添加和编辑。
当您向项目实体添加类型时,属性实体的状态将通过 ajax 请求加载。每种类型的项目都有由服务提供的不同状态选择。
该服务被注入到PropertyFormType 中以提供可用的选择。
一切正常,除了提交表单不断返回错误。似乎未加载选项。
在属性实体上,提供的状态选项为空。我知道该服务提供了正确的数据(一个包含一种项目的所有状态选择的数组)。
调试这告诉我,在POST_SUBMIT事件期间,未设置数据。
dump($event->getForm()->getData());显示Item Entity的 type 属性仍然为 null,即使它已在表单中设置。
是否可以从父 Form 对象读取提交的数据以确定已通过 ajax 加载哪些选项以修复 ConstraintViolation 错误?
Symfony 文档:
表格错误:
Caused by:
ConstraintViolation {#2314 ▶}
TransformationFailedException {#1587 ▼
#message: "Unable to reverse value for property path "status": The choice "test" does not exist or is not unique"
#code: 0
#file: "/home/vagrant/shop4raad2/vendor/symfony/form/Form.php"
#line: 1150
trace: {...}
…1
}
TransformationFailedException {#1598 ▼
#message: "The choice "test" does not exist or is not unique"
#code: 0
#file: "/home/vagrant/shop4raad2/vendor/symfony/form/Extension/Core/DataTransformer/ChoiceToValueTransformer.php"
#line: 48
trace: {...}
}
物品形式:
namespace App\Form;
use App\Entity\ItemEntity;
use App\Form\Type\PropertyFormType;
use App\Service\Provider\ItemTypeProvider;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\Extension\Core\Type\CollectionType;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
/**
* Item Form
*/
class ItemForm extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$item = $builder->getData();
$builder->add("type", ChoiceType::class, [
"choices" => ItemEntity::getTypeChoices(),
"disabled" => null !== $dataImportMapping->getId(),
"required" => null === $dataImportMapping->getId(),
/* ... */
]);
$builder->add("properties", CollectionType::class, [
"entry_type" => PropertyFormType::class,
"entry_options" => [PropertyFormType::OPTION_ITEM => $item],
"prototype" => true,
"allow_add" => true,
"allow_delete" => true,
/* ... */
]);
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => ItemEntity::class,
]);
}
}
物业表格类型:
namespace App\Form\Type;
use App\Entity\PropertyEntity;
use App\Service\Provider\PropertyStatusProvider;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Form\FormEvent;
use Symfony\Component\Form\FormEvents;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
/**
* Property Form Type
*/
class PropertyFormType extends AbstractType
{
const OPTION_ITEM = "data_item";
private $statusProvider;
public function __construct(PropertyStatusProvider $statusProvider)
{
$this->statusProvider = $statusProvider;
}
public function buildForm(FormBuilderInterface $builder, array $options)
{
$item = $options[self::OPTION_ITEM];
$formModifier = function(FormInterface $form, ItemEntity $item) {
// load choices from service - this service returns an array of available choices by
$statusChoices = $this->statusProvider->getAvailableChoices($item->getType());
$form->add("status", ChoiceType::class, [
"choices" => $statusChoices,
"required" => true,
/* ... */
]);
};
$builder->addEventListener(
FormEvents::PRE_SET_DATA,
function (FormEvent $event) use ($formModifier) {
/* @var PropertyItem $property */
$property = $event->getData();
$formModifier($event->getForm(), $property->getItem());
}
);
$builder->addEventListener(
FormEvents::POST_SUBMIT,
function (FormEvent $event) use ($formModifier) {
// It's important here to fetch $event->getForm()->getData(), as
// $event->getData() will get you the client data (that is, the ID)
/* @var PropertyItem $property */
$property = $event->getForm()->getData();
// since we've added the listener to the child, we'll have to pass on
// the parent to the callback functions
$formModifier($event->getForm()->getParent(), $property->getItem());
}
);
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => PropertyEntity::class,
]);
}
}
选择通过 ajax 使用 select2 库动态加载到属性选择中
项目-form.js:
$("select[id^='item_form_properties_'][id$='_status']").select2({
ajax: {
type: "GET",
url: Routing.generate("async-item-properties"),
dataType: "json",
data: function(params) {
var query = {
term: params.term,
};
var mapping_type = $("select#item_form_type").val();
if (null !== mapping_type && "" !== mapping_type) {
query['type'] = mapping_type;
}
return query;
},
processResults: function(data) {
var properties = [];
$.each(data, function(key, item) {
properties.push({
id: item.id,
text: item.value,
});
});
return {
results: properties
};
},
},
/* ... */
});
物品实体:
/**
* @ORM\Entity(/* ... */)
* @ORM\Tabele(/* ... */)
*/
class ItemEntity
{
/**
* @ORM\Id
* @ORM\Column(name = "item_id", type = "integer")
*/
private $id;
/**
* @ORM\Column(type = "string", length = 64)
*/
private $type;
/**
* @ORM\OneToMany(targetEntity = "PropertyEntity", mappedBy = "item", cascade={"persist", "remove"})
*/
private $properties;
/* ... */
/**
* @param PropertyEntity $property
*
* @return self
*/
public function addProperty(PropertyEntity $property)
{
$property->setItem($this);
$this->properties[] = $property;
return $this;
}
/**
* @param PropertyEntity $property
*/
public function removeProperty(PropertyEntity $property)
{
$this->properties->removeElement($property);
}
}
财产实体:
/**
* @ORM\Entity(/* ... */)
* @ORM\Tabele(/* ... */)
*/
class PropertyEntity
{
/**
* @ORM\Id
* @ORM\Column(name = "property_id", type = "integer")
*/
private $id;
/**
* @ORM\ManyToOne(targetEntity = "ItemEntity", inversedBy = "properties")
* @ORM\JoinColumn(name = "item_id", referencedColumnName = "item_id")
*/
private $item;
/**
* @ORM\Column(type = "string", length = 64)
*/
private $status;
/* ... */
}
模板非常简单,与这个问题无关,所以我在这个问题中省略了它们。