Search code examples
phpmongodbsymfonyassert

Symfony check if input data is unique


I have a custom validation array without "Entity" like this:

$dataContent = json_decode($request->getContent(), true);

$dataToCheck = [
            'name' => [
                new Assert\Required(),
                new Assert\NotBlank(),
                new Assert\Type('string'),
                new Assert\Length(['min' => 4]),
            ],
            'nick' => [
                new Assert\Required(),
                new Assert\NotBlank(),
                new Assert\Type('string'),
                new Assert\Length(['min' => 4]),
            ],
            'email' => [
                new Assert\Required(),
                new Assert\NotBlank(),
                new Assert\Email(),
            ]
]

$constraint = new Assert\Collection($dataToCheck);
$violations = $validator->validate($dataContent, $constraint);

In MongoDb I have collections "users". I need to check if 'nick' and 'email' in request is unique. So how can I add rule to fields to check if that fields are unique in DB during adding new user. And how to ignore checking unique fields during user updated details for current user Id?

Thank you.


Solution

  • One of the solutions, according the documentation Registration Form with MongoDB, would be to proceed like :

    • You user entity
    namespace Acme\AccountBundle\Document;
    
    use Doctrine\ODM\MongoDB\Mapping\Annotations as MongoDB;
    use Symfony\Component\Validator\Constraints as Assert;
    use Doctrine\Bundle\MongoDBBundle\Validator\Constraints\Unique as MongoDBUnique;
    
    /**
     * @MongoDB\Document(collection="users")
     * @MongoDBUnique(fields="email", groups={"new"})
     * @MongoDBUnique(fields="nick", groups={"new"})
     */
    class User
    {
       /**
         * @MongoDB\Id
         */
        protected $id;
    
        /**
         * @MongoDB\Field(type="string")
         * @Assert\NotBlank(groups={"new", "edit"})
         * @Assert\Email(groups={"new", "edit"})
         */
        protected $email;
    
       /**
         * @MongoDB\Field(type="string")
         * @Assert\NotBlank(groups={"new", "edit"})
         * @Assert\Length(min="4", groups={"new", "edit"})
         */
        protected $nick;
    
       /**
         * @MongoDB\Field(type="string")
         * @Assert\NotBlank(groups={"new", "edit"})
         * @Assert\Length(min="4", groups={"new", "edit"})
         */
        protected $name;
    
        public function getId()
        {
            return $this->id;
        }
    
        public function getEmail()
        {
            return $this->email;
        }
    
        public function setEmail($email)
        {
            $this->email = $email;
        }
    
        public function getNick()
        {
            return $this->nick;
        }
    
        public function setNick($nick)
        {
            $this->nick = $nick;
        }
    
        public function getName()
        {
            return $this->name;
        }
    
        public function setName($name)
        {
            $this->name = $name;
        }
    }
    
    • And then you define UserType
    namespace Acme\AccountBundle\Form\Type;
    
    use Symfony\Component\Form\AbstractType;
    use Symfony\Component\Form\Extension\Core\Type\EmailType;
    use Symfony\Component\Form\Extension\Core\Type\TextType;
    use Symfony\Component\Form\FormBuilderInterface;
    use Symfony\Component\OptionsResolver\OptionsResolver;
    use Acme\AccountBundle\Document\User;
    
    class UserType extends AbstractType
    {
        public function buildForm(FormBuilderInterface $builder, array $options)
        {
            $builder->add('email', EmailType::class)
                    ->add('nick', TextType::class)
                    ->add('name', TextType::class);
        }
    
        public function configureOptions(OptionsResolver $resolver)
        {
            $resolver->setDefaults(array(
                'data_class' => User::class,
                'validation_groups' => ['new', 'edit']
            ));
        }
    }
    

    You use validation groups because, according to what you said, you don't want to validate unique field for updating, you want to do it just for creating.

    • Finaly in a controller you can do :
    namespace Acme\AccountBundle\Controller;
    
    use Symfony\Bundle\FrameworkBundle\Controller\Controller;
    use Symfony\Component\HttpFoundation\Response;
    
    use Acme\AccountBundle\Form\Type\UserType;
    use Acme\AccountBundle\Document\User;
    use Symfony\Component\Routing\Annotation\Route;
    use Sensio\Bundle\FrameworkExtraBundle\Configuration\Security;
    
    class AccountController extends Controller
    {
        /**
         * @Route("/users/create", name="app_users_create)
         */
        public function createAction()
        {
    
            $dataContent = json_decode($request->getContent(), true);
            if ($dataContent === null || !is_array($dataContent)) {
                 // Throw exception for invalid format request
            }
            $dm = $this->get('doctrine_mongodb')->getManager();
            $user = new User();
    
            $form = $this->createForm(UserType::class, $user);    
            $form->submit($dataContent);
    
            if ($form->isSubmitted() && $form->isValid()) {
    
               $dm->persist($user);
               $dm->flush();
    
               // Return something for success
            }
    
            // Return Form error
        }
    
        /**
         * @Route("/users/edit", name="app_users_edit)
         * @Security("is_granted('IS_AUTHENTICATED_REMEMBERED')")
         */
        public function updateAction()
        {
    
            $dataContent = json_decode($request->getContent(), true);
            if ($dataContent === null || !is_array($dataContent)) {
                 // Throw exception for invalid format request
            }
            $dm = $this->get('doctrine_mongodb')->getManager();
            $user = $this->getUser();
    
            $form = $this->createForm(UserType::class, $user);    
            $form->submit($dataContent); 
    
            if ($form->isSubmitted() && $form->isValid()) {
    
               $dm->flush();
    
               // Return something for success
            }
    
            // Return Form error
        }
    
    }
    

    Update : To do it without entity, according to this documentation CustomContraint, you can use custom constraint name like UniqueCustom and add it in your constraints array. In this custom constraint(UniqueCustom) you can check if the correspondant email(or nick) already exist in your MongoDB. To ignore this check for updating, you can use validation group like above. So your constraints array like (It is just an idea) :

    $dataToCheck = [
                'email' => [
                    new Assert\Required(['groups' => ['new', 'edit']]),
                    new Assert\NotBlank(['groups' => ['new', 'edit']]),
                    new Assert\Type('string'),
                    new Assert\Email(['groups' => ['new', 'edit']]),
                    new UniqCustom(['groups' => ['new']]) // Custom uniq constraint
                ],
                'nick' => [
                    new Assert\Required(['groups' => ['new', 'edit']]),
                    new Assert\NotBlank(['groups' => ['new', 'edit']]),
                    new Assert\Type('string'),
                    new Assert\Length(['min' => 4, 'groups' => ['new', 'edit']]),
                    new UniqCustom(['groups' => ['new']])
                ],
                'name' => [
                    new Assert\Required(['groups' => ['new', 'edit']]),
                    new Assert\NotBlank(['groups' => ['new', 'edit']]),
                    new Assert\Type('string'),
                    new Assert\Length(['min' => 4, 'groups' => ['new', 'edit']]),
                ],
    
            ];
    

    The process with ODM is similar than to user ORM. I hope this attempt of solution can help you.