1

I am building a custom form input type and i'm a little confused about how to implement it's validation in the official documentation : https://symfony.com/doc/current/form/create_custom_field_type.html

My field type basically allows the user to insert as many values as he want's. It is handled by JavaScript and is rendered as a hidden input that holds the values as a JSON array.

enter image description here

The script works well, now all i need to do is make sure that the input value contains a valid single-dimension JSON array in case the user messes with it using his browser's dev tools and i'd like this to be handle by the input class to maximize the field re-usability.

How can i achieve that ?

Here's my input class, but it's pretty much empty :

<?php

namespace App\Form\Type;

use Symfony\Component\Form\AbstractType;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Form\Extension\Core\Type\HiddenType;

class ListType extends AbstractType
{
    public function configureOptions(OptionsResolver $resolver)
    {
        $resolver->setDefaults([]);
    }

    public function getParent()
    {
        return HiddenType::class;
    }
}

Thanks

Magnesium
  • 597
  • 6
  • 22
  • What will you do with this data? My question is to understand your needs. What is the look of your entity – Mcsky Sep 13 '18 at 13:33
  • Why not just use the existing HTML input elements to submit the data as an array automatically? https://stackoverflow.com/questions/20184670/html-php-form-input-as-array – Chip Dean Sep 13 '18 at 15:06
  • @Mcsky I have a dynamic structure of input types that can be created and linked to any content of my website. The input value can be a text input, a boolean or a JSON array so far, i'll probably add more. The data is stored in a text field in my entity – Magnesium Sep 13 '18 at 16:57
  • @Chip Dean I'll check this when i get back at work tomorrow – Magnesium Sep 13 '18 at 16:57
  • You want to validate that `input value contains a valid single-dimension JSON array` but you say that each input can contain a text input, a boolean or a `JSON array`. So you can have 2 levels array at least. You could do your own validator for the field of your entity. Check this https://symfony.com/doc/current/validation/custom_constraint.html . I can provide example as answer if needed – Mcsky Sep 13 '18 at 17:04
  • @Mcsky Yes, the value that is stored in the database can take several forms, that's why i'd like to do the validation on my custom input type level. Even if there are other ways to implement what i'm trying to achieve what i'm trying to do, i'd still like to know how do some validations on the input class for my own education. I've tried to look at Symfony's default types such as date or currency but there's lot's of code there and it's hard to find what could be relevant to my case. – Magnesium Sep 13 '18 at 17:19

1 Answers1

2

This is how are ended up doing it using a custom validator, better solutions are welcome:

namespace App\Form\Type;

use Symfony\Component\Form\AbstractType;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Form\Extension\Core\Type\HiddenType;

use App\Validator\Constraints\SingleDimensionJSON();

class ListType extends AbstractType
{
    public function configureOptions(OptionsResolver $resolver)
    {
        $resolver->setDefaults([
            'constraints' => [
                new SingleDimensionJSON()
            ]
        ]);
    }

    public function getParent()
    {
        return HiddenType::class;
    }
}

And the validator class:

namespace App\Validator\Constraints;

use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\ConstraintValidator;
use Symfony\Component\Validator\Exception\UnexpectedTypeException;

class SingleDimensionJSON extends ConstraintValidator
{
    public function validate($value, Constraint $constraint)
    {
        if ($value === null || $value === '') {
            return;
        }

        if (!is_string($value)) {
            throw new UnexpectedTypeException($value, 'string');
        }

        $toArray = json_decode($value, true);

        if ($toArray === null) {
            $this->context
                ->buildViolation($constraint->message)
                ->setParameter('{{ string }}', $value)
                ->addViolation()
            ;
            return;
        }
        foreach ($toArray as $item) {
            if (is_array($item)) {
                throw new InvalidArgumentException('Multi dimensional array isn\'t allowed');
            }
        }
}
Magnesium
  • 597
  • 6
  • 22