Flag of Ukraine
SymfonyCasts stands united with the people of Ukraine

Formulario de registro

Keep on Learning!

If you liked what you've learned so far, dive in!
Subscribe to get access to this tutorial plus
video, code and script downloads.

Start your All-Access Pass
Buy just this tutorial for $12.00

With a Subscription, click any sentence in the script to jump to that part of the video!

Login Subscribe

Vamos a añadir un formulario de registro a nuestro sitio. Los formularios de registro tienen algo curioso: ¡no tienen básicamente nada que ver con la seguridad! Piénsalo: el objetivo de un formulario de registro es simplemente insertar nuevos usuarios en la base de datos. Así que crear un formulario de registro no es realmente diferente de crear un formulario para insertar cualquier dato en tu base de datos.

Y para simplificar aún más las cosas, vamos a hacer trampa... generando código. Busca tu terminal y ejecuta:

symfony console make:registration-form

¡Ooh! ¡Esto nos da un error! Dice:

Faltan paquetes: ejecuta composer require form validator

En esta serie de Symfony 5, no hemos hablado del componente Formulario. Y eso es en parte porque no ha cambiado mucho desde nuestro tutorial de Symfony 4. No vamos a entrar en demasiados detalles sobre él ahora, pero lo necesitamos para ejecutar este comando. Así que vamos a instalar ambos paquetes:

composer require form validator

Genial. Cuando termine, ejecuta

symfony console make:registration-form

de nuevo. ¡Genial! Así que la primera pregunta es:

¿Queremos añadir una anotación de validación @UniqueEntity a nuestra clase User para asegurarnos de que no se crean cuentas duplicadas.

Casi seguro que quieres decir "Sí" para que el usuario reciba un error de validación si introduce un correo electrónico que ya está cogido.

Siguiente:

¿Quieres enviar un correo electrónico para verificar la dirección de correo electrónico del usuario después del registro?

Esto lo añadiremos más adelante, pero quiero hacerlo manualmente. Entonces di "No".

¿Quieres autentificar automáticamente al usuario tras el registro?

Eso suena genial, pero di "No", porque también vamos a hacerlo manualmente. Lo sé, ¡nos estoy haciendo trabajar! La última pregunta es:

¿A qué ruta debe ser redirigido el usuario tras el registro?

Vamos a utilizar nuestra ruta de la página de inicio. Así que esa es la número 16 para mí. Y... ¡listo!

Comprobando el código generado

Este comando acaba de darnos un RegistrationController, un tipo de formulario, y una plantilla que renderiza ese formulario. Vamos a... ¡comprobarlo!

Empieza con el controlador: src/Controller/RegistrationController.php:

... lines 1 - 12
class RegistrationController extends AbstractController
{
/**
* @Route("/register", name="app_register")
*/
public function register(Request $request, UserPasswordHasherInterface $userPasswordHasher): Response
{
$user = new User();
$form = $this->createForm(RegistrationFormType::class, $user);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
// encode the plain password
$user->setPassword(
$userPasswordHasher->hashPassword(
$user,
$form->get('plainPassword')->getData()
)
);
$entityManager = $this->getDoctrine()->getManager();
$entityManager->persist($user);
$entityManager->flush();
// do anything else you need here, like send an email
return $this->redirectToRoute('app_homepage');
}
return $this->render('registration/register.html.twig', [
'registrationForm' => $form->createView(),
]);
}
}

De nuevo, no vamos a hablar mucho del componente Formulario. Pero, a grandes rasgos, este controlador crea un objeto User y luego, al enviarlo, hace un hash de la contraseña simple que se ha enviado y luego guarda el User. Esto es exactamente lo mismo que estamos haciendo en nuestros accesorios para crear usuarios: no hay nada especial en esto.

Arreglar el estilo del formulario

