Symfony: create login, register pages

Wow, this is core chapter, I means for myself before precedent chapter is actually prepare for this

Before go through all the step, we should download some core bundles–>service, tools

go to termernal, and install follow :

$ composer require symfony/form

$ composer require doctrine form security validator

composer require security

this is for user entity part

$ ./bin/console debug:autowiring

we could see in terminal had download those security alias

Oh there’s might be more complete package you could just install once for the register form, and you could read reference doc here

The first concept is before create register page, you need to have the register list in order to config weather user has been registered or not ( then login), so we go to the first step to create list users page

So let’s start do it

Opps, before start, create your userType and User entity, cause later in Register controller we need to call those to bind the actions

Go to src/ create Form directory and create UserType.php :

<?php
namespace App\Form;

use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\EmailType;
use Symfony\Component\Form\Extension\Core\Type\PasswordType;
use Symfony\Component\Form\Extension\Core\Type\RepeatedType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;

class UserType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder
            ->add('name', TextType::class)
            ->add('email', EmailType::class)
            ->add('plainPassword', RepeatedType::class, [
                'type' => PasswordType::class,
                'first_options' => ['label' => 'Password'],
                'second_options' => ['label' => 'Confirm Password'],
            ]);
    }

    public function configureOptions(OptionsResolver $resolver)
    {
        $resolver->setDefaults([
            'data_class' => 'App\Entity\User',
        ]);
    }
}

those you could find the symfony register doc here,

And also create Entity directory then create User.php file:

<?php
namespace App\Entity;

use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Validator\Constraints as Assert;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;

/**
 * @ORM\Entity
 * @UniqueEntity(fields="email", message="This email address is already in use")
 */
class User implements UserInterface
{
    /**
     * @ORM\Id;
     * @ORM\Column(type="integer")
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    protected $id;

    /**
     * @ORM\Column(type="string", length=255, unique=true)
     */
    protected $email;

    /**
     * @ORM\Column(type="string", length=40)
     */
    protected $name;

    /**
     * @ORM\Column(type="string", length=50)
     */
    protected $role;

    /**
     * @Assert\Length(max=4096)
     */
    protected $plainPassword;

    /**
     * @ORM\Column(type="string", length=64)
     */
    protected $password;

    public function eraseCredentials()
    {
        return null;
    }

    public function getRole()
    {
        return $this->role;
    }

    public function setRole($role = null)
    {
        $this->role = $role;
    }

    public function getRoles()
    {
        return [$this->getRole()];
    }

    public function getId()
    {
        return $this->id;
    }

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

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

    public function getUsername()
    {
        return $this->email;
    }

    public function getEmail()
    {
        return $this->email;
    }

    public function setEmail($email)
    {
        $this->email = $email;
    }

    public function getPassword()
    {
        return $this->password;
    }

    public function setPassword($password)
    {
        $this->password = $password;
    }

    public function getPlainPassword()
    {
        return $this->plainPassword;
    }

    public function setPlainPassword($plainPassword)
    {
        $this->plainPassword = $plainPassword;
    }

    public function getSalt()
    {
        return null;
    }
}

This is also from symfony doc, which allow the data validate

All right, the user entity is build, this could allow you get through register and login binding data methods, start now:

Step 1 create your user list controllers

go to src/Controller, and create this controllers: ListController.php :

<?php
namespace App\Controller;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;


class ListController extends Controller
{
    /**
     * @Route("/welcome", name="welcome")
     */
    public function showAction(Request $request)
    {
        $characters = [
            'Burger lover' => 'Tess Hsu',
            'Pasta lover'           => 'Anais siah',
            'Arya Stark'         => 'Maisie Williams',
            'Melisandre'         => 'Carice van Houten',
            'Khal Drogo'         => 'Jason Momoa',
            'Tyrion Lannister'   => 'Peter Dinklage',
            'Ramsay Bolton'      => 'Iwan Rheon',
            'Petyr Baelish'      => 'Aidan Gillen',
            'Brienne of Tarth'   => 'Gwendoline Christie',
            'Lord Varys'         => 'Conleth Hill'
        ];

        return $this->render('default/index.html.twig', array('character' => $characters));
    }
}

