MakeRoute   C
last analyzed

Complexity

Total Complexity 53

Size/Duplication

Total Lines 451
Duplicated Lines 4.88 %

Coupling/Cohesion

Components 1
Dependencies 11

Importance

Changes 0
Metric Value
wmc 53
lcom 1
cbo 11
dl 22
loc 451
rs 6.96
c 0
b 0
f 0

29 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 6 1
A handle() 0 10 1
A mountpoint() 0 7 2
A destinationFile() 0 7 2
A warnIfRouteAlreadyExists() 0 9 3
A routeExists() 0 7 2
A webRouteExists() 0 12 4
A removeTrailingSlashIfExists() 0 7 2
A removeDuplicatedTrailingSlashes() 0 4 1
A apiRouteExists() 0 4 1
A createTmpFileWithRoute() 0 6 1
A getPath() 0 4 1
A getRouteCode() 15 15 2
A method() 7 7 2
A action() 0 10 3
A processInput() 0 5 1
A validateMethod() 0 6 2
A validateType() 0 6 2
A postActions() 0 12 4
A createMenu() 0 15 2
A warnIfSpatieMenuIsNotInstalled() 0 6 2
A createAction() 0 10 4
A createView() 0 10 2
A createController() 0 9 1
A createResourceController() 0 9 1
A addMethodToController() 0 6 1
A createTmpFileWithMethod() 0 6 1
A getMethodCode() 0 10 1
A getMethodStubPath() 0 4 1

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like MakeRoute often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use MakeRoute, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
namespace Acacha\AdminLTETemplateLaravel\Console;
4
5
use Acacha\AdminLTETemplateLaravel\Console\Routes\Controller;
6
use Acacha\AdminLTETemplateLaravel\Console\Routes\ControllerResourceRoute;
7
use Acacha\AdminLTETemplateLaravel\Console\Routes\ControllerRoute;
8
use Acacha\AdminLTETemplateLaravel\Console\Routes\GeneratesCode;
9
use Acacha\AdminLTETemplateLaravel\Console\Routes\RegularRoute;
10
use Acacha\AdminLTETemplateLaravel\Exceptions\RouteTypeNotValid;
11
use Acacha\AdminLTETemplateLaravel\Exceptions\SpatieMenuDoesNotExists;
12
use Acacha\Filesystem\Compiler\StubFileCompiler;
13
use Acacha\Filesystem\Filesystem;
14
use Illuminate\Console\Command;
15
use Illuminate\Routing\Router;
16
use Illuminate\Support\Facades\Artisan;
17
use Route;
18
use Symfony\Component\Routing\Exception\MethodNotAllowedException;
19
20
/**
21
 * Class MakeRoute.
22
 */
