CommandHandlerLocator::__construct()   A
last analyzed

Complexity

Conditions 3
Paths 1

Size

Total Lines 21
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 3
eloc 11
nc 1
nop 1
dl 0
loc 21
rs 9.9
c 1
b 0
f 0
1
<?php
2
3
/*
4
 * cqrs-symfony-messenger (https://github.com/phpgears/cqrs-symfony-messenger).
5
 * CQRS implementation with Symfony's Messenger.
6
 *
7
 * @license MIT
8
 * @link https://github.com/phpgears/cqrs-symfony-messenger
9
 * @author Julián Gutiérrez <[email protected]>
10
 */
11
12
declare(strict_types=1);
13
14
namespace Gears\CQRS\Symfony\Messenger;
15
16
use Gears\CQRS\Command;
17
use Gears\CQRS\CommandHandler;
18
use Gears\CQRS\Exception\InvalidCommandException;
19
use Gears\CQRS\Exception\InvalidCommandHandlerException;
20
use Symfony\Component\Messenger\Envelope;
21
use Symfony\Component\Messenger\Handler\HandlerDescriptor;
22
use Symfony\Component\Messenger\Handler\HandlersLocatorInterface;
23
24
class CommandHandlerLocator implements HandlersLocatorInterface
25
{
26
    /**
27
     * Command handlers map.
28
     *
29
     * @var mixed[]
30
     */
31
    protected $handlersMap;
32
33
    /**
34
     * CommandHandlerLocator constructor.
35
     *
36
     * @param mixed[] $handlers
37
     */
38
    public function __construct(array $handlers)
39
    {
40
        $handlers = \array_map(
41
            function ($handler) {
42
                if (!\is_array($handler)) {
43
                    $handler = [$handler];
44
                }
45
46
                if (\count($handler) !== 1) {
47
                    throw new InvalidCommandHandlerException(\sprintf(
48
                        'Only one command handler allowed, %s given.',
49
                        \count($handler)
50
                    ));
51
                }
52
53
                return $handler;
54
            },
55
            $handlers
56
        );
57
58
        $this->handlersMap = $handlers;
59
    }
60
61
    /**
62
     * {@inheritdoc}
63
     *
64
     * @throws InvalidCommandHandlerException
65
     */
66
    public function getHandlers(Envelope $envelope): iterable
67
    {
68
        $seen = [];
69
70
        foreach ($this->getCommandMap($envelope) as $type) {
71
            foreach ($this->handlersMap[$type] ?? [] as $alias => $handler) {
72
                if (!$handler instanceof CommandHandler) {
73
                    throw new InvalidCommandHandlerException(\sprintf(
74
                        'Command handler must implement "%s" interface, "%s" given.',
75
                        CommandHandler::class,
76
                        \is_object($handler) ? \get_class($handler) : \gettype($handler)
77
                    ));
78
                }
79
80
                $handlerCallable = function (Command $command) use ($handler): void {
81
                    $handler->handle($command);
82
                };
83
84
                if (!\in_array($handlerCallable, $seen, true)) {
85
                    $seen[] = $handlerCallable;
86
87
                    yield $alias => new HandlerDescriptor($handlerCallable);
88
                }
89
            }
90
        }
91
    }
92
93
    /**
94
     * Get command mapping.
95
     *
96
     * @param Envelope $envelope
97
     *
98
     * @throws InvalidCommandException
99
     *
100
     * @return mixed[]
101
     */
102
    final protected function getCommandMap(Envelope $envelope): array
103
    {
104
        /** @var mixed $command */
105
        $command = $envelope->getMessage();
106
107
        if (!$command instanceof Command) {
108
            throw new InvalidCommandException(\sprintf(
109
                'Command must implement "%s" interface, "%s" given.',
110
                Command::class,
111
                \is_object($command) ? \get_class($command) : \gettype($command)
112
            ));
113
        }
114
115
        $type = $command->getCommandType();
116
117
        return [$type => $type]
118
            + ['*' => '*'];
119
    }
120
}
121