This course is still being released! Check back later for more chapters.

Get Notified About this Course!

We will send you messages regarding this course only
and nothing else, we promise.
You can unsubscribe anytime by emailing us at:
privacy@symfonycasts.com
Login to bookmark this video
Buy Access to Course
05.

Contenedor e Iterador con ServiceCollectionInterface

|

Share this awesome video!

|

Keep on Learning!

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

Login Subscribe

En el capítulo anterior, hicimos que estos botones se listaran mediante programación. Pero al hacerlo, ¡rompimos la funcionalidad real de pulsar botones! Los niños se están poniendo inquietos: tenemos que arreglar esto.

En ButtonRemote, hay un par de formas de solucionarlo. El primer enfoque, que probablemente sea el más sencillo, consiste en inyectar dos argumentos: uno que sea un iterador de los servicios del botón y otro que sea un localizador, es decir, un minicontenedor con un método get() para obtener cada servicio. Eso funcionaría y es perfectamente válido. ¡Pero podemos hacerlo mejor!

ServiceCollectionInterface

Podemos inyectar un objeto que sea a la vez un iterador y un localizador:ServiceCollectionInterface. Echémosle un vistazo. Esto es un ServiceProviderInterface (que es el localizador) y un IteratorAggregate(que es el iterador). Por si fuera poco, también es Countable.

De vuelta a ButtonRemote, tenemos que cambiar AutowireIterator porAutowireLocator para que Symfony inyecte ServiceCollectionInterface:

32 lines | src/Remote/ButtonRemote.php
// ... lines 1 - 8
final class ButtonRemote
{
public function __construct(
#[AutowireLocator(ButtonInterface::class, indexAttribute: 'key')]
private ServiceCollectionInterface $buttons,
) {
}
// ... lines 16 - 30
}

Limpiaré aquí algunas importaciones no utilizadas y... bien.

De vuelta en nuestra app, refrescar y... Vale, seguimos listando los botones, así que es buena señal. Ahora, si pulsamos un botón... ¡parece que vuelve a funcionar! Entra en el perfilador para comprobar la petición POST y ver que se sigue llamando a la lógica adecuada del botón. ¡Genial!

Pereza

Una de las grandes ventajas de un localizador de servicios es que es perezoso. Los servicios no se instancian hasta que llamamos a get() para obtenerlos. E incluso entonces, sólo se crea un único servicio, aunque nos volvamos locos y llamemos a get() para el mismo servicio un montón de veces.

Me encanta ser perezoso, pero tenemos un problema. Aquí abajo, en buttons(), estamos iterando sobre todos los botones. Esto está forzando la instanciación de todos los servicios de botones sólo para obtener sus $name's. Como sólo nos interesan los nombres, ¡esto es un desperdicio!

ServiceCollectionInterface::getProvidedServices()

ServiceCollectionInterface ¡al rescate! Los localizadores de servicios Symfony tienen un método especial llamado getProvidedServices(). Elimina todo este código ydd($this->buttons->getProvidedServices()) para ver qué devuelve:

34 lines | src/Remote/ButtonRemote.php
// ... lines 1 - 8
final class ButtonRemote
{
// ... lines 11 - 24
public function buttons(): iterable
{
dd($this->buttons->getProvidedServices());
// ... lines 28 - 31
}
}

Vuelve a nuestra aplicación y actualízala. Esto parece casi idéntico al mapeo manual que utilizamos anteriormente con #[AutowireLocator].

Queremos las claves de esta matriz. De vuelta aquí, devuelve array_keys() de$this->buttons->getProvidedServices():

30 lines | src/Remote/ButtonRemote.php
// ... lines 1 - 8
final class ButtonRemote
{
// ... lines 11 - 24
public function buttons(): iterable
{
return array_keys($this->buttons->getProvidedServices());
}
}

Vuelve a la app y... actualiza. Todo sigue funcionando y, entre bastidores, ya no estamos instanciando todos los servicios de botones.

¡Rendimiento ganado!

Para celebrarlo, ¡vamos a añadir un nuevo botón a nuestro mando a distancia!

Añadir un botón de silencio

Crea una nueva clase PHP llamada MuteButton y haz que implementeButtonInterface. Pulsa Ctrl+Enter para generar el método press(). Dentro, escribe dump('Pressed mute button'). Ahora, añade #[AsTaggedItem] con un $indexde mute. Deja la prioridad por defecto, 0. Esto colocará este botón por debajo de los demás:

15 lines | src/Remote/Button/MuteButton.php
// ... lines 1 - 6
#[AsTaggedItem('mute')]
final class MuteButton implements ButtonInterface
{
public function press(): void
{
dump('Mute button pressed');
}
}

Sólo tenemos que hacer otra cosa. Cada botón tiene un icono SVG enassets/icons con el mismo nombre que el botón. Copia el archivo mute.svg detutorial/ y pégalo aquí.

¡Momento de la verdad! Vuelve a nuestra aplicación, actualiza y... ¡ahí está! Haz clic en él y comprueba el perfilador. ¡Funciona! Ahora podemos silenciar la TV cuando los niños estén viendo Barney. ¡Perfecto!

¡Eso es todo para esta refactorización! Añadir botones es sencillo y eficaz.

A continuación, vamos a añadir el registro a nuestro mando a distancia y a conocer nuestro siguiente atributo:#[AsAlias].