Así que... ¡vamos a ver qué aspecto tiene esto! Dirígete a /register para ver... ¡el formulario más feo del mundo! Nosotros... podemos hacerlo mejor. La plantilla de esta página esregistration/register.html.twig. Ábrela

{% extends 'base.html.twig' %}
{% block title %}Register{% endblock %}
{% block body %}
<h1>Register</h1>
{{ form_start(registrationForm) }}
{{ form_row(registrationForm.email) }}
{{ form_row(registrationForm.plainPassword, {
label: 'Password'
}) }}
{{ form_row(registrationForm.agreeTerms) }}
<button type="submit" class="btn">Register</button>
{{ form_end(registrationForm) }}
{% endblock %}

y... Voy a añadir un par de divs para darle más estructura. Genial... y luego indentar todo este material del formulario para que esté dentro de ellos... y luego sólo necesitamos 3 divs de cierre en la parte inferior:

... lines 1 - 4
{% block body %}
<div class="container">
<div class="row">
<div class="bg-light mt-4 p-4">
<h1>Register</h1>
{{ form_start(registrationForm) }}
{{ form_row(registrationForm.email) }}
{{ form_row(registrationForm.plainPassword, {
label: 'Password'
}) }}
{{ form_row(registrationForm.agreeTerms) }}
<button type="submit" class="btn">Register</button>
{{ form_end(registrationForm) }}
</div>
</div>
</div>
{% endblock %}

Genial. Eso no arregla realmente el formulario... pero al menos nuestro feo formulario aparece más o menos en el centro de la página. Oh, pero déjame arreglar mi error tipográfico en el mt-4. Y... sí, eso se ve mejor.

Para arreglar el formulario en sí, podemos decirle a Symfony que haga salir el formulario con un marcado que sea compatible con Bootstrap 5. Esto es... una especie de tema para el tutorial del formulario, pero es fácil. Ve a config/packages/twig.yaml. Aquí, añade una opción llamada form_themescon un nuevo elemento: boostrap_5_layout.html.twig:

twig:
default_path: '%kernel.project_dir%/templates'
form_themes:
- bootstrap_5_layout.html.twig
... lines 6 - 9

Pruébalo ahora y... ¡woh! ¡Eso ha supuesto una gran diferencia! Oh, pero déjame añadir una clase más a ese botón de registro... para que no sea invisible: btn-primary:

... lines 1 - 4
{% block body %}
<div class="container">
<div class="row">
<div class="bg-light mt-4 p-4">
... lines 9 - 10
{{ form_start(registrationForm) }}
... lines 12 - 17
<button type="submit" class="btn btn-primary">Register</button>
{{ form_end(registrationForm) }}
</div>
</div>
</div>
{% endblock %}

Genial.

Y mientras hacemos que las cosas se vean y funcionen bien, por fin podemos hacer que el botón "Regístrate"... vaya realmente a alguna parte. En base.html.twig, busca "Apúntate" - aquí está - pon el href en path() y apunta a la nueva ruta, que... si nos fijamos... se llama app_register:

... lines 1 - 12
class RegistrationController extends AbstractController
{
/**
* @Route("/register", name="app_register")
*/
public function register(Request $request, UserPasswordHasherInterface $userPasswordHasher): Response
{
... lines 20 - 43
}
}

Así que path('app_register'):

... line 1
<html>
... lines 3 - 14
<body
... lines 16 - 21
<nav
class="navbar navbar-expand-lg navbar-light bg-light px-1"
{{ is_granted('ROLE_PREVIOUS_ADMIN') ? 'style="background-color: red !important"' }}
>
<div class="container-fluid">
... lines 27 - 35
<div class="collapse navbar-collapse" id="navbar-collapsable">
... lines 37 - 47
{% if is_granted('IS_AUTHENTICATED_REMEMBERED') %}
... lines 49 - 73
{% else %}
... line 75
<a href="{{ path('app_register') }}" class="btn btn-dark">Sign up</a>
{% endif %}
</div>
</div>
</nav>
... lines 81 - 85
</body>
</html>

