Passed
Pull Request — master (#532)
by Antonio Carlos
10:20
created

ModuleMake::createModels()   F

Complexity

Conditions 15
Paths 2592

Size

Total Lines 86
Code Lines 49

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 43
CRAP Score 15.1386

Importance

Changes 0
Metric Value
cc 15
eloc 49
c 0
b 0
f 0
nc 2592
nop 2
dl 0
loc 86
ccs 43
cts 47
cp 0.9149
crap 15.1386
rs 1.7499

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
namespace A17\Twill\Commands;
4
5
use Illuminate\Config\Repository as Config;
6
use Illuminate\Console\Command;
7
use Illuminate\Filesystem\Filesystem;
8
use Illuminate\Support\Collection;
9
use Illuminate\Support\Composer;
10
use Illuminate\Support\Str;
11
12
class ModuleMake extends Command
13
{
14
    /**
15
     * The name and signature of the console command.
16
     *
17
     * @var string
18
     */
19
    protected $signature = 'twill:module {moduleName}
20
        {--B|hasBlocks}
21
        {--T|hasTranslation}
22
        {--S|hasSlug}
23
        {--M|hasMedias}
24
        {--F|hasFiles}
25
        {--P|hasPosition}
26
        {--R|hasRevisions}
27
        {--all}';
28
29
    /**
30
     * The console command description.
31
     *
32
     * @var string
33
     */
34
    protected $description = 'Create a new Twill Module';
35
36
    /**
37
     * @var Filesystem
38
     */
39
    protected $files;
40
41
    /**
42
     * @var Composer
43
     */
44
    protected $composer;
45
46
    /**
47
     * @var string[]
48
     */
49
    protected $modelTraits;
50
51
    /**
52
     * @var string[]
53
     */
54
    protected $repositoryTraits;
55
56
    /**
57
     * @var Config
58
     */
59
    protected $config;
60
61
    /**
62
     * @param Filesystem $files
63
     * @param Composer $composer
64
     * @param Config $config
65
     */
66 55
    public function __construct(Filesystem $files, Composer $composer, Config $config)
67
    {
68 55
        parent::__construct();
69
70 55
        $this->files = $files;
71 55
        $this->composer = $composer;
72 55
        $this->config = $config;
73
74 55
        $this->blockable = false;
0 ignored issues
show
Bug Best Practice introduced by
The property blockable does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
75 55
        $this->translatable = false;
0 ignored issues
show
Bug Best Practice introduced by
The property translatable does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
76 55
        $this->sluggable = false;
0 ignored issues
show
Bug Best Practice introduced by
The property sluggable does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
77 55
        $this->mediable = false;
0 ignored issues
show
Bug Best Practice introduced by
The property mediable does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
78 55
        $this->fileable = false;
0 ignored issues
show
Bug Best Practice introduced by
The property fileable does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
79 55
        $this->sortable = false;
0 ignored issues
show
Bug Best Practice introduced by
The property sortable does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
80 55
        $this->revisionable = false;
0 ignored issues
show
Bug Best Practice introduced by
The property revisionable does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
81
82 55
        $this->defaultsAnswserToNo = false;
0 ignored issues
show
Bug Best Practice introduced by
The property defaultsAnswserToNo does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
83
84 55
        $this->modelTraits = ['HasBlocks', 'HasTranslation', 'HasSlug', 'HasMedias', 'HasFiles', 'HasRevisions', 'HasPosition'];
85 55
        $this->repositoryTraits = ['HandleBlocks', 'HandleTranslations', 'HandleSlugs', 'HandleMedias', 'HandleFiles', 'HandleRevisions'];
86 55
    }
87
88
    /**
89
     * Executes the console command.
90
     *
91
     * @return mixed
92
     */
93 1
    public function handle()
94
    {
95 1
        $moduleName = Str::plural(lcfirst($this->argument('moduleName')));
0 ignored issues
show
Bug introduced by
It seems like $this->argument('moduleName') can also be of type string[]; however, parameter $str of lcfirst() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

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

95
        $moduleName = Str::plural(lcfirst(/** @scrutinizer ignore-type */ $this->argument('moduleName')));
Loading history...
96
97 1
        $enabledOptions = Collection::make($this->options())->only([
98 1
            'hasBlocks',
99
            'hasTranslation',
100
            'hasSlug',
101
            'hasMedias',
102
            'hasFiles',
103
            'hasPosition',
104
            'hasRevisions',
105
        ])->filter(function ($enabled) {
106 1
            return $enabled;
107 1
        });
108
109 1
        if (count($enabledOptions) > 0) {
110 1
            $this->defaultsAnswserToNo = true;
0 ignored issues
show
Bug Best Practice introduced by
The property defaultsAnswserToNo does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
111
        }
112
113 1
        $this->blockable = $this->checkOption('hasBlocks');
0 ignored issues
show
Bug Best Practice introduced by
The property blockable does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
114 1
        $this->translatable = $this->checkOption('hasTranslation');
0 ignored issues
show
Bug Best Practice introduced by
The property translatable does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
115 1
        $this->sluggable = $this->checkOption('hasSlug');
0 ignored issues
show
Bug Best Practice introduced by
The property sluggable does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
116 1
        $this->mediable = $this->checkOption('hasMedias');
0 ignored issues
show
Bug Best Practice introduced by
The property mediable does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
117 1
        $this->fileable = $this->checkOption('hasFiles');
0 ignored issues
show
Bug Best Practice introduced by
The property fileable does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
118 1
        $this->sortable = $this->checkOption('hasPosition');
0 ignored issues
show
Bug Best Practice introduced by
The property sortable does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
119 1
        $this->revisionable = $this->checkOption('hasRevisions');
0 ignored issues
show
Bug Best Practice introduced by
The property revisionable does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
120
121
        $activeTraits = [
122 1
            $this->blockable,
123 1
            $this->translatable,
124 1
            $this->sluggable,
125 1
            $this->mediable,
126 1
            $this->fileable,
127 1
            $this->revisionable,
128 1
            $this->sortable,
129
        ];
130
131 1
        $modelName = Str::studly(Str::singular($moduleName));
132
133 1
        $this->createMigration($moduleName);
134 1
        $this->createModels($modelName, $activeTraits);
135 1
        $this->createRepository($modelName, $activeTraits);
136 1
        $this->createController($moduleName, $modelName);
137 1
        $this->createRequest($modelName);
138 1
        $this->createViews($moduleName);
139
140 1
        $this->info("Add Route::module('{$moduleName}'); to your admin routes file.");
141 1
        $this->info("Setup a new CMS menu item in config/twill-navigation.php:");
142
143 1
        $navTitle = Str::studly($moduleName);
144 1
        $this->info("
145 1
            '{$moduleName}' => [
146 1
                'title' => '{$navTitle}',
147
                'module' => true
148
            ]
149
        ");
150
151 1
        $this->info("Migrate your database.\n");
152
153 1
        $this->info("Enjoy.");
154
155 1
        $this->composer->dumpAutoloads();
156 1
    }
157
158
    /**
159
     * Creates a new module database migration file.
160
     *
161
     * @param string $moduleName
162
     * @return void
163
     * @throws \Illuminate\Contracts\Filesystem\FileNotFoundException
164
     */
165 1
    private function createMigration($moduleName = 'items')
166
    {
167 1
        $table = Str::snake($moduleName);
168 1
        $tableClassName = Str::studly($table);
169
170 1
        $className = "Create{$tableClassName}Tables";
0 ignored issues
show
Unused Code introduced by
The assignment to $className is dead and can be removed.
Loading history...
171
172 1
        $migrationName = 'create_' . $table . '_tables';
173
174 1
        if (!count(glob(database_path('migrations/*' . $migrationName . '.php')))) {
0 ignored issues
show
Bug introduced by
It seems like glob(database_path('migr...igrationName . '.php')) can also be of type false; however, parameter $var of count() does only seem to accept Countable|array, maybe add an additional type check? ( Ignorable by Annotation )

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

174
        if (!count(/** @scrutinizer ignore-type */ glob(database_path('migrations/*' . $migrationName . '.php')))) {
Loading history...
175 1
            $migrationPath = $this->laravel->databasePath() . '/migrations';
176
177 1
            $fullPath = $this->laravel['migration.creator']->create($migrationName, $migrationPath);
178
179 1
            $stub = str_replace(
180 1
                ['{{table}}', '{{singularTableName}}', '{{tableClassName}}'],
181 1
                [$table, Str::singular($table), $tableClassName],
182 1
                $this->files->get(__DIR__ . '/stubs/migration.stub')
183
            );
184
185 1
            if ($this->translatable) {
186 1
                $stub = preg_replace('/{{!hasTranslation}}[\s\S]+?{{\/!hasTranslation}}/', '', $stub);
187
            } else {
188
                $stub = str_replace([
189
                    '{{!hasTranslation}}',
190
                    '{{/!hasTranslation}}',
191
                ], '', $stub);
192
            }
193
194 1
            $stub = $this->renderStubForOption($stub, 'hasTranslation', $this->translatable);
195 1
            $stub = $this->renderStubForOption($stub, 'hasSlug', $this->sluggable);
196 1
            $stub = $this->renderStubForOption($stub, 'hasRevisions', $this->revisionable);
197 1
            $stub = $this->renderStubForOption($stub, 'hasPosition', $this->sortable);
198
199 1
            $stub = preg_replace('/\}\);[\s\S]+?Schema::create/', "});\n\n        Schema::create", $stub);
200
201 1
            $this->files->put($fullPath, $stub);
202
203 1
            $this->info("Migration created successfully! Add some fields!");
204
        }
205 1
    }
206
207
    /**
208
     * Creates new model class files for the given model name and traits.
209
     *
210
     * @param string $modelName
211
     * @param array $activeTraits
212
     * @throws \Illuminate\Contracts\Filesystem\FileNotFoundException
213
     */
214 1
    private function createModels($modelName = 'Item', $activeTraits = [])
215
    {
216 1
        $modelClassName = $modelName;
217
218 1
        if (!$this->files->isDirectory(twill_path('Models'))) {
219
            $this->files->makeDirectory(twill_path('Models'));
220
        }
221
222 1
        if ($this->translatable) {
223 1
            if (!$this->files->isDirectory(twill_path('Models/Translations'))) {
224
                $this->files->makeDirectory(twill_path('Models/Translations'));
225
            }
226
227 1
            $modelTranslationClassName = $modelName . 'Translation';
228
229 1
            $stub = str_replace(
230 1
                ['{{modelTranslationClassName}}', '{{modelClassName}}'],
231 1
                [$modelTranslationClassName, $modelClassName],
232 1
                $this->files->get(__DIR__ . '/stubs/model_translation.stub')
233
            );
234
235 1
            $this->files->put(twill_path('Models/Translations/' . $modelTranslationClassName . '.php'), $stub);
236
        }
237
238 1
        if ($this->sluggable) {
239 1
            if (!$this->files->isDirectory(twill_path('Models/Slugs'))) {
240
                $this->files->makeDirectory(twill_path('Models/Slugs'));
241
            }
242
243 1
            $modelSlugClassName = $modelName . 'Slug';
244
245 1
            $stub = str_replace(['{{modelSlugClassName}}', '{{modelName}}'], [$modelSlugClassName, Str::snake($modelName)], $this->files->get(__DIR__ . '/stubs/model_slug.stub'));
246
247 1
            $this->files->put(twill_path('Models/Slugs/' . $modelSlugClassName . '.php'), $stub);
248
        }
249
250 1
        if ($this->revisionable) {
251 1
            if (!$this->files->isDirectory(twill_path('Models/Revisions'))) {
252
                $this->files->makeDirectory(twill_path('Models/Revisions'));
253
            }
254
255 1
            $modelRevisionClassName = $modelName . 'Revision';
256
257 1
            $stub = str_replace(['{{modelRevisionClassName}}', '{{modelName}}'], [$modelRevisionClassName, Str::snake($modelName)], $this->files->get(__DIR__ . '/stubs/model_revision.stub'));
258
259 1
            $this->files->put(twill_path('Models/Revisions/' . $modelRevisionClassName . '.php'), $stub);
260
        }
261
262 1
        $activeModelTraits = [];
263
264 1
        foreach ($activeTraits as $index => $traitIsActive) {
265 1
            if ($traitIsActive) {
266 1
                !isset($this->modelTraits[$index]) ?: $activeModelTraits[] = $this->modelTraits[$index];
267
            }
268
        }
269
270 1
        $activeModelTraitsString = empty($activeModelTraits) ? '' : 'use ' . rtrim(implode(', ', $activeModelTraits), ', ') . ';';
271
272 1
        $activeModelTraitsImports = empty($activeModelTraits) ? '' : "use A17\Twill\Models\Behaviors\\" . implode(";\nuse A17\Twill\Models\Behaviors\\", $activeModelTraits) . ";";
273
274 1
        $activeModelImplements = $this->sortable ? 'implements Sortable' : '';
275
276 1
        if ($this->sortable) {
277 1
            $activeModelTraitsImports .= "\nuse A17\Twill\Models\Behaviors\Sortable;";
278
        }
279
280 1
        $stub = str_replace([
281 1
            '{{modelClassName}}',
282
            '{{modelTraits}}',
283
            '{{modelImports}}',
284
            '{{modelImplements}}',
285
        ], [
286 1
            $modelClassName,
287 1
            $activeModelTraitsString,
288 1
            $activeModelTraitsImports,
289 1
            $activeModelImplements,
290 1
        ], $this->files->get(__DIR__ . '/stubs/model.stub'));
291
292 1
        $stub = $this->renderStubForOption($stub, 'hasTranslation', $this->translatable);
293 1
        $stub = $this->renderStubForOption($stub, 'hasSlug', $this->sluggable);
294 1
        $stub = $this->renderStubForOption($stub, 'hasMedias', $this->mediable);
295 1
        $stub = $this->renderStubForOption($stub, 'hasPosition', $this->sortable);
296
297 1
        $this->files->put(twill_path('Models/' . $modelClassName . '.php'), $stub);
298
299 1
        $this->info("Models created successfully! Fill your fillables!");
300 1
    }
301
302 1
    private function renderStubForOption($stub, $option, $enabled)
303
    {
304 1
        if ($enabled) {
305 1
            $stub = str_replace([
306 1
                '{{' . $option . '}}',
307 1
                '{{/' . $option . '}}',
308 1
            ], '', $stub);
309
        } else {
310
            $stub = preg_replace('/{{' . $option . '}}[\s\S]+?{{\/' . $option . '}}/', '', $stub);
311
        }
312
313 1
        return $stub;
314
    }
315
316
    /**
317
     * Creates new repository class file for the given model name.
318
     *
319
     * @param string $modelName
320
     * @param array $activeTraits
321
     * @return void
322
     * @throws \Illuminate\Contracts\Filesystem\FileNotFoundException
323
     */
324 1
    private function createRepository($modelName = 'Item', $activeTraits = [])
325
    {
326 1
        if (!$this->files->isDirectory(twill_path('Repositories'))) {
327
            $this->files->makeDirectory(twill_path('Repositories'));
328
        }
329
330 1
        $repositoryClassName = $modelName . 'Repository';
331
332 1
        $activeRepositoryTraits = [];
333
334 1
        foreach ($activeTraits as $index => $traitIsActive) {
335 1
            if ($traitIsActive) {
336 1
                !isset($this->repositoryTraits[$index]) ?: $activeRepositoryTraits[] = $this->repositoryTraits[$index];
337
            }
338
        }
339
340 1
        $activeRepositoryTraitsString = empty($activeRepositoryTraits) ? '' : 'use ' . (empty($activeRepositoryTraits) ? "" : rtrim(implode(', ', $activeRepositoryTraits), ', ') . ';');
341
342 1
        $activeRepositoryTraitsImports = empty($activeRepositoryTraits) ? '' : "use A17\Twill\Repositories\Behaviors\\" . implode(";\nuse A17\Twill\Repositories\Behaviors\\", $activeRepositoryTraits) . ";";
343
344 1
        $stub = str_replace(['{{repositoryClassName}}', '{{modelName}}', '{{repositoryTraits}}', '{{repositoryImports}}'], [$repositoryClassName, $modelName, $activeRepositoryTraitsString, $activeRepositoryTraitsImports], $this->files->get(__DIR__ . '/stubs/repository.stub'));
345
346 1
        $this->files->put(twill_path('Repositories/' . $repositoryClassName . '.php'), $stub);
347
348 1
        $this->info("Repository created successfully! Control all the things!");
349 1
    }
350
351
    /**
352
     * Create a new controller class file for the given module name and model name.
353
     *
354
     * @param string $moduleName
355
     * @param string $modelName
356
     * @return void
357
     * @throws \Illuminate\Contracts\Filesystem\FileNotFoundException
358
     */
359 1
    private function createController($moduleName = 'items', $modelName = 'Item')
360
    {
361 1
        if (!$this->files->isDirectory(twill_path('Http/Controllers/Admin'))) {
362
            $this->files->makeDirectory(twill_path('Http/Controllers/Admin'));
363
        }
364
365 1
        $controllerClassName = $modelName . 'Controller';
366
367 1
        $stub = str_replace(
368 1
            ['{{moduleName}}', '{{controllerClassName}}'],
369 1
            [$moduleName, $controllerClassName],
370 1
            $this->files->get(__DIR__ . '/stubs/controller.stub')
371
        );
372
373 1
        $this->files->put(twill_path('Http/Controllers/Admin/' . $controllerClassName . '.php'), $stub);
374
375 1
        $this->info("Controller created successfully! Define your index/browser/form endpoints options!");
376 1
    }
377
378
    /**
379
     * Creates a new request class file for the given model name.
380
     *
381
     * @param string $modelName
382
     * @return void
383
     * @throws \Illuminate\Contracts\Filesystem\FileNotFoundException
384
     */
385 1
    private function createRequest($modelName = 'Item')
386
    {
387 1
        if (!$this->files->isDirectory(twill_path('Http/Requests/Admin'))) {
388
            $this->files->makeDirectory(twill_path('Http/Requests/Admin'), 0755, true);
389
        }
390
391 1
        $requestClassName = $modelName . 'Request';
392
393 1
        $stub = str_replace('{{requestClassName}}', $requestClassName, $this->files->get(__DIR__ . '/stubs/request.stub'));
394
395 1
        $this->files->put(twill_path('Http/Requests/Admin/' . $requestClassName . '.php'), $stub);
396
397 1
        $this->info("Form request created successfully! Add some validation rules!");
398 1
    }
399
400
    /**
401
     * Creates appropriate module Blade view files.
402
     *
403
     * @param string $moduleName
404
     * @return void
405
     * @throws \Illuminate\Contracts\Filesystem\FileNotFoundException
406
     */
407 1
    private function createViews($moduleName = 'items')
408
    {
409 1
        $viewsPath = $this->config->get('view.paths')[0] . '/admin/' . $moduleName;
410
411 1
        if (!$this->files->isDirectory($viewsPath)) {
412 1
            $this->files->makeDirectory($viewsPath, 0755, true);
413
        }
414
415 1
        $formView = $this->translatable ? 'form_translatable' : 'form';
416
417 1
        $this->files->put($viewsPath . '/form.blade.php', $this->files->get(__DIR__ . '/stubs/' . $formView . '.blade.stub'));
418
419 1
        $this->info("Form view created successfully! Include your form fields using @formField directives!");
420 1
    }
421
422 1
    private function checkOption($option)
423
    {
424 1
        if ($this->option($option) || $this->option('all')) {
425 1
            return true;
426
        }
427
428
        $questions = [
429
            'hasBlocks' => 'Do you need to use the block editor on this module?',
430
            'hasTranslation' => 'Do you need to translate content on this module?',
431
            'hasSlug' => 'Do you need to generate slugs on this module?',
432
            'hasMedias' => 'Do you need to attach images on this module?',
433
            'hasFiles' => 'Do you need to attach files on this module?',
434
            'hasPosition' => 'Do you need to manage the position of records on this module?',
435
            'hasRevisions' => 'Do you need to enable revisions on this module?',
436
        ];
437
438
        return 'yes' === $this->choice($questions[$option], ['no', 'yes'], $this->defaultsAnswserToNo ? 0 : 1);
439
    }
440
}
441