Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

symfony2: How to add validation rules to username?

Since I am extending the BaseUser, I don't have the property $name in my user entity. How can I add validation to it? Can I just add a property $username to this entity? Would that mess with or override certain functionality or validation or such that is provided natively to a user class?

I rad this question: FOSUserBundle - Validation for username, password or email fields.
But since I am using a bunch of annotation validations, that won't work for me.

Follow-up question - I see that in fosUSerBundle XML file, for validating the user that it has natively a checker for use of a taken username. I don't get that functionality, mine goes straight to a SQL error message if I try to add a user with the same name as an existing user. Is this somehow because I am using annotations which overrides the XML validation file all together?

<?php
namespace BizTV\UserBundle\Entity;

use BizTV\UserBundle\Validator\Constraints as BizTVAssert;
use Symfony\Component\Security\Core\User\AdvancedUserInterface;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;


use FOS\UserBundle\Model\User as BaseUser;

use Doctrine\ORM\Mapping as ORM;

use BizTV\BackendBundle\Entity\company as company;


class User extends BaseUser implements AdvancedUserInterface
{
    /**
     * @ORM\Id
     * @ORM\Column(type="integer")
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    protected $id;  

//TODO: Add regex on $name for a-z, A-Z, 0-9 and _ (underscore) 
//TODO: Add constraint on $name    * @BizTVAssert\NameExists    (and finish coding this constraint)

    /**
    * @var object BizTV\BackendBundle\Entity\company
    *  
    * @ORM\ManyToOne(targetEntity="BizTV\BackendBundle\Entity\company")
    * @ORM\JoinColumn(name="company", referencedColumnName="id", nullable=false)
    */
    protected $company; 

    /**
    * @var object BizTV\UserBundle\Entity\UserGroup
    * @ORM\ManyToOne(targetEntity="BizTV\UserBundle\Entity\UserGroup")
    * @ORM\JoinColumn(name="userGroup", referencedColumnName="id", nullable=true)
    */
    protected $userGroup;   

So I have looked here (symfony2 using validation groups in form) and tried that solution by adding this to the controller when building the form. Nothing happens (still jumps straight to the INSERT INTO SQL error)

    $entity  = new User();
    $request = $this->getRequest();
    $form    = $this->createForm(new newUserType($tempCompany), $entity, array('validation_groups'=>'registration'));
    $form->bind($request);

I have also tried to set this validation group "registration group" (that is supposed to give you unique user & email validation "for free") this way, in the form class:

use Symfony\Component\OptionsResolver\OptionsResolverInterface;

public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
    'validation_groups' => array('registration'),
));
}

Still nothing happens. Straight to the SQL error upon save.

like image 983
Matt Welander Avatar asked Oct 31 '13 12:10

Matt Welander


Video Answer


2 Answers

I apologize ahead of time. I'm not entirely sure if you want to validate a $name property or the $username property, but I hope this points you in the right direction.

To add a $name property with the regex validation, it should look something like this. Not tested, but this is where I would start:

<?php

use Doctrine\ORM\Mapping as ORM;
use FOS\UserBundle\Model\User as BaseUser;
use Symfony\Component\Validator\Constraints as Assert;

/**
 * @ORM\Entity
 * @ORM\Table(name="users")
 */
class User extends BaseUser
{
    /**
     * @ORM\Id
     * @ORM\Column(type="integer")
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    protected $id;

    /**
     * @ORM\Column(type="string")
     * @Assert\NotBlank(groups={"Registration","Profile"})
     * @Assert\Regex(pattern="/^[a-zA-Z0-9_]+$/", groups={"Registration","Profile"})
     */
    protected $name;

    public function setName($name)
    {
        $this->name = $name;

        return $this;
    }

    public function getName()
    {
        return $this->name;
    }
}

The unique usernameCanonical and emailCanonical validations are inherited, but they are in validation groups. More information about validation groups here and here.

It seems to me so far that the FOSUserBundle uses the user manager like a repository of sorts, so you will want to use that to interact with the storage layer as much as possible. Here is what that might look like in a controller (again, not tested):

// setting values manually
$userManager = $this->container->get('fos_user.user_manager');
$user = $userManager->createUser();
$user->setName('Joe');
$user->setUsername('joeuser');
$user->setEmail('[email protected]');
$user->setPlainPassword('secret');
$user->addRole('ROLE_EDITOR');

// persist to database
$userManager->updateUser($user);

// inherited validations that check for username and email uniqueness
// using validation group names 'Registration' and 'Profile'
$uniqueEmail = $user->getEmailCanonical();
$uniqueUsername = $user->getUsernameCanonical();

If you are using a form builder, or a form type that is not provided by the bundle, it could look something like this:

// setting values from a form
$userManager = $this->container->get('fos_user.user_manager');
$user = $userManager->createUser();

$form = $this->container->get('form.factory')
    ->createBuilder('form', $user, array('validation_groups' => 'Registration'))
    ->add('username', 'text')
    ->add('email', 'email')
    ->add('name', 'text')
    ->add('plainPassword', 'repeated', array(
        'type' => 'password'
    ))
    ->getForm();

if ($request->isMethod('POST')) {
    $form->handleRequest($request);
    if ($form->isValid()) {
        $userManager->updateUser($user);

        // redirect response
    }
}

return $this->container->get('templating')->renderResponse($templateName, array(
    'form' => $form->createView()
));

I hope this helps. Let me know if I can provide more detail. I would post some of my code here, but my implementation is a bit different.

like image 157
jrnickell Avatar answered Sep 28 '22 18:09

jrnickell


You should be able to combine constraints defined in both annotations and in PHP (but probably not both those defined in annotations and XML)

<?php

/* ... */

use Symfony\Component\Validator\Mapping\ClassMetadata;

class User extends BaseUser implements AdvancedUserInterface
{
  /* ... */

  public static function loadValidatorMetadata(ClassMetadata $metadata)
  {
    $metadata->addPropertyConstraint(
        'username'
      , new Assert\Regex(array(
            'pattern' => '/^[a-zA-Z0-9_]$/'
        )
    ));
    $metadata->addPropertyConstraint(
        'username'
      , new BizTVAssert\NameExists()
    ));
  }

  /* ... */
}
like image 34
Peter Bailey Avatar answered Sep 28 '22 18:09

Peter Bailey