ModuleMake::createModels()   F
last analyzed

Complexity

Conditions 15
Paths 768

Size

Total Lines 92
Code Lines 56

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 36
CRAP Score 15.0044

Importance

Changes 0
Metric Value
cc 15
eloc 56
c 0
b 0
f 0
nc 768
nop 2
dl 0
loc 92
ccs 36
cts 37
cp 0.973
crap 15.0044
rs 2.0722

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\Filesystem\Filesystem;
7
use Illuminate\Support\Collection;
8
use Illuminate\Support\Composer;
9
use Illuminate\Support\Facades\File;
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:make:module {moduleName}
20
        {--B|hasBlocks}
21
        {--T|hasTranslation}
22
        {--S|hasSlug}
23
        {--M|hasMedias}
24
        {--F|hasFiles}
25
        {--P|hasPosition}
26
        {--R|hasRevisions}
27
        {--N|hasNesting}
28
        {--all}';
29
30
    /**
31
     * The console command description.
32
     *
33
     * @var string
34
     */
35
    protected $description = 'Create a new Twill Module';
36
37
    /**
38
     * @var Filesystem
39
     */
40
    protected $files;
41
42
    /**
43
     * @var Composer
44
     */
45
    protected $composer;
46
47
    /**
48
     * @var string[]
49
     */
50
    protected $modelTraits;
51
52
    /**
53
     * @var string[]
54
     */
55
    protected $repositoryTraits;
56
57
    /**
58
     * @var Config
59
     */
60
    protected $config;
61
62
    /**
63
     * @var bool
64
     */
65
    protected $blockable;
66
67
    /**
68
     * @var bool
69
     */
70
    protected $translatable;
71
72
    /**
73
     * @var bool
74
     */
75
    protected $sluggable;
76
77
    /**
78
     * @var bool
79
     */
80
    protected $mediable;
81
82
    /**
83
     * @var bool
84
     */
85
    protected $fileable;
86
87
    /**
88
     * @var bool
89
     */
90
    protected $sortable;
91
92
    /**
93
     * @var bool
94
     */
95
    protected $revisionable;
96
97
    /**
98
     * @var bool
99
     */
100
    protected $nestable;
101
102
    /**
103
     * @var bool
104
     */
105 69
    protected $defaultsAnswserToNo;
106
107 69
    /**
108
     * @var bool
109 69
     */
110 69
    protected $isCapsule = false;
111 69
112
    /**
113 69
     * @var bool
114 69
     */
115 69
    protected $isSingleton = false;
116 69
117 69
    /**
118 69
     * @var string
119 69
     */
120
    protected $moduleBasePath;
121 69
122
    /**
123 69
     * @var string
124 69
     */
125 69
    protected $capsule;
126
127
    /**
128
     * @param Filesystem $files
129
     * @param Composer $composer
130
     * @param Config $config
131
     */
132 1
    public function __construct(Filesystem $files, Composer $composer, Config $config)
133
    {
134 1
        parent::__construct();
135
136 1
        $this->files = $files;
137 1
        $this->composer = $composer;
138
        $this->config = $config;
139
140
        $this->blockable = false;
141
        $this->translatable = false;
142
        $this->sluggable = false;
143
        $this->mediable = false;
144 1
        $this->fileable = false;
145 1
        $this->sortable = false;
146 1
        $this->revisionable = false;
147
        $this->nestable = false;
148 1
149 1
        $this->defaultsAnswserToNo = false;
150
151
        $this->modelTraits = ['HasBlocks', 'HasTranslation', 'HasSlug', 'HasMedias', 'HasFiles', 'HasRevisions', 'HasPosition', 'HasNesting'];
152 1
        $this->repositoryTraits = ['HandleBlocks', 'HandleTranslations', 'HandleSlugs', 'HandleMedias', 'HandleFiles', 'HandleRevisions', '', 'HandleNesting'];
153 1
    }
154 1
155 1
    protected function checkCapsuleDirectory($dir)