here set the route to show user list by this path: http://127.0.0.1:8000/welcome

defined user’s character and real name ( as key value array)
then ready to render to this view template: index.html.twig

Step 2 your list view page

go to template/, create directory defalut, and create a file called: index.html.twig

{% extends 'base.html.twig' %}

{% block body %}
    <div class="container">
        <div class="row">
            <div class="col-md-10 col-md-offset-1">
                <div class="panel panel-success">
                    <div class="panel-heading">List of Game of Thrones Characters</div>

                    {% if app.user != null %}
                        <table class="table">
                            <tr>
                                <th>Character</th>
                                <th>Real Name</th>
                            </tr>

                            {% for key, item in character %}
                                <tr>
                                    <td>{{ key }}</td><td>{{ item }}</td>
                                </tr>
                            {% endfor %}
                        </table>
                    {% endif %}


                </div>

                {% if app.user == null %}
                    <a href="/login" class="btn btn-info"> You need to login to see the list 😜😜 >></a>
                {% endif %}
            </div>
        </div>
    </div>
{% endblock %}

Here load the list from listController, and make href to login page:

{% if app.user == null %}
    <a href="/login" class="btn btn-info"> You need to login to see the list 😜😜 >></a>
{% endif %}

Yes!!! great, last step is get the burger menu Account link to this page:

go to template/base.html.twig

<a class="dropdown-item" href="{{ path('welcome') }}">Account</a>

add the path name: welcome as we had defined in listController.php route notation

go back to browser, and click the burger menu: Account

then you should link to this page

Right on!!!!! you had seen the list page as above just created

we want to the link: You need to login to see the list work, so next let’s create login page

Step 3 Create security login controller

go to src/Controller, create file called SecurityController.php

<?php

namespace App\Controller;

use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;

class SecurityController extends Controller
{
    /**
     * @Route("/login", name="login")
     */
    public function loginAction(Request $request)
    {
        $helper = $this->get('security.authentication_utils');

        return $this->render(
            'auth/login.html.twig',
            array(
                'last_username' => $helper->getLastUsername(),
                'error'         => $helper->getLastAuthenticationError(),
            )
        );
    }

    /**
     * @Route("/login_check", name="security_login_check")
     */
    public function loginCheckAction()
    {

    }

    /**
     * @Route("/logout", name="logout")
     */
    public function logoutAction()
    {

    }
}

here define the route to login page in loginAction ( ),

when you see this :

$helper = $this->get('security.authentication_utils');

this is actually Added a security error helper, the origin above code is doing follow:

$session = $request->getSession();

    if ($request->attributes->has(SecurityContextInterface::AUTHENTICATION_ERROR)) {
        $error = $request->attributes->get(
            SecurityContextInterface::AUTHENTICATION_ERROR
        );
    } elseif (null !== $session && $session->has(SecurityContextInterface::AUTHENTICATION_ERROR)) {
        $error = $session->get(SecurityContextInterface::AUTHENTICATION_ERROR);
        $session->remove(SecurityContextInterface::AUTHENTICATION_ERROR);
    } else {
        $error = '';
    }

from symfony 2.6 you could reduced code simply by get sercurity.authentication_utils

then render the view and then the login_check route will be hit once a user clicks the login button. A user will be logged out once the logout route has been hit.

Step 4 Create login template view page

go to template/ create auth directory and create a file login.html.twig:

{% extends 'base.html.twig' %}

