【问题标题】:Dynamic embedded form with OneToOne relationship具有 OneToOne 关系的动态嵌入表单
【发布时间】:2021-04-15 12:13:08
【问题描述】:

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

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

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

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

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

我曾尝试使用表单事件来修改事件的数据,但结果证明在 PRE_SUBMIT 期间投标不是数据的一部分。 如果我尝试在 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' => [],
        ]);
    }
}

@编辑 我用过: Symfony Custom Field or Dynamic Form?

并添加:

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-&gt;setTender(new ContentsTenders());,模型中的+reset方法,它可以工作。

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

【问题讨论】:

    标签: php forms symfony doctrine


    【解决方案1】:

    您可以使用验证组

    '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;
            }
        ]);
    }
    

    【讨论】:

    • 您好,感谢您的回复!我不得不将 $data->isTender 更改为 $form['isTender']->getData(),但这并没有解决我的问题。添加解决方案后,所有情况下的验证都停止工作(选中复选框且未选中复选框)。即使它的表单为空/禁用(当投标应该为空时),它也不能解决投标仍被发送到数据库的问题。对于后面的问题,我会尝试:stackoverflow.com/questions/45728615/…
    • @Ambu 我宁愿使用数据传输对象并有条件地添加招标实体symfonycasts.com/screencast/symfony-forms/form-dto
    • 感谢您的链接!我已经使用第一个 stackoverflow 链接和您的答案。我不知道为什么你的答案第一次没有用,也许我打错了一些东西,但现在可以了!我会记得检查您的“表单模型类”链接,因为当前的解决方案感觉有点混乱。
    猜你喜欢
    • 2011-01-16
    • 1970-01-01
    • 1970-01-01
    • 2014-03-08
    • 2013-07-08
    • 1970-01-01
    • 2020-10-10
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多