Skip to content

Symfony, constraint for User

Good day! Just started learning Symfony on my own.

I’m making a news portal. The administrator can download news from an Excel file. I am converting a file to an associative array. For example:

[ 'Title' => 'Some title',
  'Text'  => 'Some text',
  'User'  => 'example@example.com',
  'Image' => 'https://loremflickr.com/640/360'
]

Next, I want to send this array to the form and use ‘constraints’ to validate it. There are no problems with the fields ‘Title’, ‘Text’, ‘Image’. I don’t know how to properly check the ‘User’ field. The user in the file is submitting an Email, but I want to check that a user with that Email exists in the database.

NewsImportType

    class NewsImportType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder
            ->add('title', TextType::class, [
                'constraints' =>
                [
                    new NotBlank(),
                    new Length(['min' => 256])
                ],
            ])
            ->add('text', TextareaType::class, [
                'constraints' =>
                [
                    new NotBlank(),
                    new Length(['max' => 1000])
                ],
            ])
            ->add('user', TextType::class, [
                'constraints' =>
                [
                    new NotBlank(),
                    new Email(),
                ],
            ->add('image', TextType::class, [
                'constraints' =>
                [
                    new NotBlank(),
                    new Length(['max' => 256]),
                    new Url()
                ],
            ]);
    }

    public function configureOptions(OptionsResolver $resolver): void
    {
        $resolver->setDefaults([
            'allow_extra_fields' => true,
            'data_class' => News::class,
        ]);
    }
}

The entities User and News are connected by a One-to-Many relationship.

I was thinking about using ChoiceType and calling UserRepository somehow, but I don’t understand how to apply it correctly.

Please tell me how to correctly write ‘constraint’ for the ‘user’ field. thank!

Answer

Create a Custom Constraint. This way it is reusable in any other form you would like to check for a user.

Create a new folder in your project src/Validator then put these 2 files in there.

The Constraint

// src/Validator/userAccountExists.php

namespace AppValidator;

use SymfonyComponentValidatorConstraint;

class UserAccountExists extends Constraint
{
    public $message = 'User account does't exists. Please check the email address and try again.';
}

The Validator

// src/Validator/userAccountExistsValidator.php

namespace AppValidator;

use SymfonyComponentValidatorConstraint;
use SymfonyComponentValidatorConstraintValidator;
use SymfonyComponentValidatorExceptionUnexpectedTypeException;
use SymfonyComponentValidatorExceptionUnexpectedValueException;
use DoctrineORMEntityManagerInterface;
use AppEntityUser;

class UserAccountExistsValidator extends ConstraintValidator
{
    private $entityManager;

    public function __construct(EntityManagerInterface $entityManager)
    {
        $this->entityManager = $entityManager;
    }

    public function validate($email, Constraint $constraint)
    {
        if (!$constraint instanceof UserAccountExists) {
            throw new UnexpectedTypeException($constraint, UserAccountExists::class);
        }

        if (null === $email || '' === $email) {
            return;
        }

        if (!is_string($email)) {
            throw new UnexpectedValueException($email, 'string');
        }

        if (!$this->userExists($email)) {
            $this->context->buildViolation($constraint->message)->addViolation();
        }
    }

    private function userExists(string $email): bool
    {
        $user = $this->entityManager->getRepository(User::class)->findOneBy(array('email' => $email));

        return null !== $user;
    }
}

In your form you can now use the validator

->add('user', TextType::class, [
    'constraints' =>
        [
            new NotBlank(),
            new Email(),
            new UserAccountExists(),
        ],

Remember to add use AppValidatorUserAccountExists; to your form.