Completed
Push — master ( 110aa1...721889 )
by Víctor
01:43
created

Bus::bindCommandWitHisCommandHandler()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 10

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 10
ccs 2
cts 2
cp 1
rs 9.9332
c 0
b 0
f 0
cc 2
nc 2
nop 1
crap 2
1
<?php
2
3
namespace Victormln\LaravelTactician;
4
5
use ReflectionClass;
6
use InvalidArgumentException;
7
use League\Tactician\CommandBus;
8
use League\Tactician\Plugins\LockingMiddleware;
9
use League\Tactician\Handler\CommandHandlerMiddleware;
10
use Solidoy\Campaign\Command\CreateCampaignCommandHandler;
11
use Victormln\LaravelTactician\Exceptions\CommandHandlerNotExists;
12
use Victormln\LaravelTactician\Locator\LocatorInterface;
13
use League\Tactician\Handler\MethodNameInflector\MethodNameInflector;
14
use League\Tactician\Handler\CommandNameExtractor\CommandNameExtractor;
15
use Victormln\LaravelTactician\Middleware\BindCommandWithCommandHandler;
16
17
/**
18
 * The default Command bus Using Tactician, this is an implementation to dispatch commands to their handlers trough a middleware stack, every class is resolved from the laravel's service container.
19
 *
20
 * @package Victormln\LaravelTactician
21
 */
22
class Bus implements CommandBusInterface
23
{
24
25
    /** @var CommandBus */
26
    protected $bus;
27
28
    /** @var CommandNameExtractor */
29
    protected $commandNameExtractor;
30
31
    /** @var MethodNameInflector */
32
    protected $methodNameInflector;
33
34
    /** @var LocatorInterface */
35
    protected $handlerLocator;
36
37
    public function __construct(
38
        MethodNameInflector $MethodNameInflector,
39
        CommandNameExtractor $CommandNameExtractor,
40
        LocatorInterface $HandlerLocator
41
    ) {
42
        $this->methodNameInflector = $MethodNameInflector;
43
        $this->commandNameExtractor = $CommandNameExtractor;
44
        $this->handlerLocator = $HandlerLocator;
45 7
    }
46
47
    /**
48
     * Dispatch a command
49
     *
50 7
     * @param  object $command    Command to be dispatched
51 7
     * @param  array  $input      Array of input to map to the command
52 7
     * @param  array  $middleware Array of middleware class name to add to the stack, they are resolved from the laravel container
53 7
     * @return mixed
54
     */
55
    public function dispatch($command, array $input = [], array $middleware = [])
56
    {
57
        $this->bindCommandWitHisCommandHandler($command);
58
59
        return $this->handleTheCommand($command, $input, $middleware);
60
    }
61
62
    private function bindCommandWitHisCommandHandler($command)
63 7
    {
64
        $commandFullName = $this->getNameOfClass($command);
65 7
        $commandHandlerFullName = $commandFullName . 'Handler';
66
        if(!class_exists($commandHandlerFullName)) {
0 ignored issues
show
Coding Style introduced by
Expected 1 space after IF keyword; 0 found
Loading history...
67
            throw CommandHandlerNotExists::with($commandHandlerFullName);
68
        }
69
70
        $this->addHandler($commandFullName, $commandHandlerFullName);
71
    }
72
73
    /**
74
     * @param $command
75 7
     * @return string
76
     */
77 7
    private function getNameOfClass($command): string
78 7
    {
79
        if(\is_string($command)) {
0 ignored issues
show
Coding Style introduced by
Expected 1 space after IF keyword; 0 found
Loading history...
80
            return $command;
81
        }
82
83
        $reflectionCommand = new \ReflectionObject($command);
84
85
        return $reflectionCommand->getNamespaceName() . '\\' . $reflectionCommand->getShortName();
86
    }
87
88 7
    /**
89
     * Add the Command Handler
90 7
     *
91 7
     * @param  string $command Class name of the command
92 7
     * @param  string $handler Class name of the handler to be resolved from the Laravel Container
93 7
     * @return mixed
94 7
     */
95
    public function addHandler($command, $handler)
96
    {
97 7
        $this->handlerLocator->addHandler($handler, $command);
98
    }
99
100
    /**
101
     * Handle the command
102
     *
103
     * @param  $command
104
     * @param  $input
105
     * @param  $middleware
106 7
     * @return mixed
107
     */
108 7
    protected function handleTheCommand($command, $input, array $middleware)
109 7
    {
110 3
        $this->bus = new CommandBus(
111
            array_merge(
112
                [new LockingMiddleware()],
113 7
                $this->resolveMiddleware($middleware),
114
                [new CommandHandlerMiddleware($this->commandNameExtractor, $this->handlerLocator, $this->methodNameInflector)]
115
            )
116
        );
117
        return $this->bus->handle($this->mapInputToCommand($command, $input));
118
    }
119
120
    /**
121
     * Resolve the middleware stack from the laravel container
122
     *
123 7
     * @param  $middleware
124
     * @return array
125 7
     */
126 1
    protected function resolveMiddleware(array $middleware)
127
    {
128 6
        $m = [];
129 6
        foreach ($middleware as $class) {
130 6
            $m[] = app($class);
131 6
        }
132 2
133 1
        return $m;
134
    }
135 2
136
    /**
137
     * Map the input to the command
138 4
     *
139 4
     * @param  $command
140 1
     * @param  $input
141
     * @return object
142 6
     */
143
    protected function mapInputToCommand($command, $input)
144
    {
145
        if (is_object($command)) {
146
            return $command;
147 5
        }
148
        $dependencies = [];
149
        $class = new ReflectionClass($command);
150
        foreach ($class->getConstructor()->getParameters() as $parameter) {
151
            if ( $parameter->getPosition() == 0 && $parameter->isArray() ) {
0 ignored issues
show
Coding Style introduced by
Expected 0 spaces before closing bracket; 1 found
Loading history...
152
                if ($input !== []) {
153
                    $dependencies[] = $input;
154
                } else {
155
                    $dependencies[] = $this->getDefaultValueOrFail($parameter);
156 5
                }
157
            } else {
158 5
                $name = $parameter->getName();
159 1
                if (array_key_exists($name, $input)) {
160
                    $dependencies[] = $input[$name];
161
                } else {
162 4
                    $dependencies[] = $this->getDefaultValueOrFail($parameter);
163
                }
164
            }
165
        }
166
167
        return $class->newInstanceArgs($dependencies);
168
    }
169
170
    /**
171
     * Returns Default Value for parameter if it exists Or Fail
172
     *
173
     * @ReflectionParameter $parameter
174
     * @return mixed
175
     */
176
    private function getDefaultValueOrFail($parameter)
177
    {
178
        if (! $parameter->isDefaultValueAvailable()) {
179
            throw new InvalidArgumentException("Unable to map input to command: {$parameter->getName()}");
180
        }
181
182
        return $parameter->getDefaultValue();
183
    }
184
185
}
186