Search code examples
phpsymfonyfosuserbundlefosrestbundlesymfony-2.7

Set up registration FOSUserBundle with FOSRestBundle REST API


Problem fixed, check my answer.

I'm building a registration endpoint on my Symfony2.7 rest api. I am using FosRestBundle and FosUserBundle

Here is the user model :

<?php

namespace AppBundle\Entity;

use FOS\UserBundle\Model\User as BaseUser;
use Doctrine\ORM\Mapping as ORM;

/**
 * @ORM\Entity
 * @ORM\Table(name="fos_user")
 */
class User extends BaseUser {

    /**
     * @ORM\Id
     * @ORM\Column(type="integer")
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    protected $id;






    public function __construct() {
        parent::__construct();
        // your own logic
    }

}

\ Here is the UserType form : \

class UserType extends AbstractType
{
    /**
     * @param FormBuilderInterface $builder
     * @param array $options
     */
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder
            ->add('email', 'email')
            ->add('username', null)
            ->add('plainPassword', 'repeated', array(
                'type' => 'password',

                'first_options' => array('label' => 'password'),
                'second_options' => array('label' => 'password_confirmation'),

            ))
        ;
    }

    /**
     * @param OptionsResolverInterface $resolver
     */
    public function setDefaultOptions(OptionsResolverInterface $resolver)
    {
        $resolver->setDefaults(array(
            'data_class' => 'AppBundle\Entity\User',
            'csrf_protection'   => false,
        ));
    }

    /**
     * @return string
     */
    public function getName()
    {
        return 'user';
    }
}

And this the post user controller :

public function postUserAction(\Symfony\Component\HttpFoundation\Request $request) {
        $user = new \AppBundle\Entity\User();
        $form = $this->createForm(new \AppBundle\Form\UserType(), $user);
        $form->handleRequest($request);

        if ($form->isValid()) {
            $em = $this->getDoctrine()->getManager();
            $em->persist($user);
            $em->flush();


            $view = $this->view(array('token'=>$this->get("lexik_jwt_authentication.jwt_manager")->create($user)), Codes::HTTP_CREATED);

            return $this->handleView($view);

        }

        return array(
            'form' => $form,
        );
    }

The problem is that when i submit wrong information, or empty information, the server return a bad formated 500 error with doctrine / mysql details of null value for not null row in state of a json response with the list of bad formated entries.

Any idea on how to fix this behaviour ? How come the validation get by passed and


Solution

  • Ok after spending a lot of time reading the FOSUserBundle code, and particularly the registration controller and the form factory, i came up with a fully working solution.

    Before doing anything don't forget to disable CSRF in your symfony2 configuration.

    Here is the controller I use to register :

     public function postUserAction(\Symfony\Component\HttpFoundation\Request $request) {
    
    
            /** @var $formFactory \FOS\UserBundle\Form\Factory\FactoryInterface */
            $formFactory = $this->get('fos_user.registration.form.factory');
            /** @var $userManager \FOS\UserBundle\Model\UserManagerInterface */
            $userManager = $this->get('fos_user.user_manager');
            /** @var $dispatcher \Symfony\Component\EventDispatcher\EventDispatcherInterface */
            $dispatcher = $this->get('event_dispatcher');
    
            $user = $userManager->createUser();
            $user->setEnabled(true);
    
            $event = new \FOS\UserBundle\Event\GetResponseUserEvent($user, $request);
            $dispatcher->dispatch(\FOS\UserBundle\FOSUserEvents::REGISTRATION_INITIALIZE, $event);
    
            if (null !== $event->getResponse()) {
                return $event->getResponse();
            }
    
            $form = $formFactory->createForm();
            $form->setData($user);
    
            $form->handleRequest($request);
    
            if ($form->isValid()) {
                $event = new \FOS\UserBundle\Event\FormEvent($form, $request);
                $dispatcher->dispatch(\FOS\UserBundle\FOSUserEvents::REGISTRATION_SUCCESS, $event);
    
                $userManager->updateUser($user);
    
                if (null === $response = $event->getResponse()) {
                    $url = $this->generateUrl('fos_user_registration_confirmed');
                    $response = new \Symfony\Component\HttpFoundation\RedirectResponse($url);
                }
    
                $dispatcher->dispatch(\FOS\UserBundle\FOSUserEvents::REGISTRATION_COMPLETED, new \FOS\UserBundle\Event\FilterUserResponseEvent($user, $request, $response));
    
                $view = $this->view(array('token' => $this->get("lexik_jwt_authentication.jwt_manager")->create($user)), Codes::HTTP_CREATED);
    
                return $this->handleView($view);
            }
    
            $view = $this->view($form, Codes::HTTP_BAD_REQUEST);
            return $this->handleView($view);
        }
    

    Now the tricky part was submiting the form using REST. The problem was that when I sent i JSON like this one :

    {
            "email":"xxxxx@xxxx.com",
            "username":"xxx",
            "plainPassword":{
                "first":"xxx",
                "second":"xxx"
            }
        }
    

    The API was responding like nothing was submited.

    The solution is that Symfony2 is waiting for you to encapsulate your form data in the form name !

    The question was "I didnt create this form so i dont know what is its name..". So i went again in the bundle code and found out that the form type was fos_user_registration and the getName function was returning fos_user_registration_form.

    As a result i tried to submit my JSON this way :

    {"fos_user_registration_form":{
            "email":"xxxxxx@xxxxxxx.com",
            "username":"xxxxxx",
            "plainPassword":{
                "first":"xxxxx",
                "second":"xxxxx"
            }
        }}
    

    And voila! it worked. If you are struggling setting up your fosuserbundle with fosrestbundle and LexikJWTAuthenticationBundle just ask me i'll be glad to help.