156 1
    {
157 1
        if (file_exists($dir)) {
158 1
            if (!$this->option('force')) {
159
                $answer = $this->choice("Capsule path exists ({$dir}). Erase and overwrite?",
160
                    ['no', 'yes'], $this->defaultsAnswserToNo
161 1
                    ? 0
162 1
                    : 1);
163 1
            }
164 1
165 1
            if ('yes' === ($answer ?? 'no') || $this->option('force')) {
166 1
                File::deleteDirectory($dir);
167 1
168
                if (file_exists($dir)) {
169
                    $this->info("Directory could not be deleted. Aborted.");
170 1
                    die;
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
171
                }
172 1
            } else {
173 1
                $this->info("Aborted");
174 1
175 1
                die;
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
176 1
            }
177 1
        }
178
    }
179 1
180 1
    /**
181
     * Executes the console command.
182 1
     *
183 1
     * @return mixed
184 1
     */
185 1
    public function handle()
186
    {
187
        // e.g. newsItems
188
        $moduleName = Str::camel(Str::plural(lcfirst($this->argument('moduleName'))));
0 ignored issues
show
Bug introduced by
It seems like $this->argument('moduleName') can also be of type array and null; however, parameter $string 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

188
        $moduleName = Str::camel(Str::plural(lcfirst(/** @scrutinizer ignore-type */ $this->argument('moduleName'))));
Loading history...
189
190 1
        // e.g. newsItem
191
        $singularModuleName = Str::camel(lcfirst($this->argument('moduleName')));
192 1
193
        // e.g. NewsItems
194 1
        $moduleTitle = Str::studly($moduleName);
195 1
196
        // e.g. NewsItem
197
        $modelName = Str::studly(Str::singular($moduleName));
198
199
        $this->capsule = app('twill.capsules.manager')->makeCapsule(['name' => $moduleName], config("twill.capsules.path"));
0 ignored issues
show
Bug introduced by
The method makeCapsule() does not exist on Illuminate\Contracts\Foundation\Application. ( Ignorable by Annotation )

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

199
        $this->capsule = app('twill.capsules.manager')->/** @scrutinizer ignore-call */ makeCapsule(['name' => $moduleName], config("twill.capsules.path"));

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
200
201
        $enabledOptions = Collection::make($this->options())->only([
202
            'hasBlocks',
203
            'hasTranslation',
204 1
            'hasSlug',
205
            'hasMedias',
206 1
            'hasFiles',
207 1
            'hasPosition',
208
            'hasRevisions',
209 1
            'hasNesting',
210
        ])->filter(function ($enabled) {
211 1
            return $enabled;
212
        });
213 1
214 1
        if (count($enabledOptions) > 0) {
215
            $this->defaultsAnswserToNo = true;
216 1
        }
217
218 1
        $this->blockable = $this->checkOption('hasBlocks');
219 1
        $this->translatable = $this->checkOption('hasTranslation');
220 1
        $this->sluggable = $this->checkOption('hasSlug');
221 1
        $this->mediable = $this->checkOption('hasMedias');
222
        $this->fileable = $this->checkOption('hasFiles');
223
        $this->sortable = $this->checkOption('hasPosition');
224 1
        $this->revisionable = $this->checkOption('hasRevisions');
225 1
        $this->nestable = $this->checkOption('hasNesting');
226
227
        if ($this->nestable) {
228
            $this->sortable = true;
229
        }
230
231
        $activeTraits = [
232
            $this->blockable,
233 1
            $this->translatable,
234 1
            $this->sluggable,
235 1
            $this->mediable,
236 1
            $this->fileable,
237
            $this->revisionable,
238 1
            $this->sortable,
239
            $this->nestable,
240 1
        ];
241
242 1
        $this->createCapsuleNamespace($moduleTitle, $modelName);
243
        $this->createCapsulePath($moduleTitle, $modelName);
244 1
245
        $this->createMigration($moduleName);
246
        $this->createModels($modelName, $activeTraits);
247
        $this->createRepository($modelName, $activeTraits);
248
        $this->createController($moduleName, $modelName);
249
        $this->createRequest($modelName);
250
        $this->createViews($moduleName);
251
252
        if ($this->isCapsule) {
253 1
            $this->createCapsuleRoutes($moduleName);
0 ignored issues
show
Unused Code introduced by
The call to A17\Twill\Commands\Modul...::createCapsuleRoutes() has too many arguments starting with $moduleName. ( Ignorable by Annotation )

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

253
            $this->/** @scrutinizer ignore-call */ 
254
                   createCapsuleRoutes($moduleName);

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
254
            $this->createCapsuleSeed($moduleName);
255 1
        } elseif ($this->isSingleton) {
256
            $this->createSingletonSeed($modelName);
257 1
            $this->info("\nAdd to routes/admin.php:\n");
258
            $this->info("    Route::singleton('{$singularModuleName}');\n");
259 1
        } else {
260 1
            $this->info("\nAdd to routes/admin.php:\n");
261
            $this->info("    Route::module('{$moduleName}');\n");
262 1
        }
263
264 1
        $navModuleName = $this->isSingleton ? $singularModuleName : $moduleName;
265 1
        $navTitle = $this->isSingleton ? $modelName : $moduleTitle;
266 1
        $navType = $this->isSingleton ? 'singleton' : 'module';
267 1
268
        $this->info("Setup a new CMS menu item in config/twill-navigation.php:\n");
269
        $this->info("    '{$navModuleName}' => [");
270 1
        $this->info("        'title' => '{$navTitle}',");
271
        $this->info("        '{$navType}' => true,");
272
        $this->info("    ],\n");
273 1
274 1
        if ($this->isCapsule) {
275
            $this->info("Setup your new Capsule in config/twill.php:\n");
276 1
            $this->info("    'capsules' => [");
277
            $this->info("        'list' => [");
278 1
            $this->info("            [");
279
            $this->info("                'name' => '{$this->capsule['name']}',");
280 1
            $this->info("                'enabled' => true,");
281
            $this->info("            ],");
282
            $this->info("        ],");
283 1
            $this->info("    ],\n");
284 1
        }
285
286 1
        if ($this->isSingleton) {
287
            $this->info("Migrate your database & seed your singleton module:\n");
288 1
            $this->info("    php artisan migrate\n");
289
            $this->info("    php artisan db:seed {$modelName}Seeder\n");
290 1
        } else {
291
            $this->info("Migrate your database.\n");
292
        }
293 1
294
        $this->info("Enjoy.");
295 1
296 1
        if ($this->nestable && !class_exists('\Kalnoy\Nestedset\NestedSet')) {
297 1
            $this->warn("\nTo support module nesting, you must install the `kalnoy/nestedset` package:");
298
            $this->warn("\n    composer require kalnoy/nestedset\n");
299
        }
300
301 1
        $this->composer->dumpAutoloads();
302
    }
