Passed
Push — master ( 51e9c7...7f3bc9 )
by Ilya
07:38 queued 04:52
created

HandlerMakeCommand::getBaseHandlerClassName()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 7
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 2

Importance

Changes 0
Metric Value
cc 2
eloc 3
nc 2
nop 0
dl 0
loc 7
ccs 4
cts 4
cp 1
crap 2
rs 10
c 0
b 0
f 0
1
<?php
2
3
namespace Hivokas\LaravelHandlers\Commands;
4
5
use Illuminate\Filesystem\Filesystem;
6
use Illuminate\Console\GeneratorCommand;
7
use Hivokas\LaravelHandlers\Support\ActionsBag;
8
use Symfony\Component\Console\Input\InputOption;
9
use Symfony\Component\Console\Input\InputArgument;
10
use Hivokas\LaravelHandlers\Exceptions\CommandException;
11
12
class HandlerMakeCommand extends GeneratorCommand
13
{
14
    /**
15
     * The console command name.
16
     *
17
     * @var string
18
     */
19
    protected $name = 'make:handler';
20
21
    /**
22
     * The console command description.
23
     *
24
     * @var string
25
     */
26
    protected $description = 'Handler generator.';
27
28
    /**
29
     * The type of class being generated.
30
     *
31
     * @var string
32
     */
33
    protected $type = 'Handler';
34
35
    /**
36
     * The name input.
37
     *
38
     * @var string|null
39
     */
40
    protected $nameInput = null;
41
42
    /**
43
     * HandlerMakeCommand constructor.
44
     *
45
     * @param Filesystem $files
46
     */
47 17
    public function __construct(Filesystem $files)
48
    {
49 17
        parent::__construct($files);
50 17
    }
51
52
    /**
53
     * Execute the console command.
54
     *
55
     * @return bool
56
     */
57 16
    public function handle(): bool
58
    {
59
        try {
60 16
            $bag = new ActionsBag;
61
62 16
            $this->processResourceOption($bag);
63 16
            $this->processActionsOption($bag);
64 15
            $this->processExceptOption($bag);
65 14
            $this->processApiOption($bag);
66
67 14
            $classNames = $this->generateClassNames($bag, $this->getValidatedNameArgument());
68
69 13
            $finalNamespace = $this->getFinalNamespace();
70
71 12
            foreach ($classNames as $className) {
72 12
                $this->nameInput = $finalNamespace.'\\'.$className;
73 12
                $this->type = $className.' handler';
74
75 12
                parent::handle();
76
            }
77 5
        } catch (CommandException $exception) {
78 5
            $this->error($exception->getMessage());
79
80 5
            return false;
81
        }
82
83 11
        return true;
84
    }
85
86
    /**
87
     * Get final namespace determined by default and specified by user namespaces.
88
     *
89
     * @return string
90
     * @throws CommandException
91
     */
92 13
    protected function getFinalNamespace(): string
93
    {
94 13
        $defaultNamespace = $this->laravel->getNamespace().'Http\\Handlers';
95
96 13
        if (! is_null($namespaceOption = $this->getValidatedAndNormalizedNamespaceOption())) {
97 2
            if (starts_with($namespaceOption, '\\')) {
98 1
                return $namespaceOption;
99
            }
100
101 1
            return $defaultNamespace.'\\'.$namespaceOption;
102
        }
103
104 10
        return $defaultNamespace;
105
    }
106
107
    /**
108
     * Get validated and normalized namespace option.
109
     *
110
     * @return string|null
111
     * @throws CommandException
112
     */
113 13
    protected function getValidatedAndNormalizedNamespaceOption(): ?string
114
    {
115 13
        $namespace = (string) $this->option('namespace');
116
117 13
        if (! $namespace) {
118 10
            return null;
119
        }
120
121 3
        $namespaceWithNormalizedSlashes = preg_replace('/[\/\\\]+/', '\\', $namespace);
122
123 3
        if (! preg_match('/^(\\\|(\\\?\w+)+)$/', $namespaceWithNormalizedSlashes)) {
124 1
            throw new CommandException('['.$namespace.'] is not a valid namespace.');
125
        }
126
127 2
        return $namespaceWithNormalizedSlashes;
128
    }
129
130
    /**
131
     * Generate class names by specified name and actions.
132
     *
133
     * @param ActionsBag $bag
134
     * @param string $name
135
     * @return array
136
     */
137 13
    protected function generateClassNames(ActionsBag $bag, string $name): array
138
    {
139 13
        $name = studly_case($name);
140
141 13
        if ($bag->isEmpty()) {
142 9
            return [$name];
143
        } else {
144
            return array_map(function (string $action) use ($name) {
145 4
                return studly_case($action).$name;
146 4
            }, $bag->get());
147
        }
148
    }
149
150
    /**
151
     * Get validated name argument.
152
     *
153
     * @return string
154
     * @throws CommandException
155
     */
156 14
    protected function getValidatedNameArgument(): string
157
    {
158 14
        $name = (string) $this->argument('name');
159 14
        if (! preg_match('/^\w+$/', $name)) {
160 1
            throw new CommandException('Name can\'t contain any non-word characters.');
161
        }
162
163 13
        return $name;
164
    }
165
166
    /**
167
     * Process --resource option.
168
     *
169
     * @param ActionsBag $bag
170
     * @return void
171
     */
172 16
    protected function processResourceOption(ActionsBag $bag): void
173
    {
174 16
        if ($this->option('resource')) {
175 4
            foreach (['index', 'show', 'create', 'store', 'edit', 'update', 'destroy'] as $action) {
176 4
                $bag->addIfNotExists($action);
177
            }
178
        }
179 16
    }
180
181
    /**
182
     * Process --actions option.
183
     *
184
     * @param ActionsBag $bag
185
     * @return void
186
     * @throws CommandException
187
     */
188 16
    protected function processActionsOption(ActionsBag $bag): void
189
    {
190 16
        if ($actions = (string) $this->option('actions')) {
191 2
            foreach (explode(',', $actions) as $action) {
192 2
                $bag->addIfNotExists(
193 2
                    $this->getValidatedAndNormalizedActionName($action)
194
                );
195
            }
196
        }
197 15
    }
198
199
    /**
200
     * Process --except option.
201
     *
202
     * @param ActionsBag $bag
203
     * @return void
204
     * @throws CommandException
205
     */
206 15
    protected function processExceptOption(ActionsBag $bag): void
207
    {
208 15
        if ($except = (string) $this->option('except')) {
209 2
            foreach (explode(',', $except) as $action) {
210 2
                $bag->deleteIfExists(
211 2
                    $this->getValidatedAndNormalizedActionName($action)
212
                );
213
            }
214
        }
215 14
    }
216
217
    /**
218
     * Process an --api option.
219
     *
220
     * @param ActionsBag $bag
221
     * @return void
222
     */
223 14
    protected function processApiOption(ActionsBag $bag): void
224
    {
225 14
        if ($this->option('api')) {
226 1
            foreach (['edit', 'create'] as $action) {
227 1
                $bag->deleteIfExists($action);
228
            }
229
        }
230 14
    }
231
232
    /**
233
     * Get validated and normalized action name.
234
     *
235
     * @param string $action
236
     * @return string
237
     * @throws CommandException
238
     */
239 4
    protected function getValidatedAndNormalizedActionName(string $action): string
240
    {
241 4
        if (preg_match('/^\w+$/', $action)) {
242 4
            return snake_case($action);
243
        }
244
245 2
        throw new CommandException('['.$action.'] is not a valid action name.');
246
    }
247
248
    /**
249
     * Get the name input.
250
     *
251
     * @return null|string
252
     */
253 12
    public function getNameInput(): ?string
254
    {
255 12
        return $this->nameInput;
256
    }
257
258
    /**
259
     * Get the stub file for the generator.
260
     *
261
     * @return string
262
     */
263 11
    protected function getStub(): string
264
    {
265 11
        return __DIR__.'/../../stubs/handler.stub';
266
    }
267
268
    /**
269
     * Get the class name of the base handler.
270
     *
271
     * @return string
272
     * @throws CommandException
273
     */
274 11
    protected function getBaseHandlerClassName(): string
275
    {
276 11
        if (class_exists($base = config('handlers.base'))) {
277 10
            return $base;
278
        }
279
280 1
        throw new CommandException('The ['.$base.'] class specified as the base handler doesn\'t exist.');
281
    }
282
283
    /**
284
     * Replace the namespace for the given stub.
285
     *
286
     * @param  string $stub
287
     * @param  string $name
288
     * @return $this
289
     * @throws CommandException
290
     */
291 11
    protected function replaceNamespace(&$stub, $name)
292
    {
293 11
        $stub = str_replace(
294 11
            ['DummyNamespace', 'DummyBaseHandlerNamespace'],
295 11
            [$this->getNamespace($name), $this->getBaseHandlerClassName()],
296 10
            $stub
297
        );
298
299 10
        return $this;
300
    }
301
302
    /**
303
     * Get the console command arguments.
304
     *
305
     * @return array
306
     */
307 17
    protected function getArguments(): array
308
    {
309
        return [
310 17
            ['name', InputArgument::REQUIRED, 'The name of the class'],
311
        ];
312
    }
313
314
    /**
315
     * Get the console command options.
316
     *
317
     * @return array
318
     */
319 17
    protected function getOptions(): array
320
    {
321
        return [
322 17
            ['resource', 'r', InputOption::VALUE_NONE, 'Generate handlers for all resource actions.'],
323 17
            ['api', 'a', InputOption::VALUE_NONE, 'Exclude the create and edit actions.'],
324 17
            ['namespace', null, InputOption::VALUE_REQUIRED, 'The namespace for generated handler(-s).'],
325 17
            ['force', 'f', InputOption::VALUE_NONE, 'Override existing handlers.'],
326 17
            ['actions', null, InputOption::VALUE_REQUIRED, 'Generate handlers for all specified actions separated by coma.'],
327 17
            ['except', null, InputOption::VALUE_REQUIRED, 'Exclude specified actions separated by coma.'],
328
        ];
329
    }
330
}
331