Contenedor e Iterador con ServiceCollectionInterface
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.
With a Subscription, click any sentence in the script to jump to that part of the video!
Login SubscribeEn 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. La primera, que probablemente sea la más sencilla, 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
:
// ... 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:
// ... lines 1 - 24 | |
public function buttons(): iterable | |
{ | |
dd($this->buttons->getProvidedServices()); | |
} | |
// ... lines 29 - 30 |
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()
:
// ... 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 se implementeButtonInterface
. Pulsa Ctrl+Enter
para generar el método press()
. Dentro, escribe dump('Pressed mute button')
. Ahora, añade #[AsTaggedItem]
con un $index
de mute
. Deja la prioridad por defecto, 0
. Esto colocará este botón por debajo de los demás:
// ... lines 1 - 6 | |
'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, actualízala y... ¡ahí está! Haz clic en él y comprueba el perfilador. ¡Funciona! Ahora podemos silenciar la tele 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]
.
Extremely useful!
The advanced concepts are explained in a way that makes them easy to understand and implement for someone at an intermediate level like me.
I’m looking forward to more courses like this to continue growing my skills.
Thank you so much! :)