303 1
304
    /**
305 1
     * Creates a new module database migration file.
306
     *
307 1
     * @param string $moduleName
308 1
     * @return void
309
     * @throws \Illuminate\Contracts\Filesystem\FileNotFoundException
310
     */
311 1
    private function createMigration($moduleName = 'items')
312 1
    {
313
        $table = Str::snake($moduleName);
314
        $tableClassName = Str::studly($table);
315
316
        $className = "Create{$tableClassName}Tables";
0 ignored issues
show
Unused Code introduced by
The assignment to $className is dead and can be removed.
Loading history...
317 1
318 1
        $migrationName = 'create_' . $table . '_tables';
319 1
320 1
        if (!count(glob($this->databasePath('migrations/*' . $migrationName . '.php')))) {
321 1
            $migrationPath = $this->databasePath() . '/migrations';
322
323 1
            $this->makeDir($migrationPath);
324 1
325 1
            $fullPath = $this->laravel['migration.creator']->create($migrationName, $migrationPath);
326 1
327
            $stub = str_replace(
328 1
                ['{{table}}', '{{singularTableName}}', '{{tableClassName}}'],
329
                [$table, Str::singular($table), $tableClassName],
330 1
                $this->files->get(__DIR__ . '/stubs/migration.stub')
331 1
            );
332
333 1
            if ($this->translatable) {
334
                $stub = preg_replace('/{{!hasTranslation}}[\s\S]+?{{\/!hasTranslation}}/', '', $stub);
335 1
            } else {
336 1
                $stub = str_replace([
337 1
                    '{{!hasTranslation}}',
338 1
                    '{{/!hasTranslation}}',
339 1
                ], '', $stub);
340
            }
341
342
            $stub = $this->renderStubForOption($stub, 'hasTranslation', $this->translatable);
343
            $stub = $this->renderStubForOption($stub, 'hasSlug', $this->sluggable);
344 1
            $stub = $this->renderStubForOption($stub, 'hasRevisions', $this->revisionable);
345
            $stub = $this->renderStubForOption($stub, 'hasPosition', $this->sortable);
346
            $stub = $this->renderStubForOption($stub, 'hasNesting', $this->nestable);
347
348
            $stub = preg_replace('/\}\);[\s\S]+?Schema::create/', "});\n\n        Schema::create", $stub);
349
350
            $this->files->put($fullPath, $stub);
351
352
            $this->info("Migration created successfully! Add some fields!");
353
        }
354
    }
