HandlerMakeCommand::processExceptOption()   A
last analyzed

Complexity

Conditions 3
Paths 3

Size

Total Lines 6
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 3

Importance

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