Что я хочу сделать
В форме у меня есть вложенная коллекция OneToMany ProspektDates
с DateField во вложенной коллекции OneToMany Prospektlaendervarianten
объекта Prospekt
, которую я хочу проверитьс валидатором класса для свойств Entity Prospekt
, зависящих от свойства в родительской коллекции Prospektaendervarianten
.
Проблема
Когда я редактирую существующее свойство date
из ProspektDate
, все в порядке.Проблема в том, что когда вызывается валидатор класса и я добавил новый date
в форму, свойство laendervarianten
в коллекции ProspektDates
не установлено, и у меня нет доступа к родителю ине может проверить их свойства.
Добавление даты в ProspektDate
очень похоже на описанное здесь: https://symfony.com/doc/current/form/form_collections.html
Решения, которые я думал о
Я, наверное,Я мог бы добавить скрытое поле сущности в форму ProspektDates
, но для меня это звучит как обходной путь, так как Doctrine в какой-то момент устанавливает свойство laendervarianten
, слишком поздно для меня, и мне интересно, есть ли лучшая возможность бездобавление поля в форму.
Я также подумал о Class Validator для ProspektLaendervarianten
, в котором у меня есть доступ ко всему, но это также звучит как обходной путь для чего-то, что я пропустил.Кроме того, мне всегда нужно проверять всю коллекцию, которую я хочу, чтобы иметь возможность проверить одну date
из ProspektDates
.
Настройка
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;
}
Часть формы
{% 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 %}
Класс валидатора для 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 для коллекционных проспектовDates
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 для коллекционных проспектов Laendervarianten
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';
}
}