355 1
356
    /**
357 1
     * Creates new model class files for the given model name and traits.
358
     *
359 1
     * @param string $modelName
360
     * @param array $activeTraits
361 1
     * @throws \Illuminate\Contracts\Filesystem\FileNotFoundException
362
     */
363 1
    private function createModels($modelName = 'Item', $activeTraits = [])
364 1
    {
365 1
        $modelClassName = $this->namespace('models', 'Models', $modelName);
366
367
        $modelsDir = $this->isCapsule ? $this->capsule['models_dir'] : 'Models';
368
369 1
        $this->makeTwillDirectory($modelsDir);
370
371 1
        if ($this->translatable) {
372
            $this->makeTwillDirectory($baseDir = $this->isCapsule ? $modelsDir : "{$modelsDir}/Translations");
373 1
374
            $modelTranslationClassName = $modelName . 'Translation';
375 1
376
            $stub = str_replace(
377 1
                ['{{modelTranslationClassName}}', '{{modelClassWithNamespace}}', '{{modelClassName}}', '{{namespace}}', '{{baseTranslationModel}}'],
378 1
                [$modelTranslationClassName, $modelClassName, $modelName, $this->namespace('models', 'Models\Translations'), config('twill.base_translation_model')],
379
                $this->files->get(__DIR__ . '/stubs/model_translation.stub')
380
            );
381
382
            twill_put_stub(twill_path("{$baseDir}/" . $modelTranslationClassName . '.php'), $stub);
383
        }
384
385
        if ($this->sluggable) {
386
            $this->makeTwillDirectory($baseDir = $this->isCapsule ? $modelsDir : "{$modelsDir}/Slugs");
387
388 1
            $modelSlugClassName = $modelName . 'Slug';
389
390 1
            $stub = str_replace(
391
                ['{{modelSlugClassName}}', '{{modelClassWithNamespace}}', '{{modelName}}', '{{namespace}}', '{{baseSlugModel}}'],
392 1
                [$modelSlugClassName, $modelClassName, Str::snake($modelName), $this->namespace('models', 'Models\Slugs'), config('twill.base_slug_model')],
393
                $this->files->get(__DIR__ . '/stubs/model_slug.stub')
394 1
            );
395 1
396 1
            twill_put_stub(twill_path("{$baseDir}/" . $modelSlugClassName . '.php'), $stub);
397 1
        }
398
399
        if ($this->revisionable) {
400 1
            $this->makeTwillDirectory($baseDir = $this->isCapsule ? $modelsDir : "{$modelsDir}/Revisions");
401
402 1
            $modelRevisionClassName = $modelName . 'Revision';
403 1
404
            $stub = str_replace(
405
                ['{{modelRevisionClassName}}', '{{modelClassWithNamespace}}', '{{modelName}}', '{{namespace}}', '{{baseRevisionModel}}'],
406
                [$modelRevisionClassName, $modelClassName, Str::snake($modelName), $this->namespace('models', 'Models\Revisions'), config('twill.base_revision_model')],
407
                $this->files->get(__DIR__ . '/stubs/model_revision.stub')
408
            );
409
410
            twill_put_stub(twill_path("{$baseDir}/" . $modelRevisionClassName . '.php'), $stub);
411
        }
412 1
413
        $activeModelTraits = [];
414 1
415
        foreach ($activeTraits as $index => $traitIsActive) {
416 1
            if ($traitIsActive) {
417
                !isset($this->modelTraits[$index]) ?: $activeModelTraits[] = $this->modelTraits[$index];
418 1
            }
419
        }
420 1
421
        $activeModelTraitsString = empty($activeModelTraits) ? '' : 'use ' . rtrim(implode(', ', $activeModelTraits), ', ') . ';';
422 1
423 1
        $activeModelTraitsImports = empty($activeModelTraits) ? '' : "use A17\Twill\Models\Behaviors\\" . implode(";\nuse A17\Twill\Models\Behaviors\\", $activeModelTraits) . ";";
424
425
        $activeModelImplements = $this->sortable ? 'implements Sortable' : '';
426
427
        if ($this->sortable) {
428
            $activeModelTraitsImports .= "\nuse A17\Twill\Models\Behaviors\Sortable;";
429
        }
430
431
        $stub = str_replace([
432 1
            '{{modelClassName}}',
433
            '{{modelTraits}}',
434 1
            '{{modelImports}}',
435
            '{{modelImplements}}',
436 1
            '{{namespace}}',
437
            '{{baseModel}}',
438 1
        ], [
439
            $modelName,
440 1
            $activeModelTraitsString,
441
            $activeModelTraitsImports,
442 1
            $activeModelImplements,
443 1
            $this->namespace('models', 'Models'),
444
            config('twill.base_model'),
445 1
        ], $this->files->get(__DIR__ . '/stubs/model.stub'));
446
447 1
        $stub = $this->renderStubForOption($stub, 'hasTranslation', $this->translatable);
448 1
        $stub = $this->renderStubForOption($stub, 'hasSlug', $this->sluggable);
449
        $stub = $this->renderStubForOption($stub, 'hasMedias', $this->mediable);
450
        $stub = $this->renderStubForOption($stub, 'hasPosition', $this->sortable);
451
452
        twill_put_stub(twill_path("{$modelsDir}/" . $modelName . '.php'), $stub);
453
454
        $this->info("Models created successfully! Fill your fillables!");
455
    }
