Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Validation of fields not present in form but in Entity

I have a form for user registration, and only username field is present in the form. And in my form, I wish to allow user input the username only. Nicename would be same as username on registration.

This form is bind to a User entity, i.e., in my form type class:

public function setDefaultOptions(OptionsResolverInterface $resolver)
{
    $resolver->setDefaults(array(
        'data_class' => 'Some\Bundle\Entity\User',
    ));
}

entity User, which has a NotBlank constraint set for both username and nicename.

namespace Some\Bundle\Entity;

use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Constraints;

//...

class User
{
    //...

    /**
     * @var string $username
     *
     * @ORM\Column(name="user_login", type="string", length=60, unique=true)
     * @Constraints\NotBlank()
     */
    private $username;

    /**
     * @var string $nicename
     *
     * @ORM\Column(name="user_nicename", type="string", length=64)
     * @Constraints\NotBlank()
     */
    private $nicename;

    //...

However, if I build a form with only username but not nicename, on validation i.e. $form->isValid() it fails to validate.

To bypass this, I come up with the following:

namespace Some\Bundle\Controller;

use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Some\Bundle\Form\Type\RegisterType;
//...

class UserController extends Controller
{
   //...
public function registerAction()
{
    //...
    $request = $this->getRequest();
    $form = $this->createForm(new RegisterType());

    if ($request->getMethod() == 'POST') {
        // force set nicename to username.
        $registerFields = $request->request->get('register');
        $registerFields['nicename'] = $registerFields['username'];
        $request->request->set('register', $registerFields);

        $form->bind($request);

        if ($form->isValid()) {
            $user = $form->getData();
            //persist $user, etc...

And in form type I add this to my buildForm method:

$builder->add('nicename', 'hidden');

But I find this very inelegant, leave some burden to the controller (extract from the request object, put in data, and put it back into the request object, ouch!), and user can see the hidden field if he were to inspect the source code of generated HTML.

Is there anyway that can at least any controller using the form type does not need do things like above, while retaining the entity constraints?

I cannot change the table schema which backs up the User entity, and I would like to keep the NotBlank constraint.


EDIT: After long hassle, I decided to use Validation groups and it worked.

class User
{
//...

/**
 * @var string $username
 *
 * @ORM\Column(name="user_login", type="string", length=60, unique=true)
 * @Constraints\NotBlank(groups={"register", "edit"})
 */
private $username;

/**
 * @var string $nicename
 *
 * @ORM\Column(name="user_nicename", type="string", length=64)
 * @Constraints\NotBlank(groups={"edit"})
 */
private $nicename;

Form Type:

public function setDefaultOptions(OptionsResolverInterface $resolver)
{
    $resolver->setDefaults(array(
        'data_class' => 'Some\Bundle\Entity\User',
        'validation_groups' => array('register', 'Default')
    ));
}

That 'Default' is needed or it ignores all other constraints I added in the form type buildForm method... Mind you, its case sensitive: 'default' does not work.

Though, I find that it is not enough (and sorry I didn't put it in my original question), because when I persist, I need to do this in my controller:

$user->setNicename($user->getUsername());

As a bonus, I move this from controller to Form Type level by adding a Form Event Subscriber

In form type buildForm method:

$builder->addEventSubscriber(new RegisterPostBindListener($builder->getFormFactory()));

And the RegisterPostBindListener class

<?php
namespace Some\Bundle\Form\EventListener;

use Symfony\Component\Form\Event\DataEvent;
use Symfony\Component\Form\FormFactoryInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\Form\FormEvents;

class RegisterPostBindListener implements EventSubscriberInterface
{

public function __construct(FormFactoryInterface $factory)
{

}

public static function getSubscribedEvents()
{
    return array(FormEvents::POST_BIND => 'setNames');
}

public function setNames(DataEvent $event)
{
    $data = $event->getData();

    $data->setNicename($data->getUsername());
}
}
like image 306
luiges90 Avatar asked Nov 07 '12 03:11

luiges90


Video Answer


1 Answers

I think you should use validation groups.

like image 158
1ed Avatar answered Nov 09 '22 23:11

1ed