Completed
Push — master ( d69d84...947776 )
by Víctor
02:26
created

TransactionableBus   A

Complexity

Total Complexity 19

Size/Duplication

Total Lines 166
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 5

Test Coverage

Coverage 0%

Importance

Changes 0
Metric Value
wmc 19
lcom 1
cbo 5
dl 0
loc 166
ccs 0
cts 87
cp 0
rs 10
c 0
b 0
f 0

9 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 9 1
A dispatch() 0 6 1
A bindCommandWitHisCommandHandler() 0 10 2
A getNameOfClass() 0 10 2
A addHandler() 0 4 1
A handleTheCommand() 0 12 1
A resolveMiddleware() 0 9 2
B mapInputToCommand() 0 26 7
A getDefaultValueOrFail() 0 8 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 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
use Victormln\LaravelTactician\Middleware\DatabaseTransactions;
15
16
/**
17
 * 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.
18
 *
19
 * @package Victormln\LaravelTactician
20
 */
21
class TransactionableBus implements CommandBusInterface
22
{
23
24
    /** @var CommandBus */
25
    protected $bus;
26
27
    /** @var CommandNameExtractor */
28
    protected $commandNameExtractor;
29
30
    /** @var MethodNameInflector */
31
    protected $methodNameInflector;
32
33
    /** @var LocatorInterface */
34
    protected $handlerLocator;
35
36
    public function __construct(
37
        MethodNameInflector $MethodNameInflector,
38
        CommandNameExtractor $CommandNameExtractor,
39
        LocatorInterface $HandlerLocator
40
    ) {
41
        $this->methodNameInflector = $MethodNameInflector;
42
        $this->commandNameExtractor = $CommandNameExtractor;
43
        $this->handlerLocator = $HandlerLocator;
44
    }
45
46
    /**
47
     * Dispatch a command
48
     *
49
     * @param  object $command    Command to be dispatched
50
     * @param  array  $input      Array of input to map to the command
51
     * @param  array  $middleware Array of middleware class name to add to the stack, they are resolved from the laravel container
52
     * @throws CommandHandlerNotExists
53
     * @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
    {
64
        $commandFullName = $this->getNameOfClass($command);
65
        $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
     * @return string
76
     */
77
    private function getNameOfClass($command): string
78
    {
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
    /**
89
     * Add the Command Handler
90
     *
91
     * @param  string $command Class name of the command
92
     * @param  string $handler Class name of the handler to be resolved from the Laravel Container
93
     * @return mixed
94
     */
95
    public function addHandler($command, $handler)
96
    {
97
        $this->handlerLocator->addHandler($handler, $command);
98
    }
99
100
    /**
101
     * Handle the command
102
     *
103
     * @param  $command
104
     * @param  $input
105
     * @param  $middleware
106
     * @return mixed
107
     */
108
    protected function handleTheCommand($command, $input, array $middleware)
109
    {
110
        $this->bus = new CommandBus(
111
            array_merge(
112
                [new DatabaseTransactions()],
113
                [new LockingMiddleware()],
114
                $this->resolveMiddleware($middleware),
115
                [new CommandHandlerMiddleware($this->commandNameExtractor, $this->handlerLocator, $this->methodNameInflector)]
116
            )
117
        );
118
        return $this->bus->handle($this->mapInputToCommand($command, $input));
119
    }
120
121
    /**
122
     * Resolve the middleware stack from the laravel container
123
     *
124
     * @param  $middleware
125
     * @return array
126
     */
127
    protected function resolveMiddleware(array $middleware)
128
    {
129
        $m = [];
130
        foreach ($middleware as $class) {
131
            $m[] = app($class);
132
        }
133
134
        return $m;
135
    }
136
137
    /**
138
     * Map the input to the command
139
     *
140
     * @param  $command
141
     * @param  $input
142
     * @return object
143
     */
144
    protected function mapInputToCommand($command, $input)
145
    {
146
        if (is_object($command)) {
147
            return $command;
148
        }
149
        $dependencies = [];
150
        $class = new ReflectionClass($command);
151
        foreach ($class->getConstructor()->getParameters() as $parameter) {
152
            if ( $parameter->getPosition() == 0 && $parameter->isArray() ) {
0 ignored issues
show
Coding Style introduced by
Expected 0 spaces before closing bracket; 1 found
Loading history...
153
                if ($input !== []) {
154
                    $dependencies[] = $input;
155
                } else {
156
                    $dependencies[] = $this->getDefaultValueOrFail($parameter);
157
                }
158
            } else {
159
                $name = $parameter->getName();
160
                if (array_key_exists($name, $input)) {
161
                    $dependencies[] = $input[$name];
162
                } else {
163
                    $dependencies[] = $this->getDefaultValueOrFail($parameter);
164
                }
165
            }
166
        }
167
168
        return $class->newInstanceArgs($dependencies);
169
    }
170
171
    /**
172
     * Returns Default Value for parameter if it exists Or Fail
173
     *
174
     * @ReflectionParameter $parameter
175
     * @return mixed
176
     */
177
    private function getDefaultValueOrFail($parameter)
178
    {
179
        if (! $parameter->isDefaultValueAvailable()) {
180
            throw new InvalidArgumentException("Unable to map input to command: {$parameter->getName()}");
181
        }
182
183
        return $parameter->getDefaultValue();
184
    }
185
186
}
187