3

是否可以在表单集合中动态生成选择?

情况:

  • 项目实体

  • 属性实体

这些实体可以通过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;

    /* ... */
}

模板非常简单,与这个问题无关,所以我在这个问题中省略了它们。

4

0 回答 0