具有 OneToOne 关系的动态嵌入表单

Dynamic embedded form with OneToOne relationship

我正在尝试为带有插件(招标)的文章创建表单。

文章与插件是一对一的关系,插件可以被禁用(null)。

如果带有插件的复选框被选中,则需要验证插件,然后与文章一起提交。

我遇到的问题是,我无法在提交前将插件设置为 null,是什么导致了错误 - 表单仍然尝试提交与文章相关的插件,而它的值可以'不能为空。

如何在复选框未选中时禁用插件并仍然将文章提交到数据库,但没有插件?

我曾尝试使用表单事件来修改事件的数据,但事实证明在 PRE_SUBMIT 期间 tender 不是数据的一部分。 如果我尝试在 SUBMIT 期间修改数据,那么我必须更改实体方法中的逻辑,但感觉不对,还会导致一些我不记得且无法解决的问题。 我也尝试过使用 'empty_data' => null,认为它会完全按照我的要求行事,但事实证明它对我的表格根本没有影响。

文章实体:

<?php

namespace App\Entity;

use App\Repository\ContentsRepository;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;

/**
* @ORM\Entity(repositoryClass=ContentsRepository::class)
*/
class Contents
{
    /**
    * @ORM\Id()
    * @ORM\GeneratedValue()
    * @ORM\Column(type="integer")
    */
    private $id;

    /**
    * @ORM\OneToOne(targetEntity=ContentsTenders::class, mappedBy="content", cascade={"persist", "remove"})
    */
    private $tender;

    public function getId(): ?int
    {
        return $this->id;
    }

    public function getPosition(): ?int
    {
        return $this->position;
    }

    public function getTender(): ?ContentsTenders
    {
        return $this->tender;
    }

    public function setTender(ContentsTenders $tender): self
    {
        $this->tender = $tender;

        // set the owning side of the relation if necessary
        if ($tender->getContent() !== $this) {
            $tender->setContent($this);
        }

        return $this;
    }
}

插件的实体:

<?php

namespace App\Entity;

use App\Repository\ContentsTendersRepository;
use DateTime;
use Doctrine\ORM\Mapping as ORM;

/**
* @ORM\Entity(repositoryClass=ContentsTendersRepository::class)
*/
class ContentsTenders
{
    /**
    * @ORM\Id()
    * @ORM\GeneratedValue()
    * @ORM\Column(type="integer")
    */
    private $id;

    /**
    * @ORM\OneToOne(targetEntity=Contents::class, inversedBy="tender", cascade={"persist", "remove"})
    * @ORM\JoinColumn(nullable=false)
    */
    private $content;

    public function getId(): ?int
    {
        return $this->id;
    }

    public function getContent(): ?Contents
    {
        return $this->content;
    }

    public function setContent(Contents $content): self
    {
        $this->content = $content;

        return $this;
    }
}

文章形式:

class ContentsType extends AbstractType
{

    //public function __construct(EntityManagerInterface $em, UserInterface $user)
    //{
    //    $this->em = $em;
    //    $this->user = $user;
    //}

    /**
    * {@inheritdoc}
    */
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder
            ->add('isTender', CheckboxType::class, [
                'mapped' => false,
                'required' => false,
                'label' => 'Przetarg',
            ])
            ->add('tender', NewTendersType::class, [
                'required' => false,
                'constraints' => array(new Valid()),
                'empty_data' => null
            ])
            ->add('save', SubmitType::class, [
                'label' => 'Zapisz',
                'attr' => ['class' => 'save'],
            ])  
        ;

        $builder->addEventListener(
            FormEvents::SUBMIT,
            function (FormEvent $event) {
                $form = $event->getForm();
                $data = $event->getData();
                if(!$form['isTender']->getData()){
                    //
                }
            }
        );
    }

    /**
    * @param OptionsResolver $resolver
    */
    public function configureOptions(OptionsResolver $resolver)
    {
        $resolver->setDefaults([
            'data_class' => Contents::Class,
            'accessible_ids' => [],
        ]);
    }
}

@编辑 我用过:

并添加:

class NullableTenderTransformer implements DataTransformerInterface
{
    public function transform($value)
    {
        return $value;
    }

    public function reverseTransform($data)
    {
    if($data->getTender()->getTenContracting() === null 
    && $data->getTender()->getTenOrderFor() === null
    && $data->getTender()->getTenNumber() === null) {
        $data->resetTender();
    }

    return $data;
    }
}

结合下面的答案和控制器中的$content->setTender(new ContentsTenders());,+模型中的重置方法,它有效。

不过,这可能不是一个完美的解决方案。

您可以使用验证组

'constraints' => array(new Valid(['groups' => "withTender")


public function configureOptions(OptionsResolver $resolver)
{
    $resolver->setDefaults([
        'data_class' => Contents::Class,
        'accessible_ids' => [],
        'validation_groups' => function (FormInterface $form): array {
            $data = $form->getData();

            $groups = ['Default'];
            if ($data->isTender) {
                $groups[] = 'withTender';
            }
            return $groups;
        }
    ]);
}