456
457
    private function renderStubForOption($stub, $option, $enabled)
458
    {
459
        if ($enabled) {
460
            $stub = str_replace([
461
                '{{' . $option . '}}',
462
                '{{/' . $option . '}}',
463
            ], '', $stub);
464
        } else {
465
            $stub = preg_replace('/{{' . $option . '}}[\s\S]+?{{\/' . $option . '}}/', '', $stub);
466
        }
467
468
        return $stub;
469
    }
470
471
    /**
472
     * Creates new repository class file for the given model name.
473
     *
474
     * @param string $modelName
475
     * @param array $activeTraits
476
     * @return void
477
     * @throws \Illuminate\Contracts\Filesystem\FileNotFoundException
478
     */
479
    private function createRepository($modelName = 'Item', $activeTraits = [])
480
    {
481
        $modelsDir = $this->isCapsule ? $this->capsule['repositories_dir'] : 'Repositories';
482
483
        $modelClass = $this->isCapsule ? $this->capsule['model'] : config('twill.namespace') . "\Models\\{$this->capsule['singular']}";
484
485
        $this->makeTwillDirectory($modelsDir);
486
487
        $repositoryClassName = $modelName . 'Repository';
488
489
        $activeRepositoryTraits = [];
490
491
        foreach ($activeTraits as $index => $traitIsActive) {
492
            if ($traitIsActive) {
493
                !isset($this->repositoryTraits[$index]) ?: $activeRepositoryTraits[] = $this->repositoryTraits[$index];
494
            }
495
        }
496
497
        $activeRepositoryTraits = array_filter($activeRepositoryTraits);
498
499
        $activeRepositoryTraitsString = empty($activeRepositoryTraits) ? '' : 'use ' . (empty($activeRepositoryTraits) ? "" : rtrim(implode(', ', $activeRepositoryTraits), ', ') . ';');
500
501
        $activeRepositoryTraitsImports = empty($activeRepositoryTraits) ? '' : "use A17\Twill\Repositories\Behaviors\\" . implode(";\nuse A17\Twill\Repositories\Behaviors\\", $activeRepositoryTraits) . ";";
502
503
        $stub = str_replace(
504
            ['{{repositoryClassName}}', '{{modelName}}', '{{repositoryTraits}}', '{{repositoryImports}}', '{{namespace}}', '{{modelClass}}', '{{baseRepository}}'],
505
            [$repositoryClassName, $modelName, $activeRepositoryTraitsString, $activeRepositoryTraitsImports, $this->namespace('repositories', 'Repositories'), $modelClass, config('twill.base_repository')],
506
            $this->files->get(__DIR__ . '/stubs/repository.stub')
507
        );
508
509
        twill_put_stub(twill_path("{$modelsDir}/" . $repositoryClassName . '.php'), $stub);
510
511
        $this->info("Repository created successfully! Control all the things!");
512
    }
