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());
}
}
I think you should use validation groups.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With