dtzh131555 2019-01-25 14:37
浏览 60

添加新集合元素时,无法在嵌套集合的类验证器中获取父集合(Symfony 3.4)

What i want to do

In a form I have a nested OneToMany-Collection ProspektDates with a DateField in a nested OneToMany-Collection Prospektlaendervarianten of Entity Prospekt, that i want to validate with a class validator against properties of Entity Prospekt dependent of a property in parent Collection Prospektaendervarianten.

Problem

When I edit an existing property date of ProspektDate, everything's fine. The problem is, that when the class validator is called and i have added a new date in the Form, the property laendervarianten in the collection ProspektDates isn't set and i don't have access to the parent and can't validate against their properties.

The adding of a date in ProspektDate is much like described here: https://symfony.com/doc/current/form/form_collections.html

Solutions i thought about

I probably could add a hidden entity field to the form of ProspektDates, but that sounds like a workaround to me, since Doctrine does at one point set the property laendervarianten, only too late for me and i wonder if there's any better possibility without adding a field to the form.

I also thought about a Class Validator for ProspektLaendervarianten in which i have access to everything, but that also sounds like a workaround for something i missed. Also then i always have to validate the whole collection i want to be able to validate a single date of ProspektDates.

Setup

Entity Prospekt

/**
 * Prospekt
 *
 * @ORM\Table(name="prospekt")
 * @ORM\Entity()
 */
class Prospekt
{
    /**
     * @var \DateTime
     *
     * @ORM\Column(name="du", type="datetime", nullable=true)
     */
    private $du;


    /**
     * @var \DateTime
     *
     * @ORM\Column(name="uebersetzung_an_druckerei_date", type="datetime", nullable=true)
     */
    private $uebersetzungAnDruckereiDate;


    /**
     *
     * @Assert\Valid
     * @ORM\OneToMany(targetEntity="ProspektLaendervarianten", mappedBy="prospekt", orphanRemoval=true, cascade={"all"}, fetch="EAGER")
     */
    private $laendervarianten;

}

Entity ProspektLaendervarianten

/**
 * ProspektLaendervarianten
 *
 * @ORM\Table(name="prospekt_laendervarianten")
 * @ORM\Entity()

 */
class ProspektLaendervarianten
{
    /**
     * @ORM\ManyToOne(targetEntity="Prospekt", inversedBy="laendervarianten")
     * @ORM\JoinColumn(name="prospekt_id", referencedColumnName="id", nullable=true)
     */
    private $prospekt;

    /**
     * @ORM\ManyToOne(targetEntity="BaseCountries")
     * @ORM\JoinColumn(name="base_country_id", referencedColumnName="id", nullable=true)
     */
    private $baseCountry;

    /**
     *
     * @Assert\Valid()
     * @ORM\OneToMany(targetEntity="ProspektDates", mappedBy="laendervarianten", orphanRemoval=true, cascade={"all"}, fetch="EAGER")
     * @ORM\OrderBy({"date" = "ASC"})
     */
    protected $prospektDates;
}

Entity ProspektDates

/**
 * ProspektDates
 *
 * @CustomAssert\EtGermany
 *
 * @ORM\Table(name="prospekt_date)
 * @ORM\Entity()
 */


class ProspektDates
{
    /**
     * @var \DateTime|null
     * @Assert\Valid()
     * @ORM\Column(name="date", type="datetime", nullable=true)
     */
    private $date;

    /**
     * @var ProspektLaendervarianten
     *
     * @ORM\ManytoOne(targetEntity="ProspektLaendervarianten", inversedBy="prospektDates")
     * @ORM\JoinColumn(name="prospekt_laender_varianten_id", referencedColumnName="id", nullable=false)
     */
    private $laendervarianten;
}

Part of the Form

{% for land in form.laendervarianten %}

    <div class="row">
        <div class="col-md-4">
            <div class="row">
                <div class="col-md-6 aktiv">
                        {{ land.vars.data.baseCountry.title }}
                </div>
            </div>
        </div>
        <div class="col-md-8 without-padding">
            <div class="row">

                <div class="col-md-4">
                    <div class="row prospekt-dates"
                         data-prototype="{{ form_widget(land.prospektDates.vars.prototype)|e('html_attr') }}"
                         data-index="{{ land.prospektDates|length }}">

                        {{ form_widget(land.prospektDates) }}


                        <div class="form-group">
                            <a href="#" class="prospekt-date-add">
                                <span class="fa fa-plus-circle"></span>
                            </a>
                        </div>


                    </div>
                </div>
            </div>
        </div>
    </div>
{% endfor %}

