Search code examples
phpsymfonyfosuserbundlesymfony-3.2

Symfony3 login and register form in one template on FOSUserBundle


I need to put in the same page the login form and the registration form. I'm using Symfony 3.2 and FOS User Bundle.

I found this: How to merge login and register form in one template on FOSUserBundle

Which was my first approach, but there is a problem. On validation error, the page gets redirected to another route (with only the template of the submitted form displaying). The problem is that the SecurityController and RegistrationController doesn't know of the controller which renders both FOS controllers in Twig, they just display the form template. With this approach, I can't override the template the fos controllers are displaying to display the main template, becuase that would generate an infinite recursion.

I tried overriding the fos controllers to do a redirect on a validation error of the form, but the redirect makes the validation messages get lost.

I also tried forwarding the request to both fos SecurityController and RegistrationController ( like this: http://symfony.com/doc/current/controller/forwarding.html ) but for the check action of the login, I get:

RuntimeException 'You must configure the check path to be handled by the firewall using form_login in your security firewall configuration.'.

It's already configured and was working, but forwarding the request directly to the controller seems to break it (I couldn't figure out how is the login check being procesed really, as the check method returns only a runtime exception)

The last solution I could think of is to create both forms in the new controller, but I don't know how to call the fos user login check manually (for the registration I could just copy all the registerAction from RegistrationController).

Thanks for your time.


Solution

  • Ok, I finally could get it working. I made a new controller which renders the login form and the registration form using the output of the fos controllers. The login form action route is login_check, the registration form action route is the same as the the route of the controller (the value of frontend_login)

    The controller:

    /**
     * @Route("/ingresar", name="frontend_login")
     * @Method({"GET", "POST"}) 
     */
    public function loginAndRegisterAction(Request $request){
    
        $login_response = $this->forward('FOSUserBundle:Security:login', array( $request ));
        $register_response = $this->forward('FOSUserBundle:Registration:register', array( $request ));
    
        return $this->render('frontend/usuario/login_register.html.twig', array(
            'login' => $login_response->getContent(),
            'register' => $register_response->getContent(),
            ));
    }
    

    In the template that displays the contents, display it raw (the controllers return the forms already rendererd as html in it's content)

    {{ login|raw }}
    {{ register|raw }}
    

    I have to override the FosUserBundle templates soy the don't extend the fosuserbundle layout. The Resources/FOSUserBundle/layout.html.twig:

    {% block fos_user_content %}{% endblock fos_user_content %}
    

    Configure the security.yml to indicate that the login path is the one of the controller we defined. On error it will display the frontend_login route.

    frontend:
            pattern: ^/
            context: website
            form_login:
                provider: fos_userbundle
                login_path: frontend_login
                check_path: login_check
    

    Finally, override the template displaying the form so that the route of the action of the registration form is frontend_login. I do this way beacause I will need to change the html structure, I supposed that changing the fos_user_registration_register route to the one we defined should do the trick.

    {{ form_start(form, {'method': 'post', 'action': path('frontend_login'), 'attr': {'class': 'fos_user_registration_register', 'novalidate': 'novalidate'}}) }}
        {{ form_widget(form) }}
        <div>
            <input type="submit" value="Submit" />
        </div>
    {{ form_end(form) }}