¡Genial!

Esto funcionará ahora si lo probamos. Pero, antes de hacerlo, quiero añadir otra característica a esto. Después de enviar con éxito el formulario de registro, quiero autentificar automáticamente al usuario. ¿Es posible? Por supuesto Hagámoslo a continuación.

Leave a comment!

¡Este tutorial también funciona muy bien para Symfony 6!

What PHP libraries does this tutorial use?

// composer.json
{
    "require": {
        "php": "^7.4.1 || ^8.0.0",
        "ext-ctype": "*",
        "ext-iconv": "*",
        "babdev/pagerfanta-bundle": "^3.3", // v3.3.0
        "composer/package-versions-deprecated": "^1.11", // 1.11.99.4
        "doctrine/annotations": "^1.0", // 1.13.2
        "doctrine/doctrine-bundle": "^2.1", // 2.6.3
        "doctrine/doctrine-migrations-bundle": "^3.0", // 3.1.1
        "doctrine/orm": "^2.7", // 2.10.1
        "knplabs/knp-markdown-bundle": "^1.8", // 1.9.0
        "knplabs/knp-time-bundle": "^1.11", // v1.16.1
        "pagerfanta/doctrine-orm-adapter": "^3.3", // v3.3.0
        "pagerfanta/twig": "^3.3", // v3.3.0
        "phpdocumentor/reflection-docblock": "^5.2", // 5.2.2
        "scheb/2fa-bundle": "^5.12", // v5.12.1
        "scheb/2fa-qr-code": "^5.12", // v5.12.1
        "scheb/2fa-totp": "^5.12", // v5.12.1
        "sensio/framework-extra-bundle": "^6.0", // v6.2.0
        "stof/doctrine-extensions-bundle": "^1.4", // v1.6.0
        "symfony/asset": "5.3.*", // v5.3.4
        "symfony/console": "5.3.*", // v5.3.7
        "symfony/dotenv": "5.3.*", // v5.3.8
        "symfony/flex": "^1.3.1", // v1.17.5
        "symfony/form": "5.3.*", // v5.3.8
        "symfony/framework-bundle": "5.3.*", // v5.3.8
        "symfony/monolog-bundle": "^3.0", // v3.7.0
        "symfony/property-access": "5.3.*", // v5.3.8
        "symfony/property-info": "5.3.*", // v5.3.8
        "symfony/rate-limiter": "5.3.*", // v5.3.4
        "symfony/runtime": "5.3.*", // v5.3.4
        "symfony/security-bundle": "5.3.*", // v5.3.8
        "symfony/serializer": "5.3.*", // v5.3.8
        "symfony/stopwatch": "5.3.*", // v5.3.4
        "symfony/twig-bundle": "5.3.*", // v5.3.4
        "symfony/ux-chartjs": "^1.3", // v1.3.0
        "symfony/validator": "5.3.*", // v5.3.8
        "symfony/webpack-encore-bundle": "^1.7", // v1.12.0
        "symfony/yaml": "5.3.*", // v5.3.6
        "symfonycasts/verify-email-bundle": "^1.5", // v1.5.0
        "twig/extra-bundle": "^2.12|^3.0", // v3.3.3
        "twig/string-extra": "^3.3", // v3.3.3
        "twig/twig": "^2.12|^3.0" // v3.3.3
    },
    "require-dev": {
        "doctrine/doctrine-fixtures-bundle": "^3.3", // 3.4.0
        "symfony/debug-bundle": "5.3.*", // v5.3.4
        "symfony/maker-bundle": "^1.15", // v1.34.0
        "symfony/var-dumper": "5.3.*", // v5.3.8
        "symfony/web-profiler-bundle": "5.3.*", // v5.3.8
        "zenstruck/foundry": "^1.1" // v1.13.3
    }
}