Passed
Push — master ( c36ed8...8867a0 )
by Quentin
13:25 queued 05:48
created

ModuleMake::createModels()   F

Complexity

Conditions 15
Paths 2592

Size

Total Lines 82
Code Lines 46

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 40
CRAP Score 15.1689

Importance

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