513
514
    /**
515
     * Create a new controller class file for the given module name and model name.
516
     *
517
     * @param string $moduleName
518
     * @param string $modelName
519
     * @return void
520
     * @throws \Illuminate\Contracts\Filesystem\FileNotFoundException
521
     */
522
    private function createController($moduleName = 'items', $modelName = 'Item')
523
    {
524
        $controllerClassName = $modelName . 'Controller';
525
526
        $dir = $this->isCapsule ? $this->capsule['controllers_dir'] : 'Http/Controllers/Admin';
527
528
        if ($this->isSingleton) {
529
            $baseController = config('twill.base_singleton_controller');
530
        } elseif ($this->nestable) {
531
            $baseController = config('twill.base_nested_controller');
532
        } else {
533
            $baseController = config('twill.base_controller');
534
        }
535
536
        $this->makeTwillDirectory($dir);
537
538
        $stub = str_replace(
539
            ['{{moduleName}}', '{{controllerClassName}}', '{{namespace}}', '{{baseController}}'],
540
            [$moduleName, $controllerClassName, $this->namespace('controllers', 'Http\Controllers\Admin'), $baseController],
541
            $this->files->get(__DIR__ . '/stubs/controller.stub')
542
        );
543
544
        $permalinkOption = '';
545
        $reorderOption = '';
546
547
        if (!$this->sluggable) {
548
            $permalinkOption = "'permalink' => false,";
549
        }
550
551
        if ($this->nestable) {
552
            $reorderOption = "'reorder' => true,";
553
554
            $stub = str_replace(['{{hasNesting}}', '{{/hasNesting}}'], '', $stub);
555
        } else {
556
            $stub = preg_replace('/{{hasNesting}}[\s\S]+?{{\/hasNesting}}/', '', $stub);
557
        }
558
559
        $stub = str_replace(
560
            ['{{permalinkOption}}', '{{reorderOption}}'],
561
            [$permalinkOption, $reorderOption],
562
            $stub
563
        );
564
565
        // Remove lines including only whitespace, leave true empty lines untouched
566
        $stub = preg_replace('/^[\s]+\n/m', '', $stub);
567
568
        twill_put_stub(twill_path("{$dir}/" . $controllerClassName . '.php'), $stub);
569
570
        $this->info("Controller created successfully! Define your index/browser/form endpoints options!");
571
    }
572
573
    /**
574
     * Creates a new request class file for the given model name.
575
     *
576
     * @param string $modelName
577
     * @return void
578
     * @throws \Illuminate\Contracts\Filesystem\FileNotFoundException
579
     */
580
    private function createRequest($modelName = 'Item')
581
    {
582
        $dir = $this->isCapsule ? $this->capsule['requests_dir'] : 'Http/Requests/Admin';
583
584
        $this->makeTwillDirectory($dir);
585
586
        $requestClassName = $modelName . 'Request';
587
588
        $stub = str_replace(
589
            ['{{requestClassName}}', '{{namespace}}', '{{baseRequest}}'],
590
            [$requestClassName, $this->namespace('requests', 'Http\Requests\Admin'), config('twill.base_request')],
591
            $this->files->get(__DIR__ . '/stubs/request.stub')
592
        );
593
594
        twill_put_stub(twill_path("{$dir}/" . $requestClassName . '.php'), $stub);
595
596
        $this->info("Form request created successfully! Add some validation rules!");
597
    }
598
599
    /**
600
     * Creates appropriate module Blade view files.
601
     *
602
     * @param string $moduleName
603
     * @return void
604
     * @throws \Illuminate\Contracts\Filesystem\FileNotFoundException
605
     */
606
    private function createViews($moduleName = 'items')
