Symfony: tutorial 3: los modelos y las entidades, almacenando datos en BDs

Symfony tutorial 3 Doctrine

Recorriendo todas las funcionalidades de Symfony Flex (Symfony 4), traigo de nuevo otro HOWTO para trabajar con el guardado de los datos. Symfony basa el guardado de los datos, la persistencia de los datos, en Doctrine. Es decir, tenemos un módulo llamado Doctrine, mejor dicho varios, que te independizan de la base de datos añadiendo una capa intermedia entre los controladores, plantillas, servicios.. de tu aplicación Symfony y la base de datos. En esta capa intermedia se definen lo que se llaman entidades, y Doctrine se encarga de hacer todo.

Siguiendo este tutorial veremos cómo en poco tiempo podemos arrancar proyectos que antes podíamos tardar días o semanas, ahora en pocas horas. Es decir, en muy pocos comandos de consola, y con muy poco esfuerzo, podemos montar grandes estructuras en bases de datos, o editarlas. Y nos podemos casi olvidar de hacer muchas consultas directamente a la Base de Datos.

Un poco de teoría

Symfony es un framework que sigue el modelo MVC que significa Modelo-Vista-Controlador. En un post anterior hemos visto cómo trabajar con los controladores y un poco de las vistas, que también tienen mucho juego. Ahora vamos a ver el tema de los modelos, sinónimo de entidades. Es decir, en este framework la mayoría de los datos, o casi la mayoría, se guardan a base de objetos de PHP que llamamos entidades. Todas estas entidades se representan en objetos de PHP y se graban en Symfony Flex bajo el directorio:

 src/Entity/

Entonces, bajo este directorio tendremos todas las entidades de nuestra aplicación. Simplemente serán unas clases de PHP con sus getters y setters que citábamos antes, pero con unas cuantas anotaciones a modo de comentarios que hacen muchas cosas de forma automática. Esta abstracción de los datos te permitirá listar, almacenar, editar y borrar datos de forma muy fácil y en muy pocas líneas de código. A Doctrine le diremos, “toma estos objetos y guárdalos en la base de datos”, “dame todos los objetos con estas características”, podremos obtener un objeto en concreto, modificarlo y decirle a Doctrine: “guarda este objeto modificado”. Todo esto en muy pocas líneas de código fuente.

Evidentemente, puedes seguir usando el SQL tradicional, pero no es nada recomendable si no es estrictamente necesario. Quiero decir, que a veces Doctrine no alcanza a todo lo que necesitamos, pero es raro que necesitemos saltarnos a Doctrine. Si nos estamos saltando continuamente Doctrine, o no lo estamos usando en nuestras aplicaciones Symfony, da por hecho que algo muy malo estamos haciendo, y nos estamos perdiendo una de las mejores herramientas que proporciona Symfony. No tendría nada de sentido, ya que se supone que queremos ser muy productivos, hacer muchas cosas en muy poco tiempo. Dejemos a Doctrine el trabajo duro con la base de datos y centrémonos en los especial de nuestra aplicación web.

Creando el proyecto del ejemplo

En nuestro directorio de pruebas, simplemente creamos el nuevo proyecto Symfony ejecutando lo siguiente:

composer create-project symfony/skeleton tutorial-3

..y esperamos. Si todo ha ido bien ya tenemos un directorio nuevo tutorial-3 con el esqueleto de Symfony Flex listo para trabajar.

symfony-tutorial-3-creando-proyecto

Vamos a añadir los módulos en Symfony que vamos a usar en este proyecto ejecutando:

cd tutorial-3
composer require maker doctrine server

Creando unas entidades

A partir de ahora, olvídate de trabajar directamente los ficheros de entidades. Muy pocas veces es necesario programar estos ficheros directamente. Como ya decía anteriormente, si quieres ser productivo, debes usar de nuevo los comandos de consola. De esta forma, vamos a crear un prototipo de aplicación web de gestión de pedidos. Reduciremos todo a las siguientes entidades:

