
¿Qué aprenderemos?
- A crear un panel de control para nuestras entidades
- A usar controladores embebidos
- A usar servicios
2.1 Panel de control
Vamos a crear un panel de control para nuestra aplicación.
Para ello usaremos el bundle EasyAdmin de Symfony.
1
composer require easycorp/easyadmin-bundle
Una vez instalado vamos a crear nuestro Dashboard
1
php bin/console make:admin:dashboard
Que creará una nueva ruta admin y un nuevo controlador DashboardController
Si visitamos la ruta http://127.0.0.1:8080/admin verás la página de inicio

Si aparace un error avisando de que no encuentra el controlador, limpia la caché mediante el comando
php bin/console cache:clear
2.2 Team
Primero configuramos la conexión en el archivo .env
1
DATABASE_URL=mysql://root:sa@127.0.0.1:3306/tienda
Y creamos la base de datos:
1
php bin/console doctrine:database:create
Vamos a empezar con los miembros del equipo

El primer paso es crear la entidad Team.
Y generar la migración:
1
2
php bin/console make:migration
php bin/console doctrine:migrations:migrate
Y luego generar el controlador CRUD
CRUD son las iniciales de Create, Read, Update y Delete que son las operaciones básicas que se realizan con una entidad
Y por último, modificar DashboardController para que cargue por defecto el panel de control de la entidad Team
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// Otros use
use EasyCorp\Bundle\EasyAdminBundle\Router\AdminUrlGenerator;
#[Route('/admin', name: 'admin')]
public function index(): Response
{
$adminUrlGenerator = $this->container->get(AdminUrlGenerator::class);
return $this->redirect($adminUrlGenerator->setController(TeamCrudController::class)->generateUrl());
}
public function configureMenuItems(): iterable
{
yield MenuItem::linkToDashboard('Dashboard', 'fa fa-home');
// Añadimos al menú el enlace al crud de Team
yield MenuItem::linkToCrud('Team', 'fas fa-list', Team::class);
}

Ahora ya podemos añadir miembros al equipo.

Pero claro, EasyAdmin no sabe que el campo Photo debe ser de tipo File
Así que configuramos TeamCrudController para modificar los campos:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<?php
namespace App\Controller\Admin;
use App\Entity\Team;
use EasyCorp\Bundle\EasyAdminBundle\Controller\AbstractCrudController;
use EasyCorp\Bundle\EasyAdminBundle\Field\Field;
use EasyCorp\Bundle\EasyAdminBundle\Field\ImageField;
class TeamCrudController extends AbstractCrudController
{
public static function getEntityFqcn(): string
{
return Team::class;
}
public function configureFields(string $pageName): iterable
{
return [
Field::new('name'),
ImageField::new('photo')->setUploadDir('/public/img')->setBasePath('/img/'),
Field::new('designation')
];
}
}
Lo estamos configurando para que los campos name y designation sean de tipo Field (campo de texto) y el campo photo sea de tipo ImageField y además configuramos el directorio de subida y cuál es la ruta de la carpeta.
Ahora ya podemos subir una foto:

Ahora vamos a subir todas las imágenes del equipo a la base de datos mediante el siguiente sql:
1
2
3
4
5
6
INSERT INTO `team` (`id`, `name`, `photo`, `designation`) VALUES
(1, 'Team 1', '/team-1.jpg', 'Designation'),
(2, 'Team 2', '/team-2.jpg', 'Designation'),
(3, 'Team 3', '/team-3.jpg', 'Designation'),
(4, 'Team 4', '/team-4.jpg', 'Designation'),
(5, 'Team 5', '/team-5.jpg', 'Designation');

Ya sólo nos queda hacer el partial para mostrar a los miembros del equipo en la ruta index.
Pasamos todo el código html a una plantilla llamada partials/_team.html.twig y en la vista index.html.twig la incluimos.
Ya solo queda conectarla con la base de datos.
Primero modificamos el controlador de la ruta index
1
2
3
4
5
6
7
#[Route('/', name: 'index')]
public function index(ManagerRegistry $doctrine): Response
{
$repository = $doctrine->getRepository(Team::class);
$team = $repository->findAll();
return $this->render('page/index.html.twig', compact('team'));
}
Creamos el partial para el miembro del equipo:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<div class="team-item">
<div class="position-relative overflow-hidden">
<img class="img-fluid w-100" src="{{ asset('img/' ~ teamMember.photo) }}" alt="">
<div class="team-overlay">
<div class="d-flex align-items-center justify-content-start">
<a class="btn btn-light btn-square mx-1" href="#"><i class="bi bi-twitter"></i></a>
<a class="btn btn-light btn-square mx-1" href="#"><i class="bi bi-facebook"></i></a>
<a class="btn btn-light btn-square mx-1" href="#"><i class="bi bi-linkedin"></i></a>
</div>
</div>
</div>
<div class="bg-light text-center p-4">
<h5 class="text-uppercase">{{ teamMember.name }}</h5>
<p class="m-0">{{ teamMember.designation }}</p>
</div>
</div>
Y ahora modificamos _team.html.twig para recorrer los miembros del equipo:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<!-- Team Start -->
<div class="container-fluid py-5">
<div class="container">
<div class="border-start border-5 border-primary ps-5 mb-5" style="max-width: 600px;">
<h6 class="text-primary text-uppercase">Team Members</h6>
<h1 class="display-5 text-uppercase mb-0">Qualified Pets Care Professionals</h1>
</div>
<div class="owl-carousel team-carousel position-relative" style="padding-right: 25px;">
{% for teamMember in team %}
{{include('partials/_teamMember.html.twig', {'teamMember': teamMember})}}
{% endfor %}
</div>
</div>
</div>
<!-- Team End -->
Reto
Haz lo mismo en la página
about
2.3 Product
Reto
Crea el panel de control para administrar los productos. La entidad
Productdebe tener los atributosname,photoyprice, este último de tipo float
Una vez creada la entidad, carga el siguiente SQL con los productos:
1
2
3
4
5
6
INSERT INTO `product` (`id`, `name`, `photo`, `price`) VALUES
(1, 'Producto 1', 'product-1.png', 12.45),
(2, 'Producto 2', 'product-2.png', 100),
(3, 'Producto 3', 'product-3.png', 20),
(4, 'Producto 4', 'product-4.png', 50),
(5, 'Producto 5', 'product-3.png', 34);
Y ahora crea el partial para los productos que incluirás en la ruta product e index

En este caso vemos hemos de cargar los productos en dos rutas (index y product). Y un pricipio básico de la programación es DRY (Don’t Repeat Yourself)
Para no repetir código, vamos a crear un servicio:
Your application is full of useful objects: a “Mailer” object might help you send emails while another object might help you save things to the database. Almost everything that your app “does” is actually done by one of these objects. And each time you install a new bundle, you get access to even more!
In Symfony, these useful objects are called services and each service lives inside a very special object called the service container. The container allows you to centralize the way objects are constructed. It makes your life easier, promotes a strong architecture and is super fast!
Un servicio es una forma de compartir código entre varias partes de la aplicación. Para usarlo sólo hay que hacer type-hinting y dejar que Symfony nos lo inyecte (Dependency Injection Principle). Vamos a verlo:
-
Creamos el servicio en
App\Service\ProductsService1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
<?php namespace App\Service; use App\Entity\Product; use Doctrine\Persistence\ManagerRegistry; class ProductsService{ private $doctrine; public function __construct(ManagerRegistry $doctrine) { //Como hace falta acceder a ManagerRegistry lo inyectamos en el constructor $this->doctrine = $doctrine; } public function getProducts(): ?array{ $repository = $this->doctrine->getRepository(Product::class); return $repository->findAll(); } }
-
Y ahora lo inyectamos en todos aquellos métodos en que queramos usarlo.
1 2 3 4 5 6
#[Route('/product', name: 'product')] public function product(ProductsService $productsService): Response { $products = $productsService->getProducts(); return $this->render('product/product.html.twig', compact('products')); }
Reto
Haz lo mismo para la ruta
/