Completed
Push — master ( 8184ab...35754b )
by Víctor
08:16
created

Bus::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 9

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 1

Importance

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