Cliente con dni, email y nombre.
Producto con referencia, nombre y precio.
Pedido con referencia, que va a ser de un cliente y va a tener unos productos.

EER ClientePedidoProducto

Este ejemplo se podría extender a un eCommerce, un marketplace, o un complejo ERP, no hay límites y la forma de trabajarlo siempre es la misma. Simplemente tenemos clientes que hacen pedidos y estos pedidos tienen productos. Un producto puede haberse pedido N veces, y un pedido puede tener N productos. En este caso un cliente puede hacer N pedidos y cada pedido sólo corresponde a 1 único cliente.

Para esto ejecutamos los siguientes comandos, siguiendo las instrucciones que se indican por pantalla:

php bin/console make:entity Cliente

..y creamos 3 campos de tipo string simplemente siguiendo las instrucciones. Ejecutamos:

php bin/console make:entity Pedido

..y creamos sólo un campo de tipo string para la referencia de pedido, luego vemos cómo relacionamos los campos. Ahora nos queda hacer lo mismo:

php bin/console make:entity Producto

..y creamos el campo referencia y nombre de tipo string, y el campo precio de tipo decimal. Conviene crear y borrar las entidades que se van creando en el directorio src/Entity/* sin miedo. Podemos borrar estos ficheros cuantas veces queramos cuando estamos empezando un proyecto. Conviene jugar mucho con esto, borrando los ficheros creados, viendo lo que se crea, para coger práctica. Además el maker de Symfony también te permite modificar las entidades ya creadas, no tienes que crearlas con todos los campos de una vez. Y si no tienes soltura con esto te conviene coger mucha soltura, ya que te va a ahorrar horas y horas comparando con los métodos tradicionales de hacer esto.

Para modificar una entidad que existe, sólo tenemos que volver a lanzar el comando de crear sobre la misma entidad. Symfony detectará que ya existe, y en vez de crearla de nuevo, la modificará.

Guardando esta estructura en la base de datos

Ya tenemos unos modelos hechos, que nos van a permitir trabajar con pedidos de clientes que tienen ciertos productos en PHP. Querremos guardar toda esta información en la base de datos, pero antes deben crearse las tablas y la base de datos.

Y otra vez, no crees la base de datos manualmente, Symfony también se encarga de esto. Antes de esto, configuramos el fichero .env y ponemos la base de datos que estemos usando, en mi caso es esta:

DATABASE_URL=mysql://root@127.0.0.1:3306/tutorial3

..y ejecutamos para crear la base datos:

php bin/console doctrine:database:create

..luego creamos las tablas con:

php bin/console doctrine:schema:create

..a continuación, cada vez que querramos actualizar las tablas, tendremos que ejecutar esto en el entorno de desarrollo:

php bin/console doctrine:schema:update --dump-sql [--force]

Si estamos seguros de la modificación, le pondremo el force. ¡OJO! no es la forma correcta, pero para desarrollar nos sirve. Lo ideal es usar el nuevo módulo de Doctrine Migrations que ha sido integrado dentro del pack. Este Doctrine Migrations se encarga de preparar ficheros de actualización de la base de datos, pudiendo incluso volver a atrás los pasos a producción, o añadiendo tratamientos sobre los datos de producción si fuera necesario. Esto tiene mucho juego, y en el día a día de una aplicación web en producción es muy importante, así que de nuevo, tenemos a Symfony haciéndonos la vida mucho más fácil 😉

Añadiendo relaciones entre entidades

Hasta aquí tenemos 3 entidades no relacionadas que guardan unos datos. Es decir, tenemos en la base de datos 3 tablas con ciertas columnas. Pero no hemos relacionado pedidos con los clientes, ni pedidos con los productos. Esta relación tradicionalmente se haría a base de claves ajenas.

Ahora bien, en Symfony de nuevo, gracias a Doctrine, vamos a ahorrarnos este trabajo. Vamos a editar la entidad Pedido y le vamos a añadir 2 campos nuevos: cliente y productos. Estos campos van a relacionar, y además bidireccionalmente, un cliente con todos sus pedidos, un pedido con su cliente, un pedido con sus productos, y también, un producto con todos sus pedidos. ¿Mucho mejor que con SQL tradicional verdad?

php bin/console make:entity Pedido

Doctrine make entity relation

Fíjate en la imagen, estoy editando Pedido, añadiendo el campo cliente, que relaciona un pedido con su cliente. Siguiendo las instrucciones, tenemos una relación ManyToOne, porque un pedido sólo es de un cliente, y un cliente puede tener varios pedidos. Recordemos la definición de la base de datos en el EER:

EER ClientePedidoProducto

Terminando esto, tendremos creadas las siguientes funciones en PHP:

// dado un cliente obtenemos sus pedidos
$pedidos = $cliente->getPedidos();
// dado un pedido obtenemos el cliente asociado
$cliente = $pedido->getCliente();
// dado un pedido obtenemos sus productos
$productos = $pedido->getProductos();
// dado un producto obtenemos los pedidos que se han hecho
$pedidos = $product->getPedidos();

..con esto podremos navegar por los datos, haciendo lo que necesitemos. Todo esto sin escribir ninguna engorrosa consulta en lenguaje de la base de datos. Hacemos la otra relación de igual manera entre Pedido <-> Producto y lanzamos esto para actualizar la base de datos:

php bin/console doctrine:schema:update --dump-sql --force

Almacenando datos

Esto es todo lo necesario para crear toda la estructura de la base de datos en un proyecto Symfony. Para terminar sólo nos queda guardar unos datos en la base de datos, a modo de comprobación. Si estas siguiendo todo el tutorial, vamos a hacer un controlador que simplemente guarde unos datos en la base de datos. Y si mirando el contenido de la base de datos, ves los datos guardados, es que ¡has conseguido aprender los fundamentos de Doctrine!

Creando el controlador:

php bin/console make:controller home

Abrimos el fichero src/Controller/HomeController.php y ponemos lo siguiente:

<?php

namespace App\Controller;

use Symfony\Component\Routing\Annotation\Route;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use App\Entity\Cliente;
use App\Entity\Pedido;
use App\Entity\Producto;

class HomeController extends Controller
{
    /**
    * @Route("/home", name="home")
    */
    public function index()
    {
        $entityManager = $this->getDoctrine()->getManager();

        $cliente = new Cliente();
        $cliente->setDni('DNI1');
        $cliente->setNombre('NombreCliente1');
        $cliente->setEmail('email1@email1.com');
        $entityManager->persist($cliente);

        $pedido = new Pedido();
        $pedido->setCliente($cliente);
        $pedido->setReferencia('ReferenciaPedido1');
        $entityManager->persist($pedido);

        $product = new Producto();
        $product->setReferencia('ReferenciaProducto1');
        $product->setNombre('NombreProducto1');
        $product->setPrecio(123.12);
        $entityManager->persist($product);

        $entityManager->flush();

        return $this->json([
            'message' => 'Welcome to your new controller!',
            'path' => 'src/Controller/HomeController.php',
        ]);
    }
}

Marco en negrita las líneas que he añadido. A continuación ejecutamos desde línea de comandos lo siguiente para arrancar el servidor de desarrollo:

php bin/console server:start

Y vamos con el navegador a la siguiente URL:

http://localhost:8000/home

Esto es todo, ya sólo queda ir a la base de datos a ver si realmente tenemos los datos de prueba generados en el controlador.

Symfony 4 tutorial 3 Doctrine base de datos

Espero haberme explicado bien, que menudo testamento me está quedando 😀 sino un comentario abajo y contesto gustoso a cualquier pregunta sobre esto. De todas formas, esto no es más que el comienzo de la persistencia de los datos con Doctrine. De verdad que Doctrine es muy grande, es una joya de la programación PHP, así que trataré de traer a menudo code-katas relacionados.

¡Un saludo!

Compartir..

Dejar un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *