Flag of Ukraine
SymfonyCasts stands united with the people of Ukraine

MakerBundle y Autoconfiguración

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

¡Felicidades, equipo! ¡Hemos terminado con lo más pesado de este tutorial! Así que es hora de dar la vuelta de la victoria. Vamos a instalar uno de mis bundles favoritos de Symfony: MakerBundle. Busca tu terminal y ejecuta:

composer require maker --dev

En este caso, estoy usando la bandera --dev porque se trata de una utilidad de generación de código que sólo necesitamos localmente, no en producción.

Este bundle, por supuesto, proporciona servicios. Pero estos servicios no están pensados para que los utilicemos directamente. En su lugar, todos los servicios de este bundle potencian un montón de nuevos comandos de bin/console. Ejecuta

php bin/console

y busca la sección make. Ooh. Aquí hay un montón de cosas para configurar la seguridad, generar entidades de doctrina para la base de datos (lo que haremos en el siguiente tutorial), hacer un CRUD, y mucho más.

Generar una nueva clase de mando

Vamos a probar una: ¿qué tal si intentamos construir nuestro propio y nuevo comando de consola personalizado que aparecerá en esta lista? Para ello, ejecuta:

php bin/console make:command

Esto te pedirá interactivamente el nombre del comando. Digamosapp:talk-to-me. No es necesario, pero es bastante habitual anteponer a tus comandos personalizados el prefijo app:. Y... ¡listo!

Eso ha creado exactamente un nuevo archivo: src/Command/TalkToMeCommand.php. Vamos a abrirlo:

<?php
namespace App\Command;
use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;
#[AsCommand(
name: 'app:talk-to-me',
description: 'Add a short description for your command',
)]
class TalkToMeCommand extends Command
{
protected function configure(): void
{
$this
->addArgument('arg1', InputArgument::OPTIONAL, 'Argument description')
->addOption('option1', null, InputOption::VALUE_NONE, 'Option description')
;
}
protected function execute(InputInterface $input, OutputInterface $output): int
{
$io = new SymfonyStyle($input, $output);
$arg1 = $input->getArgument('arg1');
if ($arg1) {
$io->note(sprintf('You passed an argument: %s', $arg1));
}
if ($input->getOption('option1')) {
// ...
}
$io->success('You have a new command! Now make it your own! Pass --help to see your options.');
return Command::SUCCESS;
}
}

¡Genial! ¡Arriba, puedes ver que el nombre y la descripción del comando se hacen en un atributo PHP! Luego, abajo en este método configure(), del que hablaremos más en un minuto, podemos configurar los argumentos y opciones que se pueden pasar desde la línea de comandos.

Cuando ejecutemos el comando, se llamará a execute()... donde podemos imprimir cosas en la pantalla o leer opciones y argumentos.

Quizá lo mejor de esta clase es que... ya funciona. ¡Compruébalo! De vuelta a tu terminal, ejecuta;

php bin/console app:talk-to-me

Y... ¡está vivo! No hace mucho, pero esta salida viene de aquí abajo. ¡Guau!

Autoconfiguración: Descubriendo automáticamente los "plugins"

Pero espera... ¿cómo ha visto Symfony instantáneamente nuestra nueva clase Command y ha sabido que debe empezar a utilizarla? ¿Es porque vive en el directorio src/Command/... y Symfony escanea las clases que viven aquí? No Podríamos cambiar el nombre de este directorio aThereAreDefinitelyNoCommandsInHere... y Symfony seguiría viendo el comando.

La forma en que esto funciona es mucho más genial. Abre config/services.yaml y mira la sección _defaults:

... lines 1 - 12
services:
# default configuration for services in *this* file
_defaults:
autowire: true # Automatically injects dependencies in your services.
autoconfigure: true # Automatically registers your services as commands, event subscribers, etc.
... lines 18 - 32

Hemos hablado de lo que significa autowire: true, pero no he explicado el propósito deautoconfigure: true. Como está por debajo de _defaults, la autoconfiguración está activa en todos nuestros servicios, incluido nuestro nuevo servicio TalkToMeCommand. Cuando autoconfiguration está activado, básicamente le dice a Symfony:

Oye, por favor, mira la clase base o la interfaz de cada servicio, y si parece que una clase debe ser un comando de consola... o un suscriptor de eventos... o cualquier otra clase que se enganche a una parte de Symfony, por favor, integra automáticamente integra el servicio en ese sistema. Bien, gracias. ¡Adiós!

¡Si! Symfony ve que nuestra clase extiende Command y piensa:

Hmm, puede que no sea una IA autoconsciente... pero apuesto a que esto es un comando. Será mejor que se lo notifique al ¡> al sistema de la consola sobre ello!

Me encanta la autoconfiguración. Significa que podemos crear una clase PHP, extender cualquier clase base o implementar cualquier interfaz necesaria para la "cosa" que estamos construyendo, y... simplemente funcionará.

Internamente, si quieres conocer todos los detalles frikis, la autoconfiguración añade una etiqueta a tu servicio, como console.command, que es lo que, en última instancia, ayuda a que el sistema de la consola se fije en él.

Muy bien, ahora que nuestro comando funciona, vamos a divertirnos un poco y a personalizarlo a continuación.

Leave a comment!

1
Login or Register to join the conversation
Rufnex Avatar

Great one, thanks ;o)

Reply
Cat in space

"Houston: no signs of life"
Start the conversation!

What PHP libraries does this tutorial use?

// composer.json
{
    "require": {
        "php": ">=8.1",
        "ext-ctype": "*",
        "ext-iconv": "*",
        "knplabs/knp-time-bundle": "^1.18", // v1.19.0
        "symfony/asset": "6.1.*", // v6.1.0-RC1
        "symfony/console": "6.1.*", // v6.1.0-RC1
        "symfony/dotenv": "6.1.*", // v6.1.0-RC1
        "symfony/flex": "^2", // v2.1.8
        "symfony/framework-bundle": "6.1.*", // v6.1.0-RC1
        "symfony/http-client": "6.1.*", // v6.1.0-RC1
        "symfony/monolog-bundle": "^3.0", // v3.8.0
        "symfony/runtime": "6.1.*", // v6.1.0-RC1
        "symfony/twig-bundle": "6.1.*", // v6.1.0-RC1
        "symfony/ux-turbo": "^2.0", // v2.1.1
        "symfony/webpack-encore-bundle": "^1.13", // v1.14.1
        "symfony/yaml": "6.1.*", // v6.1.0-RC1
        "twig/extra-bundle": "^2.12|^3.0", // v3.4.0
        "twig/twig": "^2.12|^3.0" // v3.4.0
    },
    "require-dev": {
        "symfony/debug-bundle": "6.1.*", // v6.1.0-RC1
        "symfony/maker-bundle": "^1.41", // v1.42.0
        "symfony/stopwatch": "6.1.*", // v6.1.0-RC1
        "symfony/web-profiler-bundle": "6.1.*" // v6.1.0-RC1
    }
}