Class Validator for Entity ProspektDates

    class EtGermanyValidator extends ConstraintValidator
    {
        public function validate($protocol, Constraint $constraint)
        {

            /* @var $protocol ProspektDates */
            if (!$constraint instanceof EtGermany) {
                throw new UnexpectedTypeException($constraint, EtGermany::class);
            }


            if (null === $protocol->getDate() || '' === $protocol->getDate()) {
                return;
            }
            //when saving new date laendervarianten is null. Here's my problem!
            if (empty($protocol->getLaendervarianten())) {
                return;
            }

            // only validate if BaseCountry of parent Collection is Germany
            if (BaseCountries::DE !== $protocol->getLaendervarianten()->getBaseCountry()->getId()) {
                return;
            }

            // check if property Date (DateTime) of ProspektDates is later than property Du (DateTime) of Entity Prospekt
            $du = $protocol->getLaendervarianten()->getProspekt()->getDu();
            $et = $protocol->getDate();
            if ($et < $du) {
                $this->context->buildViolation($constraint->message)
                    ->atPath('date')
                    ->addViolation();
            }
    /*
            if (!is_string($value)) {
                throw new UnexpectedTypeException($value, 'string');
            }
    */

        }
    } 

FormType for Collection ProspektDates

class ProspektDateType extends AbstractType
{

    /**
     * {@inheritdoc}
     */
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder
            ->add('date',
                DateType::class,
                array(

                    'attr' => [
                        'class' => 'form-control input-inline date_custom input-sm date_et',
                        'html5' => false,
                        'readonly' => true

                    ],
                    'required' => false,
                    'widget'        => 'single_text',
                    'format'        => 'ccc, dd.MM.y',
                    'label' => 'ET',
                    'model_timezone' => 'Europe/Berlin',

                )
            );
    }

    /**
     * {@inheritdoc}
     */
    public function configureOptions(OptionsResolver $resolver)
    {
        $resolver->setDefaults(array(
            'data_class' => ProspektDates::class,
            'error_bubbling'=>false
        ));
    }

    /**
     * {@inheritdoc}
     */
    public function getBlockPrefix()
    {
        return 'prospektDates';
    }
}

FormType for Collection ProspektLaendervarianten

class ProspektLaendervariantenType extends AbstractType

{
    /**
     * {@inheritdoc}
     */
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder->add('prospektDates', CollectionType::class, [
            'label'         => 'ETs',
            'entry_type'    => ProspektDateType::class,
            'entry_options' => [
                'label' => false,
                'attr' => [
                    'class' => 'date-item', 
                ],
            ],
            'allow_add'     => true,
            'allow_delete'  => true,
            'prototype'     => true,
            'required'      => false,
           'by_reference'  => false,
            'delete_empty'  => true,
           'prototype_name' => '__prospektDates-collection__',

           'attr'          => [
                'class' => 'prospektDates-collection',
            ],
           'block_name' => 'prospektDates',

       ]);

    }



    /**
     * {@inheritdoc}
     */
    public function configureOptions(OptionsResolver $resolver)
    {
        $resolver->setDefaults(array(
            'data_class' => 'ProspektLaendervarianten::class',
        ));
    }

    /**
     * {@inheritdoc}
     */
    public function getBlockPrefix()
    {
        return 'laendervarianten';
    }
}
  • 写回答

0条回答 默认 最新

    报告相同问题?

    悬赏问题

    • ¥100 任意维数的K均值聚类
    • ¥15 stamps做sbas-insar,时序沉降图怎么画
    • ¥15 unity第一人称射击小游戏,有demo,在原脚本的基础上进行修改以达到要求
    • ¥15 买了个传感器,根据商家发的代码和步骤使用但是代码报错了不会改,有没有人可以看看
    • ¥15 关于#Java#的问题,如何解决?
    • ¥15 加热介质是液体,换热器壳侧导热系数和总的导热系数怎么算
    • ¥100 嵌入式系统基于PIC16F882和热敏电阻的数字温度计
    • ¥15 cmd cl 0x000007b
    • ¥20 BAPI_PR_CHANGE how to add account assignment information for service line
    • ¥500 火焰左右视图、视差(基于双目相机)