Passed
Push — develop ( 21f34e...f33517 )
by Ilya
02:48
created

getValidatedAndNormalizedActionName()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 9
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 2

Importance

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