607
    {
608
        $viewsPath = $this->viewPath($moduleName);
609
610
        $this->makeTwillDirectory($viewsPath);
611
612
        $formView = $this->translatable ? 'form_translatable' : 'form';
613
614
        twill_put_stub($viewsPath . '/form.blade.php', $this->files->get(__DIR__ . '/stubs/' . $formView . '.blade.stub'));
615
616
        $this->info("Form view created successfully! Include your form fields using @formField directives!");
617
    }
618
619
    /**
620
     * Creates a basic routes file for the Capsule.
621
     *
622
     * @param string $moduleName
623
     * @return void
624
     * @throws \Illuminate\Contracts\Filesystem\FileNotFoundException
625
     */
626
    public function createCapsuleRoutes()
627
    {
628
        $this->makeDir($this->capsule['routes_file']);
629
630
        $contents = str_replace(
631
            '{{moduleName}}',
632
            $this->capsule['module'],
633
            $this->files->get(__DIR__ . '/stubs/routes_admin.stub')
634
        );
635
636
        twill_put_stub($this->capsule['routes_file'], $contents);
637
638
        $this->info("Routes file created successfully!");
639
    }
640
641
    /**
642
     * Creates a new capsule database seed file.
643
     *
644
     * @param string $moduleName
645
     * @return void
646
     * @throws \Illuminate\Contracts\Filesystem\FileNotFoundException
647
     */
648
    private function createCapsuleSeed($moduleName = 'items')
649
    {
650
        $this->makeTwillDirectory($this->capsule['seeds_psr4_path']);
651
652
        $stub = $this->files->get(__DIR__ . '/stubs/database_seeder_capsule.stub');
653
654
        $stub = str_replace('{moduleName}', $this->capsule['plural'], $stub);
655
656
        $this->files->put("{$this->capsule['seeds_psr4_path']}/DatabaseSeeder.php", $stub);
657
658
        $this->info("Seed created successfully!");
659
    }
660
661
    /**
662
     * Creates a new singleton module database seed file.
663
     *
664
     * @param string $modelName
665
     * @return void
666
     * @throws \Illuminate\Contracts\Filesystem\FileNotFoundException
667
     */
668
    private function createSingletonSeed($modelName = 'Item')
669
    {
670
        $repositoryName = $modelName . 'Repository';
671
        $seederName = $modelName . 'Seeder';
672
673
        $dir = $this->databasePath('seeders');
674
675
        $this->makeTwillDirectory($dir);
676
677
        $stub = $this->files->get(__DIR__ . '/stubs/database_seeder_singleton.stub');
678
679
        $stub = $this->replaceVariables([
680
            'seederClassName' => $seederName,
681
            'modelClassName' => $modelName,
682
            'repositoryClassName' => $repositoryName,
683
        ], $stub);
684
685
        $stub = $this->replaceConditionals([
686
            'hasTranslations' => $this->translatable,
687
            '!hasTranslations' => !$this->translatable,
688
        ], $stub);
689
690
        $stub = $this->removeEmptyLinesWithOnlySpaces($stub);
691
692
        $this->files->put("{$dir}/{$seederName}.php", $stub);
693
694
        $this->info("Seed created successfully!");
695
    }
696
697
    private function checkOption($option)
698
    {
699
        if (!$this->hasOption($option)) {
700
            return false;
701
        }
702
703
        if ($this->option($option) || $this->option('all')) {
704
            return true;
705
        }
706
707
        $questions = [
708
            'hasBlocks' => 'Do you need to use the block editor on this module?',
709
            'hasTranslation' => 'Do you need to translate content on this module?',
710
            'hasSlug' => 'Do you need to generate slugs on this module?',
711
            'hasMedias' => 'Do you need to attach images on this module?',
712
            'hasFiles' => 'Do you need to attach files on this module?',
713
            'hasPosition' => 'Do you need to manage the position of records on this module?',
714
            'hasRevisions' => 'Do you need to enable revisions on this module?',
715
            'hasNesting' => 'Do you need to enable nesting on this module?',
716
        ];
717
718
        $defaultAnswers = [
719
            'hasNesting' => 0,
720
        ];
721
722
        $currentDefaultAnswer = $this->defaultsAnswserToNo ? 0 : ($defaultAnswers[$option] ?? 1);
723
724
        return 'yes' === $this->choice($questions[$option], ['no', 'yes'], $currentDefaultAnswer);
725
    }
