Reputation: 981
So, I have a form which has a collection and the user can add new items to this collection. My entity in this collection has an id and an integer. I want to validate that the user cannot add a item after another that has a lower integer than the first one.
For exemple, the form is empty, the user adds a record to the collection by clicking on the Add button. He enters 100 in the input field and adds another record and enters 50 in the input field. When he tries to submit I want the second record to have an error because 50 is lower than 100 but he added it after in the list.
Here are my FormTypes and Entites
class ItemType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('value');
}
}
class ItemCollectionType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('items', 'collection', array(
'type' => new ItemType(),
'allow_add' => true,
'allow_delete' => true,
'by_reference' => false,
'label' => false,
));
$builder->add('save');
}
}
class Item
{
/**
* @var integer
*
* @ORM\Column(name="id", type="integer")
* @ORM\Id
* @ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* @ORM\Column(type="integer", nullable=true)
*/
protected $value;
}
class List
{
/**
* @ORM\OneToMany(targetEntity="Item", cascade={"persist"})
* @Assert\Valid
**/
private $items;
}
I think I could do the validation in the Controller but I would like to do it in my Entity and using a Validation or a custom Validation.
** EDIT **
I can also do what DonCallisto suggested but I am trying to get error on the specific row that is in problem not just on the global class like he suggested.
I can't find how to add the propertyPath to the item in problem, I have tried to use the following code but it seems like it's still binding the error on the class rather than on the item itself like I would like.
$context->addViolationAt(
$context->getPropertyPath().'.items.data.value',
'Your violation message'
);
It also doesn't take in consideration the current element.
The following code is not working either. Here I'm hardcoding the key of the array just for the purpose of testing.
$context->addViolationAt(
$context->getPropertyPath().'.items[0].data.value',
'Your violation message'
);
Can anyone tell me how to correctly set up a path to access a child's value in a collection?
Upvotes: 3
Views: 7466
Reputation: 981
I found the answer to my question, I used part of DonCallisto's answer and I moved the code from the List
Entity to Item
and in a validator that I am calling as a CLASS_CONSTRAINT
on my Item
Entity.
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\ConstraintValidator;
/**
* @Annotation
*/
class ItemsOrderedValidator extends ConstraintValidator {
public function validate($value, Constraint $constraint) {
foreach ($value->getList()->getItems() as $item) {
$currentValue = $item->getValue();
if (isset($prevValue) && $prevValue >= $currentValue && $value === $item) {
$this->context->buildViolation($constraint->message)
->atPath('value')
->addViolation();
}
$prevValue = $currentValue;
}
}
}
Upvotes: 2
Reputation: 29932
If I understood correctly your question, you could modify your List entity as follows
use Symfony\Component\Validator\Constraints as Assert;
use Symfony\Component\Validator\ExecutionContext;
/**
* //Whatever you have here to declare this as an entity
* @Assert\Callback(methods={"areItemsOrdered"})
*/
class List
{
/**
* @ORM\OneToMany(targetEntity="Item", cascade={"persist"})
* @Assert\Valid
**/
private $items;
//whatever methods you have here
public function areItemsOrdered(ExecutionContext $context)
{
foreach ($this->items as $item) {
$current_value = $item->getValue();
if (!isset($prev_value)) {
$prev_value = $current_value;
} else {
if ($prev_value > $current_value) {
$context->addViolation('Your violation message');
}
}
}
}
}
For more information about entity callback methods: http://symfony.com/doc/2.3/reference/constraints/Callback.html
For more information about ExecutionContext: http://api.symfony.com/2.4/Symfony/Component/Validator/ExecutionContextInterface.html
Is a correct behaviour to inform user about error? I mean, could you not sort added items yourself? If so, please update your answer and I will be happy to update my code for this purpose.
Upvotes: 1