{% block body %}
    <div class="container">
        <div class="row">
            <div class="col-md-8 col-md-offset-2">
                <div class="panel panel-default">
                    <div class="panel-heading">Login</div>
                    <div class="panel-body">
                        <form class="form-horizontal" role="form" method="POST" action="{{ path('security_login_check') }}">
                            {% if error %}
                                <div class="alert alert-danger">
                                    {{ error.messageKey|trans(error.messageData) }}
                                </div>
                            {% endif %}

                            <div class="form-group">
                                <label for="email" class="col-md-4 control-label">E-Mail Address</label>

                                <div class="col-md-6">
                                    <input id="email" type="email" class="form-control" name="_email" value="{{ last_username }}">
                                </div>
                            </div>

                            <div class="form-group">
                                <label for="password" class="col-md-4 control-label">Password</label>

                                <div class="col-md-6">
                                    <input id="password" type="password" class="form-control" name="_password">
                                </div>
                            </div>

                            <div class="form-group">
                                <div class="col-md-6 col-md-offset-4">
                                    <button type="submit" class="btn btn-primary">
                                        <i class="fa fa-btn fa-sign-in"></i> Login
                                    </button>
                                </div>
                            </div>
                        </form>
                    </div>
                </div>
            </div>
        </div>
    </div>
{% endblock %}

Right here you first create your login page, go to browser and check:

Step5 create RegisterController

go to src/Controller and create file called RegisterController.php

<?php
namespace App\Controller;

use App\Entity\User;
use App\Form\UserType;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;

class RegistrationController extends Controller
{
    /**
     * @Route("/register", name="register")
     */
    public function registerAction(Request $request)
    {
        // Create a new blank user and process the form
        $user = new User();
        $form = $this->createForm(UserType::class, $user);
        $form->handleRequest($request);

        if ($form->isSubmitted() && $form->isValid()) {
            // Encode the new users password
            $encoder = $this->get('security.password_encoder');
            $password = $encoder->encodePassword($user, $user->getPlainPassword());
            $user->setPassword($password);

            // Set their role
            $user->setRole('ROLE_USER');

            // Save
            $em = $this->getDoctrine()->getManager();
            $em->persist($user);
            $em->flush();

            return $this->redirectToRoute('login');
        }

        return $this->render('auth/register.html.twig', [
            'form' => $form->createView(),
        ]);
    }

In this controller we will use User and UserType class:

use App\Entity\User;
use App\Form\UserType;

we will add those later on

inside registerAction ( ) function, it is to handle the form rendering and submission. If the form is submitted, the controller performs the validation and saves the data into the database, those code from symfony register form doc
Handling the Form Submission part

the next step is to render this form. This is done by passing a special form “view” object to your template : ( thank for Form bundles we had download before)

$form->createView()

Step6 create Register page view

go to template/auth and create file called register.html.twig:

{% extends 'base.html.twig' %}

{% block body %}
    <div class="container">
        <div class="row">
            <div class="col-md-8 col-md-offset-2">
                <div class="panel panel-default">
                    <div class="panel-heading">Register</div>
                    <div class="panel-body">
                        {{ form_start(form) }}
                        <div class="form_group">
                            <div class="col-md-8 col-md-offset-2">
                                {{ form_row(form.name, {'attr': {'class': 'form-control'}}) }}
                            </div>
                        </div>

                        <div class="form_group">
                            <div class="col-md-8 col-md-offset-2">
                                {{ form_row(form.email, {'attr': {'class': 'form-control'}}) }}
                            </div>
                        </div>

                        <div class="form_group">
                            <div class="col-md-8 col-md-offset-2">
                                {{ form_row(form.plainPassword.first, {'attr': {'class': 'form-control'}}) }}
                            </div>
                        </div>

                        <div class="form_group">
                            <div class="col-md-8 col-md-offset-2">
                                {{ form_row(form.plainPassword.second, {'attr': {'class': 'form-control'}}) }}
                            </div>
                        </div>

                        <div class="form-group">
                            <div class="col-md-8 col-md-offset-4" style="margin-top:5px;">
                                <button type="submit" class="btn btn-primary">
                                    <i class="fa fa-btn fa-user"></i> Register
                                </button>
                            </div>
                        </div>
                        {{ form_end(form) }}
                    </div>
                </div>
            </div>
        </div>
    </div>
{% endblock %}

here you could see use helper form to defined from the form build class:

{{ form_start(form) }}
{{ form_widget(form) }}
{{ form_end(form) }}

And in order to get the link in burger menu, do the same add login name path in base.html.twig
Create account

Yes, go to your browser:

Right on, so here we had create Account, create Account and login pages

Take a break

github commit code