726
727
    public function createCapsulePath($moduleName, $modelName)
728
    {
729
        if (!$this->isCapsule) {
730
            $this->moduleBasePath = base_path();
731
732
            return;
733
        }
734
735
        $this->checkCapsuleDirectory(
736
            $this->moduleBasePath = config('twill.capsules.path') . "/{$moduleName}"
737
        );
738
739
        $this->makeDir($this->moduleBasePath);
740
    }
741
742
    public function createCapsuleNamespace($module, $model)
743
    {
744
        $base = config('twill.capsules.namespace');
745
746
        $this->capsuleNamespace = "{$base}\\{$module}";
0 ignored issues
show
Bug Best Practice introduced by
The property capsuleNamespace does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
747
    }
748
749
    public function databasePath($path = '')
750
    {
751
        if (!$this->isCapsule) {
752
            return database_path($path);
753
        }
754
755
        return "{$this->moduleBasePath}/database" . (filled($path) ? "/{$path}" : '');
756
    }
757
758
    public function makeDir($dir)
759
    {
760
        $info = pathinfo($dir);
761
762
        $dir = isset($info['extension']) ? $info['dirname'] : $dir;
763
764
        if (!is_dir($dir)) {
765
            mkdir($dir, 0755, true);
766
        }
767
768
        if (!is_dir($dir)) {
769
            $this->info("It wasn't possible to create capsule directory {$dir}");
770
771
            die;
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
772
        }
773
    }
774
775
    public function makeTwillDirectory($path)
776
    {
777
        make_twill_directory($path);
778
    }
779
780
    public function namespace ($type, $suffix, $class = null) {
781
        $class = (filled($class) ? "\\$class" : '');
782
783
        if (!$this->isCapsule) {
784
            return "App\\{$suffix}{$class}";
785
        }
786
787
        return $this->capsule[$type] . $class;
788
    }
789
790
    public function viewPath($moduleName)
791
    {
792
        if (!$this->isCapsule) {
793
            return $viewsPath = $this->config->get('view.paths')[0] . '/admin/' . $moduleName;
0 ignored issues
show
Unused Code introduced by
The assignment to $viewsPath is dead and can be removed.
Loading history...
794
        }
795
796
        $this->makeDir($dir = "{$this->moduleBasePath}/resources/views/admin");
797
798
        return $dir;
799
    }
800
801
    /**
802
     * @param array $variables
803
     * @param string $stub
804
     * @param array|null $delimiters
805
     * @return string
806
     */
807
    public function replaceVariables($variables, $stub, $delimiters = null)
808
    {
809
        $delimiters = $delimiters ?: ['{{', '}}'];
810
811
        foreach ($variables as $key => $value) {
812
            $key = "{$delimiters[0]}{$key}{$delimiters[1]}";
813
814
            $stub = str_replace($key, $value, $stub);
815
        }
816
817
        return $stub;
818
    }
819
820
    /**
821
     * @param array $variables
822
     * @param string $stub
823
     * @param array|null $delimiters
824
     * @return string
825
     */
826
    public function replaceConditionals($conditionals, $stub, $delimiters = null)
827
    {
828
        $delimiters = $delimiters ?: ['{{', '}}'];
829
830
        foreach ($conditionals as $key => $value) {
831
            $start = "{$delimiters[0]}{$key}{$delimiters[1]}";
832
            $end = "{$delimiters[0]}\/{$key}{$delimiters[1]}";
833
834
            if ((bool)$value) {
835
                // replace delimiters only
836
                $stub = preg_replace("/$start/", '', $stub);
837
                $stub = preg_replace("/$end/", '', $stub);
838
            } else {
839
                // replace delimiters and everything between
840
                $anything = '[\s\S]+?';
841
                $stub = preg_replace("/{$start}{$anything}{$end}/", '', $stub);
842
            }
843
        }
844
845
        return $stub;
846
    }
847
848
    /**
849
     * @param string $stub
850
     * @return string
851
     */
852
    public function removeEmptyLinesWithOnlySpaces($stub)
853
    {
854
        return preg_replace('/^ +\n/m', '', $stub);
855
    }
856
}
857