Bus::handleTheCommand()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 10
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 1 Features 0
Metric Value
cc 1
eloc 6
c 2
b 1
f 0
nc 1
nop 3
dl 0
loc 10
rs 10
1
<?php
2
3
namespace Joselfonseca\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 Joselfonseca\LaravelTactician\Locator\LocatorInterface;
11
use League\Tactician\Handler\MethodNameInflector\MethodNameInflector;
12
use League\Tactician\Handler\CommandNameExtractor\CommandNameExtractor;
13
14
/**
15
 * The default Command bus Using Tactician, this is an implementation to dispatch commands to their handlers
16
 * trough a middleware stack, every class is resolved from the laravel's service container.
17
 *
18
 * @package Joselfonseca\LaravelTactician
19
 */
20
class Bus implements CommandBusInterface
21
{
22
23
    /**
24
     * @var CommandBus
25
     */
26
    protected $bus;
27
    /**
28
     * @var CommandNameExtractor
29
     */
30
    protected $CommandNameExtractor;
31
    /**
32
     * @var MethodNameInflector
33
     */
34
    protected $MethodNameInflector;
35
    /**
36
     * @var LocatorInterface
37
     */
38
    protected $HandlerLocator;
39
40
    private $inInputCommand;
41
42
    /**
43
     * @param MethodNameInflector  $MethodNameInflector
44
     * @param CommandNameExtractor $CommandNameExtractor
45
     * @param LocatorInterface       $HandlerLocator
46
     */
47
    public function __construct(
48
        MethodNameInflector $MethodNameInflector,
49
        CommandNameExtractor $CommandNameExtractor,
50
        LocatorInterface $HandlerLocator
51
    ) {
52
        $this->MethodNameInflector = $MethodNameInflector;
53
        $this->CommandNameExtractor = $CommandNameExtractor;
54
        $this->HandlerLocator = $HandlerLocator;
55
    }
56
57
    /**
58
     * Dispatch a command
59
     *
60
     * @param  object $command    Command to be dispatched
61
     * @param  array  $input      Array of input to map to the command
62
     * @param  array  $middleware Array of middleware class name to add to the stack, they are resolved from the laravel container
63
     * @return mixed
64
     */
65
    public function dispatch($command, array $input = [], array $middleware = [])
66
    {
67
        return $this->handleTheCommand($command, $input, $middleware);
68
    }
69
70
    /**
71
     * Add the Command Handler
72
     *
73
     * @param  string $command Class name of the command
74
     * @param  string $handler Class name of the handler to be resolved from the Laravel Container
75
     * @return mixed
76
     */
77
    public function addHandler($command, $handler)
78
    {
79
        $this->HandlerLocator->addHandler($handler, $command);
80
    }
81
82
    /**
83
     * Handle the command
84
     *
85
     * @param  $command
86
     * @param  $input
87
     * @param  $middleware
88
     * @return mixed
89
     */
90
    protected function handleTheCommand($command, $input, array $middleware)
91
    {
92
        $this->bus = new CommandBus(
93
            array_merge(
94
                [new LockingMiddleware()],
95
                $this->resolveMiddleware($middleware),
96
                [new CommandHandlerMiddleware($this->CommandNameExtractor, $this->HandlerLocator, $this->MethodNameInflector)]
97
            )
98
        );
99
        return $this->bus->handle($this->mapInputToCommand($command, $input));
100
    }
101
102
    /**
103
     * Resolve the middleware stack from the laravel container
104
     *
105
     * @param  $middleware
106
     * @return array
107
     */
108
    protected function resolveMiddleware(array $middleware)
109
    {
110
        $m = [];
111
        foreach ($middleware as $class) {
112
            $m[] = app($class);
113
        }
114
115
        return $m;
116
    }
117
118
    /**
119
     * Map the input to the command
120
     *
121
     * @param  $command
122
     * @param  $input
123
     * @return object
124
     */
125
    protected function mapInputToCommand($command, $input)
126
    {
127
        if (is_object($command)) {
128
            return $command;
129
        }
130
        $this->inInputCommand = $command;
131
        $dependencies = [];
132
        $class = new ReflectionClass($command);
133
        foreach ($class->getConstructor()->getParameters() as $parameter) {
134
            if ( $parameter->getPosition() == 0 && $parameter->getType() && $parameter->getType()->getName() === 'array') {
0 ignored issues
show
Bug introduced by
The method getName() does not exist on ReflectionType. It seems like you code against a sub-type of ReflectionType such as ReflectionNamedType. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

134
            if ( $parameter->getPosition() == 0 && $parameter->getType() && $parameter->getType()->/** @scrutinizer ignore-call */ getName() === 'array') {
Loading history...
135
                if ($input !== []) {
136
                    $dependencies[] = $input;
137
                } else {
138
                    $dependencies[] = $this->getDefaultValueOrFail($parameter);
139
                }
140
            } else {
141
                $name = $parameter->getName();
142
                if (array_key_exists($name, $input)) {
143
                    $dependencies[] = $input[$name];
144
                } else {
145
                    $dependencies[] = $this->getDefaultValueOrFail($parameter);
146
                }
147
            }
148
        }
149
150
        return $class->newInstanceArgs($dependencies);
151
    }
152
153
    /**
154
     * Returns Default Value for parameter if it exists Or Fail
155
     *
156
     * @ReflectionParameter $parameter
157
     * @return mixed
158
     */
159
    private function getDefaultValueOrFail($parameter)
160
    {
161
        if (! $parameter->isDefaultValueAvailable()) {
162
            throw new InvalidArgumentException("Unable to map input to command {$this->inInputCommand} for parameter {$parameter->getName()}");
163
        }
164
165
        return $parameter->getDefaultValue();
166
    }
167
}
168