23
class MakeRoute extends Command
24
{
25
    use Controller, CreatesModels;
26
27
    /**
28
     * Path to web routes file.
29
     *
30
     * @var string
31
     */
32
    protected $web_routes_path = 'routes/web.php';
33
34
    /**
35
     * Path to api routes file.
36
     *
37
     * @var string
38
     */
39
    protected $api_routes_path = 'routes/api.php';
40
41
    /**
42
     * Compiler for stub file.
43
     *
44
     * @var StubFileCompiler
45
     */
46
    protected $compiler;
47
48
    /**
49
     * Compiler for stub file.
50
     *
51
     * @var Filesystem
52
     */
53
    protected $filesystem;
54
55
    /**
56
     * @var array
57
     */
58
    protected static $lookup = [
59
        'regular' => RegularRoute::class,
60
        'controller' => ControllerRoute::class,
61
        'resource' => ControllerResourceRoute::class,
62
    ];
63
64
    /**
65
     * The name and signature of the console command.
66
     */
67
    protected $signature = 'make:route {link : The route link} {action? : View or controller to create} 
68
    {--t|type=regular : Type of route to create (regular,controller,resource)} {--m|method=get : HTTP method} 
69
    {--api : Route is an api route} {--a|createaction : Create view or controller after route}
70
    {--menu : Create also menu entry using make:menu command} {--model : Create also a model using command make:model}';
71
72
    /**
73
     * The console command description.
74
     *
75
     * @var string
76
     */
77
    protected $description = 'Insert a route to routes/web.php file';
78
79
    /**
80
     * AdminLTERoute constructor.
81
     *
82
     * @param StubFileCompiler $compiler
83
     * @param Filesystem $filesystem
84
     */
85
    public function __construct(StubFileCompiler $compiler, Filesystem $filesystem)
86
    {
87
        parent::__construct();
88
        $this->compiler = $compiler;
89
        $this->filesystem = $filesystem;
90
    }
91
92
    /**
93
     * Execute the console command.
94
     */
95
    public function handle()
96
    {
97
        $this->processInput();
98
        $this->warnIfRouteAlreadyExists($link = $this->argument('link'));
99
        $tmpfile = $this->createTmpFileWithRoute();
100
        $path = $this->getPath($tmpfile);
101
        add_file_into_file($this->mountpoint(), $path, $dstFile = $this->destinationFile());
102
        $this->info('Route ' . undot_path($link) . ' added to ' .  $dstFile . '.');
103
        $this->postActions();
104
    }
105
106
    /**
107
     * Get mountpoint.
108
     *
109
     * @return string
110
     */
111
    protected function mountpoint()
112
    {
113
        if ($this->option('api')) {
114
            return '#adminlte_api_routes';
115
        }
116
        return '#adminlte_routes';
117
    }
118
119
    /**
120
     * Destination route file.
121
     *
122
     * @return string
123
     */
124
    protected function destinationFile()
125
    {
126
        if ($this->option('api')) {
127
            return base_path($this->api_routes_path);
128
        }
129
        return base_path($this->web_routes_path);
130
    }
131
132
    /**
133
     * Warn if route already exists.
134
     *
135
     * @param $link
136
     */
137
    protected function warnIfRouteAlreadyExists($link)
138
    {
139
        if ($this->routeExists($link)) {
140
            if ($this->confirm('Route already exists. Do you wish to continue?')) {
141
                return;
142
            }
143
            die();
144
        }
145
    }
146
147
    /**
148
     * Check if route exists.
149
     *
150
     * @param $link
151
     * @return mixed
152
     */
153
    protected function routeExists($link)
154
    {
155
        if ($this->option('api')) {
156
            return $this->apiRouteExists($link);
157
        }
158
        return $this->webRouteExists($link);
159
    }
160
161
    /**
162
     * Check if web route exists.
163
     *
164
     * @param $link
165
     * @return mixed
166
     */
167
    protected function webRouteExists($link)
168
    {
169
        $link = $this->removeTrailingSlashIfExists($link);
170
        $link = $this->removeDuplicatedTrailingSlashes($link);
171
        foreach (Route::getRoutes() as $value) {
172
            if (in_array(strtoupper($this->option('method')), array_merge($value->methods(), ['ANY'])) &&
173
                $value->uri() === $link) {
174
                return true;
175
            }
176
        }
177
        return false;
178
    }
179
180
    /**
181
     * Remove (if exists) trailing slash from link.
182
     *
183
     * @param $link
184
     * @return string
185
     */
186
    protected function removeTrailingSlashIfExists($link)
187
    {
188
        if (starts_with($link, '/')) {
189
            return substr($link, 1);
190
        }
191
        return $link;
192
    }
193
194
    /**
195
     * Remove duplicated trailing slashes.
196
     *
197
     * @param $link
198
     * @return mixed
199
     */
200
    protected function removeDuplicatedTrailingSlashes($link)
201
    {
202
        return preg_replace('/(\/+)/', '/', $link);
203
    }
204
205
    /**
206
     * Check if api route exists.
207
     *
208
     * @param $link
209
     * @return mixed
210
     */
211
    protected function apiRouteExists($link)
212
    {
213
        return $this->webRouteExists('api/v1/' . $link);
214
    }
215
216
    /**
217
     * Crete tmp file with route to add.
218
     *
219
     * @return mixed
220
     */
221
    protected function createTmpFileWithRoute()
222
    {
223
        $temp = tmpfile();
224
        fwrite($temp, $this->getRouteCode());
225
        return $temp;
226
    }
227
228
    /**
229
     * Get path from file resource.
230
     *
231
     * @param $tmpfile
232
     * @return mixed
233
     */
234
    protected function getPath($tmpfile)
235
    {
236
        return stream_get_meta_data($tmpfile)['uri'];
237
    }
238
239
    /**
240
     * Get route code to insert depending on type.
241
     *
242
     * @return mixed
243
     */
244 View Code Duplication
    protected function getRouteCode()
0 ignored issues
show
Duplication introduced by Sergi Tur Badenas
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
245
    {
246
        $type = $this->option('type');
247
        $class = isset(static::$lookup[$type])
248
            ? static::$lookup[$type]
249
            : RegularRoute::class;
250
        /** @var GeneratesCode $route */
251
        $route = new $class($this->compiler, $this->filesystem);
252
        $route->setReplacements([
253
            undot_path($this->argument('link')),
254
            $this->action(),
255
            $this->method()
256
        ]);
257
        return $route->code();
258
    }
259
260
    /**
261
     * Get method.
262
     *
263
     * @return string
264
     */
265 View Code Duplication
    protected function method()
0 ignored issues
show
Duplication introduced by Sergi Tur Badenas
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
266
    {
267
        if (strtolower($this->option('method')) == 'head') {
268
            return 'get';
269
        }
270
        return strtolower($this->option('method'));
271
    }
272
273
    /**
274
     * Get the action replacement.
275
     *
276
     * @return array|string
277
     */
278
    protected function action()
279
    {
280
        if ($this->argument('action') != null) {
0 ignored issues
show
Bug introduced by Sergi Tur Badenas
It seems like you are loosely comparing $this->argument('action') of type string|array|null against null; this is ambiguous if the string can be empty. Consider using a strict comparison !== instead.
Loading history...
281
            return $this->argument('action');
282
        }
283
        if (strtolower($this->option('type')) != 'regular') {
284
            return $this->argument('link') . 'Controller';
285
        }
286
        return $this->argument('link');
287
    }
288
289
    /**
290
     * Process input.
291
     */
292
    protected function processInput()
293
    {
294
        $this->validateMethod();
295
        $this->validateType();
296
    }
297
298
    /**
299
     * Validate option method.
300
     */
301
    protected function validateMethod()
302
    {
303
        if (! in_array(strtoupper($this->option('method')), $methods = array_merge(Router::$verbs, ['ANY']))) {
304
            throw new MethodNotAllowedException($methods);
305
        }
306
    }
307
308
    /**
309
     * Validate option type.
310
     */
311
    protected function validateType()
312
    {
313
        if (! in_array(strtolower($this->option('type')), ['regular','controller','resource'])) {
314
            throw new RouteTypeNotValid();
315
        }
316
    }
317
318
    /**
319
     * Execute post actions (if exists)
320
     */
321
    protected function postActions()
322
    {
323
        if ($this->option('createaction') != null) {
0 ignored issues
show
Bug introduced by Sergi Tur Badenas
It seems like you are loosely comparing $this->option('createaction') of type string|array|null against null; this is ambiguous if the string can be empty. Consider using a strict comparison !== instead.
Loading history...
324
            $this->createAction();
325
        }
326
        if ($this->option('menu') != null) {
0 ignored issues
show
Bug introduced by Sergi Tur Badenas
It seems like you are loosely comparing $this->option('menu') of type string|array|null against null; this is ambiguous if the string can be empty. Consider using a strict comparison !== instead.
Loading history...
327
            $this->createMenu();
328
        }
329
        if ($this->option('model') != null) {
0 ignored issues
show
Bug introduced by Sergi Tur Badenas
It seems like you are loosely comparing $this->option('model') of type string|array|null against null; this is ambiguous if the string can be empty. Consider using a strict comparison !== instead.
Loading history...
330
            $this->createModel($this->argument('link'));
331
        }
332
    }
333
334
    /**
335
     * Create menu.
336
     */
337
    protected function createMenu()
338
    {
339
        try {
340
            $this->warnIfSpatieMenuIsNotInstalled();
341
        } catch (\Exception $e) {
342
            //Skip installation of menu
343
            $this->error($e->getMessage());
344
            return;
345
        }
346
        Artisan::call('make:menu', [
347
            'link' => $link = undot_path($this->argument('link')),
348
            'name' => ucfirst($link),
349
        ]);
350
        $this->info('Menu entry ' . $link .' added to config/menu.php file.');
351
    }
352
353
    /**
354
     * Warn if spatie menu ins not installed.
355
     *
356
     * @throws SpatieMenuDoesNotExists
357
     */
358
    protected function warnIfSpatieMenuIsNotInstalled()
359
    {
360
        if (!(app()->getProvider('Spatie\Menu\Laravel\MenuServiceProvider'))) {
361
            throw new SpatieMenuDoesNotExists();
362
        }
363
    }
364
365
    /**
366
     * Create action (view|controller).
367
     */
368
    protected function createAction()
369
    {
370
        if (strtolower($this->option('type')) == 'regular' || $this->option('type') == null) {
0 ignored issues
show
Bug introduced by Sergi Tur Badenas
It seems like you are loosely comparing $this->option('type') of type string|array|null against null; this is ambiguous if the string can be empty. Consider using a strict comparison === instead.
Loading history...
371
            return $this->createView();
372
        }
373
        if (strtolower($this->option('type')) == 'controller') {
374
            return $this->createController();
375
        }
376
        return $this->createResourceController();
377
    }
378
379
    /**
380
     * Create View.
381
     *
382
     * @param null $name
383
     */
384
    protected function createView($name = null)
385
    {
386
        if ($name == null) {
387
            $name = $this->action();
388
        }
389
        Artisan::call('make:view', [
390
            'name' => $name
391
        ]);
392
        $this->info('View ' . undot_path($name) .'.blade.php created.');
393
    }
394
395
    /**
396
     * Create regular controller.
397
     */
398
    protected function createController()
399
    {
400
        Artisan::call('make:controller', [
401
            'name' => $controller = $this->controllerWithoutMethod($this->action())
402
        ]);
403
        $this->addMethodToController($controller, $this->controllerMethod($this->action()));
404
        $this->info('Controller ' . $controller .' created.');
405
        $this->createView($this->argument('link'));
0 ignored issues
show
Bug introduced by Sergi Tur Badenas
It seems like $this->argument('link') targeting Illuminate\Console\Command::argument() can also be of type array or string; however, Acacha\AdminLTETemplateL...MakeRoute::createView() does only seem to accept null, maybe add an additional type check?

This check looks at variables that are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
406
    }
407
408
    /**
409
     * Create resource controller.
410
     */
411
    protected function createResourceController()
412
    {
413
        Artisan::call('make:controller', [
414
            'name' => $controller = $this->controllerWithoutMethod($this->action()),
415
            '--resource' => true
416
        ]);
417
        $this->info('Resource Controller ' . $controller .' created.');
418
        $this->createView($this->argument('link'));
0 ignored issues
show
Bug introduced by Sergi Tur Badenas
It seems like $this->argument('link') targeting Illuminate\Console\Command::argument() can also be of type array or string; however, Acacha\AdminLTETemplateL...MakeRoute::createView() does only seem to accept null, maybe add an additional type check?

This check looks at variables that are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
419
    }
420
421
    /**
422
     * Add method to controller.
423
     *
424
     * @param $controller
425
     * @param $controllerMethod     *
426
     */
427
    protected function addMethodToController($controller, $controllerMethod)
428
    {
429
        $tmpfile = $this->createTmpFileWithMethod($controllerMethod);
430
        $path = $this->getPath($tmpfile);
431
        add_file_into_file('\/\/', $path, app_path('Http/Controllers/' . $controller . '.php'));
432
    }
433
434
    /**
435
     * Crete tmp file with route to add.
436
     *
437
     * @param $controllerMethod
438
     * @return mixed
439
     */
440
    protected function createTmpFileWithMethod($controllerMethod)
441
    {
442
        $temp = tmpfile();
443
        fwrite($temp, $this->getMethodCode($controllerMethod));
444
        return $temp;
445
    }
446
447
    /**
448
     * Get method code.
449
     *
450
     * @param $controllerMethod
451
     * @return mixed
452
     */
453
    protected function getMethodCode($controllerMethod)
454
    {
455
        return $this->compiler->compile(
456
            $this->filesystem->get($this->getMethodStubPath()),
457
            [
458
                'METHOD' => $controllerMethod,
459
                'VIEW' => $this->argument('link')
460
            ]
461
        );
462
    }
463
464
    /**
465
     * Get method stub path.
466
     *
467
     * @return string
468
     */
469
    protected function getMethodStubPath()
470
    {
471
        return __DIR__ . '/stubs/method.stub';
472
    }
473
}
474