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
08.

Desencadenar la cadena de responsabilidad

|

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

Abre GameApplication. Vamos a configurar la cadena dentro de su constructor, y empezaremos por instanciar todos los manejadores. Escribe $casinoHandler = new CasinoHandler(), $levelHandler = new LevelHandler(), y finalmente $onFireHandler = new OnFireHandler().

221 lines | src/GameApplication.php
// ... lines 1 - 8
use App\ChainHandler\CasinoHandler;
use App\ChainHandler\LevelHandler;
use App\ChainHandler\OnFireHandler;
// ... lines 12 - 16
class GameApplication
{
// ... lines 19 - 26
public function __construct(private readonly CharacterBuilder $characterBuilder)
{
// ... lines 29 - 30
$casinoHandler = new CasinoHandler();
$levelHandler = new LevelHandler();
$onFireHandler = new OnFireHandler();
// ... lines 34 - 38
}
// ... lines 40 - 219
}

Aquí es donde podemos decidir el orden o secuencia de la cadena. Si tu aplicación no necesita ejecutar los manejadores en ningún orden concreto, ¡genial! ¡Puedes configurarlo como quieras! Pero en nuestro caso, sabemos que el CasinoHandler puede afectar al jugador si sacamos un 7, así que hay que llamarlo primero. Los otros dos controladores pueden ir en cualquier orden, así que empezaremos con el CasinoHandler.

Escribe $casinoHandler->setNext($levelHandler), seguido de LevelHandler - $levelHandler->setNext($onFireHandler) - y podemos dejar solo el OnFireHandler. Será el último de la cadena. Lo último que tenemos que hacer es establecer el CasinoHandler en una propiedad de esta clase, así que escribe $this->xpBonusHandler = $casinoHandler, y mantén pulsadas las teclas "Opción" + "Intro" para añadir la propiedad. Y cambia su tipo a XpBonusHandlerInterface. ¡Perfecto!

221 lines | src/GameApplication.php
// ... lines 1 - 11
use App\ChainHandler\XpBonusHandlerInterface;
// ... lines 13 - 16
class GameApplication
{
// ... lines 19 - 24
private XpBonusHandlerInterface $xpBonusHandler;
// ... line 26
public function __construct(private readonly CharacterBuilder $characterBuilder)
{
// ... lines 29 - 34
$casinoHandler->setNext($levelHandler);
$levelHandler->setNext($onFireHandler);
$this->xpBonusHandler = $casinoHandler;
}
// ... lines 40 - 219
}

Vale, esta cadena tiene que activarse cuando termine una batalla, y hay un método muy práctico que podemos utilizar para hacerlo. Busca el método endBattle(), y justo antes de notificar a los observadores, activa la cadena escribiendo $xpBonus = $this->xpBonusHandler->handle(), donde el primer argumento es $winner y el segundo es $fightResultSet->of($winner). Por último, añadiremos la XP extra al $winner con $winner->addXp($xpBonus).

224 lines | src/GameApplication.php
// ... lines 1 - 16
class GameApplication
{
// ... lines 19 - 100
private function endBattle(FightResultSet $fightResultSet, Character $winner, Character $loser): void
{
// ... lines 103 - 109
$xpBonus = $this->xpBonusHandler->handle($winner, $fightResultSet->of($winner));
$winner->addXp($xpBonus);
// ... lines 112 - 116
}
// ... lines 118 - 222
}

¡Genial! ¡Vamos a probarlo!

Gira hasta tu terminal y ejecuta:

php bin/console app:game:play

Seré un luchador y atacaré hasta que, con suerte, gane, y... ¡ganamos! Pero, hmm... ¿cómo sabemos qué manejador ha entrado en acción? Bueno, ése es un inconveniente de este patrón de diseño. Es difícil de depurar, ya que se puede llamar a cualquier controlador. Para evitarlo, podemos imprimir un mensaje en cada manejador para que sea obvio.

Depuración de manejadores

Para hacerlo rápidamente, puedes copiar el código de debajo de este vídeo, pegarlo y... ¡listo! ¡Veamos si ha funcionado!

33 lines | src/ChainHandler/LevelHandler.php
// ... lines 1 - 6
use App\GameApplication;
// ... line 8
class LevelHandler implements XpBonusHandlerInterface
{
// ... lines 11 - 12
public function handle(Character $player, FightResult $fightResult): int
{
if ($player->getLevel() === 1) {
GameApplication::$printer->info('You earned extra XP thanks to the Level handler!');
// ... lines 17 - 18
}
// ... lines 20 - 25
}
// ... lines 27 - 31
}

33 lines | src/ChainHandler/OnFireHandler.php
// ... lines 1 - 6
use App\GameApplication;
// ... line 8
class OnFireHandler implements XpBonusHandlerInterface
{
// ... lines 11 - 12
public function handle(Character $player, FightResult $fightResult): int
{
if ($fightResult->getWinStreak() >= 3) {
GameApplication::$printer->info('You earned extra XP thanks to the OnFire handler!');
// ... lines 17 - 18
}
// ... lines 20 - 25
}
// ... lines 27 - 31
}

43 lines | src/ChainHandler/CasinoHandler.php
// ... lines 1 - 7
use App\GameApplication;
// ... line 9
class CasinoHandler implements XpBonusHandlerInterface
// ... lines 11 - 13
public function handle(Character $player, FightResult $fightResult): int
{
// ... lines 16 - 24
if ($dice1 === $dice2) {
GameApplication::$printer->info('You earned extra XP thanks to the Casino handler!');
// ... lines 27 - 28
}
// ... lines 30 - 35
}
// ... lines 37 - 41
}

De vuelta a tu terminal, ejecuta

php bin/console app:game:play

De nuevo, juega hasta que termine la batalla, y... ¡mira esto! Ahí está nuestro mensaje! El LevelHandler se activó y nos recompensó con XP extra. ¡Fantástico!

Vale, esto es genial, pero creo que podemos hacerlo mejor. Podemos aprovechar la inyección de dependencias de Symfony para inicializar la cadena. Como extra, refactorizaremos los manejadores para que dejen de comprobar si $next está establecido aplicando el patrón Objeto Nulo. ¡Vamos a hacerlo!