Passed
Pull Request — 2.x (#1446)
by Harings
13:09
created

ModuleMake::createModels()   F

Complexity

Conditions 15
Paths 768

Size

Total Lines 134
Code Lines 86

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 17
CRAP Score 15.2627

Importance

Changes 0
Metric Value
cc 15
eloc 86
c 0
b 0
f 0
nc 768
nop 2
dl 0
loc 134
ccs 17
cts 19
cp 0.8947
crap 15.2627
rs 1.9632

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 A17\Twill\Facades\TwillCapsules;
6
use A17\Twill\Helpers\Capsule;
7
use Illuminate\Config\Repository as Config;
8
use Illuminate\Filesystem\Filesystem;
9
use Illuminate\Support\Collection;
10
use Illuminate\Support\Composer;
11
use Illuminate\Support\Facades\File;
12
use Illuminate\Support\Str;
13
14
class ModuleMake extends Command
15
{
16
    /**
17
     * The name and signature of the console command.
18
     *
19
     * @var string
20
     */
21
    protected $signature = 'twill:make:module {moduleName}
22
        {--B|hasBlocks}
23
        {--T|hasTranslation}
24
        {--S|hasSlug}
25
        {--M|hasMedias}
26
        {--F|hasFiles}
27
        {--P|hasPosition}
28
        {--R|hasRevisions}
29
        {--N|hasNesting}
30
        {--all}';
31
32
    /**
33
     * The console command description.
34
     *
35
     * @var string
36
     */
37
    protected $description = 'Create a new Twill Module';
38
39
    /**
40
     * @var Filesystem
41
     */
42
    protected $files;
43
44
    /**
45
     * @var Composer
46
     */
47
    protected $composer;
48
49
    /**
50
     * @var string[]
51
     */
52
    protected $modelTraits;
53
54
    /**
55
     * @var string[]
56
     */
57
    protected $repositoryTraits;
58
59
    /**
60
     * @var Config
61
     */
62
    protected $config;
63
64
    /**
65
     * @var bool
66
     */
67
    protected $blockable;
68
69
    /**
70
     * @var bool
71
     */
72
    protected $translatable;
73
74
    /**
75
     * @var bool
76
     */
77
    protected $sluggable;
78
79
    /**
80
     * @var bool
81
     */
82
    protected $mediable;
83
84
    /**
85
     * @var bool
86
     */
87
    protected $fileable;
88
89
    /**
90
     * @var bool
91
     */
92
    protected $sortable;
93
94
    /**
95
     * @var bool
96
     */
97
    protected $revisionable;
98
99
    /**
100
     * @var bool
101
     */
102
    protected $nestable;
103
104
    /**
105 69
     * @var bool
106
     */
107 69
    protected $defaultsAnswserToNo;
108
109 69
    /**
110 69
     * @var bool
111 69
     */
112
    protected $isCapsule = false;
113 69
114 69
    /**
115 69
     * @var bool
116 69
     */
117 69
    protected $isSingleton = false;
118 69
119 69
    /**
120
     * @var string
121 69
     */
122
    protected $moduleBasePath;
123 69
124 69
    /**
125 69
     * @var \A17\Twill\Helpers\Capsule
126
     */
127
    protected $capsule;
128
129
    /**
130
     * If true we save to flat directories.
131
     *
132 1
     * @var bool
133
     */
134 1
    protected $customDirs = false;
135
136 1
    /**
137 1
     * @param Filesystem $files
138
     * @param Composer $composer
139
     * @param Config $config
140
     */
141
    public function __construct(Filesystem $files, Composer $composer, Config $config)
142
    {
143
        parent::__construct();
144 1
145 1
        $this->files = $files;
146 1
        $this->composer = $composer;
147
        $this->config = $config;
148 1
149 1
        $this->blockable = false;
150
        $this->translatable = false;
151
        $this->sluggable = false;
152 1
        $this->mediable = false;
153 1
        $this->fileable = false;
154 1
        $this->sortable = false;
155 1
        $this->revisionable = false;
156 1
        $this->nestable = false;
157 1
158 1
        $this->defaultsAnswserToNo = false;
159
160
        $this->modelTraits = [
161 1
            'HasBlocks',
162 1
            'HasTranslation',
163 1
            'HasSlug',
164 1
            'HasMedias',
165 1
            'HasFiles',
166 1
            'HasRevisions',
167 1
            'HasPosition',
168
            'HasNesting',
169
        ];
170 1
        $this->repositoryTraits = [
171
            'HandleBlocks',
172 1
            'HandleTranslations',
173 1
            'HandleSlugs',
174 1
            'HandleMedias',
175 1
            'HandleFiles',
176 1
            'HandleRevisions',
177 1
            '',
178
            'HandleNesting',
179 1
        ];
180 1
    }
181
182 1
    protected function checkCapsuleDirectory($dir)
183 1
    {
184 1
        if (file_exists($dir)) {
185 1
            if (!$this->option('force')) {
186
                $answer = $this->choice(
187
                    "Capsule path exists ($dir). Erase and overwrite?",
188
                    ['no', 'yes'],
189
                    $this->defaultsAnswserToNo
190 1
                        ? 0
191
                        : 1
192 1
                );
193
            }
194 1
195 1
            if ('yes' === ($answer ?? 'no') || $this->option('force')) {
196
                File::deleteDirectory($dir);
197
198
                if (file_exists($dir)) {
199
                    $this->info("Directory could not be deleted. Aborted.");
200
                    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...
201
                }
202
            } else {
203
                $this->info("Aborted");
204 1
205
                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...
206 1
            }
207 1
        }
208
    }
209 1
210
    /**
211 1
     * Executes the console command.
212
     *
213 1
     * @return mixed
214 1
     */
215
    public function handle()
216 1
    {
217
        // e.g. newsItems
218 1
        $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

218
        $moduleName = Str::camel(Str::plural(lcfirst(/** @scrutinizer ignore-type */ $this->argument('moduleName'))));
Loading history...
219 1
220 1
        // e.g. newsItem
221 1
        $singularModuleName = Str::camel(lcfirst($this->argument('moduleName')));
222
223
        // e.g. NewsItems
224 1
        $moduleTitle = Str::studly($moduleName);
225 1
226
        // e.g. NewsItem
227
        $modelName = Str::studly(Str::singular($moduleName));
228
229
        if ($this->isCapsule && $this->option('packageDirectory') && $this->option('packageNamespace')) {
230
            $dir = base_path() . '/' . $this->option('packageDirectory') . '/src/Twill/Capsules/' . $moduleTitle;
231
            if (!$this->confirm('Creating capsule in ' . $dir, true)) {
232
                exit(1);
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...
233 1
            }
234 1
            $this->customDirs = true;
235 1
            $this->capsule = new Capsule(
236 1
                $moduleTitle,
237
                $this->option('packageNamespace') . '\\Twill\\Capsules',
238 1
                $dir
239
            );
240 1
        } elseif ($this->isCapsule) {
241
            $this->capsule = TwillCapsules::makeProjectCapsule($moduleTitle);
242 1
        }
243
244 1
        $enabledOptions = Collection::make($this->options())->only([
245
            'hasBlocks',
246
            'hasTranslation',
247
            'hasSlug',
248
            'hasMedias',
249
            'hasFiles',
250
            'hasPosition',
251
            'hasRevisions',
252
            'hasNesting',
253 1
        ])->filter(function ($enabled) {
254
            return $enabled;
255 1
        });
256
257 1
        if (count($enabledOptions) > 0) {
258
            $this->defaultsAnswserToNo = true;
259 1
        }
260 1
261
        $this->blockable = $this->checkOption('hasBlocks');
262 1
        $this->translatable = $this->checkOption('hasTranslation');
263
        $this->sluggable = $this->checkOption('hasSlug');
264 1
        $this->mediable = $this->checkOption('hasMedias');
265 1
        $this->fileable = $this->checkOption('hasFiles');
266 1
        $this->sortable = $this->checkOption('hasPosition');
267 1
        $this->revisionable = $this->checkOption('hasRevisions');
268
        $this->nestable = $this->checkOption('hasNesting');
269
270 1
        if ($this->nestable) {
271
            $this->sortable = true;
272
        }
273 1
274 1
        $activeTraits = [
275
            $this->blockable,
276 1
            $this->translatable,
277
            $this->sluggable,
278 1
            $this->mediable,
279
            $this->fileable,
280 1
            $this->revisionable,
281
            $this->sortable,
282
            $this->nestable,
283 1
        ];
284 1
285
        $this->createCapsuleNamespace($moduleTitle, $modelName);
286 1
        $this->createCapsulePath($moduleTitle, $modelName);
287
288 1
        $this->createMigration($moduleName);
289
        $this->createModels($modelName, $activeTraits);
290 1
        $this->createRepository($modelName, $activeTraits);
291
        $this->createController($moduleName, $modelName);
292
        $this->createRequest($modelName);
293 1
        $this->createViews($moduleName);
294
295 1
        if ($this->isCapsule) {
296 1
            if ($this->isSingleton) {
297 1
                $this->createCapsuleSingletonSeeder();
298
            }
299
            else {
300
                $this->createCapsuleSeed();
301 1
            }
302
            $this->createCapsuleRoutes();
303 1
        } elseif ($this->isSingleton) {
304
            $this->createSingletonSeed($modelName);
305 1
            $this->info("\nAdd to routes/admin.php:\n");
306
            $this->info("    Route::singleton('{$singularModuleName}');\n");
307 1
        } else {
308 1
            $this->info("\nAdd to routes/admin.php:\n");
309
            $this->info("    Route::module('{$moduleName}');\n");
310
        }
311 1
312 1
        $navModuleName = $this->isSingleton ? $singularModuleName : $moduleName;
313
        $navTitle = $this->isSingleton ? $modelName : $moduleTitle;
314
        $navType = $this->isSingleton ? 'singleton' : 'module';
315
316
        if (!$this->customDirs) {
317 1
            $this->info("Setup a new CMS menu item in config/twill-navigation.php:\n");
318 1
            $this->info("    '{$navModuleName}' => [");
319 1
            $this->info("        'title' => '{$navTitle}',");
320 1
            $this->info("        '{$navType}' => true,");
321 1
            $this->info("    ],\n");
322
323 1
            if ($this->isCapsule) {
324 1
                $this->info("Setup your new Capsule in config/twill.php:\n");
325 1
                $this->info("    'capsules' => [");
326 1
                $this->info("        'list' => [");
327
                $this->info("            [");
328 1
                $this->info("                'name' => '{$this->capsule->name}',");
329
                $this->info("                'enabled' => true,");
330 1
                $this->info("            ],");
331 1
                $this->info("        ],");
332
                $this->info("    ],\n");
333 1
            }
334
335 1
            if ($this->isSingleton) {
336 1
                $this->info("Migrate your database & seed your singleton module:\n");
337 1
                $this->info("    php artisan migrate\n");
338 1
                $this->info("    php artisan db:seed {$modelName}Seeder\n");
339 1
            } else {
340
                $this->info("Migrate your database.\n");
341
            }
342
        }
343
344 1
        $this->info("Enjoy.");
345
346
        if ($this->nestable && !class_exists('\Kalnoy\Nestedset\NestedSet')) {
347
            $this->warn("\nTo support module nesting, you must install the `kalnoy/nestedset` package:");
348
            $this->warn("\n    composer require kalnoy/nestedset\n");
349
        }
350
351
        $this->composer->dumpAutoloads();
352
    }
353
354
    /**
355 1
     * Creates a new module database migration file.
356
     *
357 1
     * @param string $moduleName
358
     * @return void
359 1
     * @throws \Illuminate\Contracts\Filesystem\FileNotFoundException
360
     */
361 1
    private function createMigration($moduleName = 'items')
362
    {
363 1
        $table = Str::snake($moduleName);
364 1
        $tableClassName = Str::studly($table);
365 1
366
        $migrationName = 'create_' . $table . '_tables';
367
368
        if (!count(glob($this->databasePath('migrations/*' . $migrationName . '.php')))) {
369 1
            $migrationPath = $this->databasePath() . '/migrations';
370
371 1
            $this->makeDir($migrationPath);
372
373 1
            $fullPath = $this->laravel['migration.creator']->create($migrationName, $migrationPath);
374
375 1
            $stub = str_replace(
376
                ['{{table}}', '{{singularTableName}}', '{{tableClassName}}'],
377 1
                [$table, Str::singular($table), $tableClassName],
378 1
                $this->files->get(__DIR__ . '/stubs/migration.stub')
379
            );
380
381
            if ($this->translatable) {
382
                $stub = preg_replace('/{{!hasTranslation}}[\s\S]+?{{\/!hasTranslation}}/', '', $stub);
383
            } else {
384
                $stub = str_replace([
385
                    '{{!hasTranslation}}',
386
                    '{{/!hasTranslation}}',
387
                ], '', $stub);
388 1
            }
389
390 1
            $stub = $this->renderStubForOption($stub, 'hasTranslation', $this->translatable);
391
            $stub = $this->renderStubForOption($stub, 'hasSlug', $this->sluggable);
392 1
            $stub = $this->renderStubForOption($stub, 'hasRevisions', $this->revisionable);
393
            $stub = $this->renderStubForOption($stub, 'hasPosition', $this->sortable);
394 1
            $stub = $this->renderStubForOption($stub, 'hasNesting', $this->nestable);
395 1
396 1
            $stub = preg_replace('/\}\);[\s\S]+?Schema::create/', "});\n\n        Schema::create", $stub);
397 1
398
            $this->files->put($fullPath, $stub);
399
400 1
            $this->info("Migration created successfully! Add some fields!");
401
        }
402 1
    }
403 1
404
    /**
405
     * Creates new model class files for the given model name and traits.
406
     *
407
     * @param string $modelName
408
     * @param array $activeTraits
409
     * @throws \Illuminate\Contracts\Filesystem\FileNotFoundException
410
     */
411
    private function createModels($modelName = 'Item', $activeTraits = [])
412 1
    {
413
        $modelClassName = $this->namespace('models', 'Models', $modelName);
414 1
415
        $modelsDir = $this->isCapsule ? $this->capsule->getModelsDir() : 'Models';
416 1
417
        $this->makeTwillDirectory($modelsDir);
418 1
419
        if ($this->translatable) {
420 1
            $this->makeTwillDirectory($baseDir = $this->isCapsule ? $this->capsule->getModelsDir() : "$modelsDir/Translations");
421
422 1
            $modelTranslationClassName = $modelName . 'Translation';
423 1
424
            $stub = str_replace(
425
                [
426
                    '{{modelTranslationClassName}}',
427
                    '{{modelClassWithNamespace}}',
428
                    '{{modelClassName}}',
429
                    '{{namespace}}',
430
                    '{{baseTranslationModel}}',
431
                ],
432 1
                [
433
                    $modelTranslationClassName,
434 1
                    $modelClassName,
435
                    $modelName,
436 1
                    $this->namespace('models', 'Models\Translations'),
437
                    config('twill.base_translation_model'),
438 1
                ],
439
                $this->files->get(__DIR__ . '/stubs/model_translation.stub')
440 1
            );
441
442 1
            $this->putTwillStub(twill_path("$baseDir/" . $modelTranslationClassName . '.php'), $stub);
443 1
        }
444
445 1
        if ($this->sluggable) {
446
            $this->makeTwillDirectory($baseDir = $this->isCapsule ? $this->capsule->getModelsDir() : "$modelsDir/Slugs");
447 1
448 1
            $modelSlugClassName = $modelName . 'Slug';
449
450
            $stub = str_replace(
451
                [
452
                    '{{modelSlugClassName}}',
453
                    '{{modelClassWithNamespace}}',
454
                    '{{modelName}}',
455
                    '{{namespace}}',
456
                    '{{baseSlugModel}}',
457
                ],
458
                [
459
                    $modelSlugClassName,
460
                    $modelClassName,
461
                    Str::snake($modelName),
462
                    $this->namespace('models', 'Models\Slugs'),
463
                    config('twill.base_slug_model'),
464
                ],
465
                $this->files->get(__DIR__ . '/stubs/model_slug.stub')
466
            );
467
468
            $this->putTwillStub(twill_path("$baseDir/" . $modelSlugClassName . '.php'), $stub);
469
        }
470
471
        if ($this->revisionable) {
472
            $this->makeTwillDirectory($baseDir = $this->isCapsule ? $this->capsule->getModelsDir() : "$modelsDir/Revisions");
473
474
            $modelRevisionClassName = $modelName . 'Revision';
475
476
            $stub = str_replace(
477
                [
478
                    '{{modelRevisionClassName}}',
479
                    '{{modelClassWithNamespace}}',
480
                    '{{modelName}}',
481
                    '{{namespace}}',
482
                    '{{baseRevisionModel}}',
483
                ],
484
                [
485
                    $modelRevisionClassName,
486
                    $modelClassName,
487
                    Str::snake($modelName),
488
                    $this->namespace('models', 'Models\Revisions'),
489
                    config('twill.base_revision_model'),
490
                ],
491
                $this->files->get(__DIR__ . '/stubs/model_revision.stub')
492
            );
493
494
            $this->putTwillStub(twill_path("$baseDir/" . $modelRevisionClassName . '.php'), $stub);
495
        }
496
497
        $activeModelTraits = [];
498
499
        foreach ($activeTraits as $index => $traitIsActive) {
500
            if ($traitIsActive) {
501
                !isset($this->modelTraits[$index]) ?: $activeModelTraits[] = $this->modelTraits[$index];
502
            }
503
        }
504
505
        $activeModelTraitsString = empty($activeModelTraits) ? '' : 'use ' . rtrim(
506
                implode(', ', $activeModelTraits),
507
                ', '
508
            ) . ';';
509
510
        $activeModelTraitsImports = empty($activeModelTraits) ? '' : "use A17\Twill\Models\Behaviors\\" . implode(
511
                ";\nuse A17\Twill\Models\Behaviors\\",
512
                $activeModelTraits
513
            ) . ";";
514
515
        $activeModelImplements = $this->sortable ? 'implements Sortable' : '';
516
517
        if ($this->sortable) {
518
            $activeModelTraitsImports .= "\nuse A17\Twill\Models\Behaviors\Sortable;";
519
        }
520
521
        $stub = str_replace([
522
            '{{modelClassName}}',
523
            '{{modelTraits}}',
524
            '{{modelImports}}',
525
            '{{modelImplements}}',
526
            '{{namespace}}',
527
            '{{baseModel}}',
528
        ], [
529
            $modelName,
530
            $activeModelTraitsString,
531
            $activeModelTraitsImports,
532
            $activeModelImplements,
533
            $this->namespace('models', 'Models'),
534
            config('twill.base_model'),
535
        ], $this->files->get(__DIR__ . '/stubs/model.stub'));
536
537
        $stub = $this->renderStubForOption($stub, 'hasTranslation', $this->translatable);
538
        $stub = $this->renderStubForOption($stub, 'hasSlug', $this->sluggable);
539
        $stub = $this->renderStubForOption($stub, 'hasMedias', $this->mediable);
540
        $stub = $this->renderStubForOption($stub, 'hasPosition', $this->sortable);
541
542
        $this->putTwillStub(twill_path("$modelsDir/" . $modelName . '.php'), $stub);
543
544
        $this->info("Models created successfully! Fill your fillables!");
545
    }
546
547
    private function renderStubForOption($stub, $option, $enabled)
548
    {
549
        if ($enabled) {
550
            $stub = str_replace([
551
                '{{' . $option . '}}',
552
                '{{/' . $option . '}}',
553
            ], '', $stub);
554
        } else {
555
            $stub = preg_replace('/{{' . $option . '}}[\s\S]+?{{\/' . $option . '}}/', '', $stub);
556
        }
557
558
        return $stub;
559
    }
560
561
    /**
562
     * Creates new repository class file for the given model name.
563
     *
564
     * @param string $modelName
565
     * @param array $activeTraits
566
     * @return void
567
     * @throws \Illuminate\Contracts\Filesystem\FileNotFoundException
568
     */
569
    private function createRepository($modelName = 'Item', $activeTraits = [])
570
    {
571
        $modelsDir = $this->isCapsule ? $this->capsule->getRepositoriesDir() : 'Repositories';
572
573
        $modelClass = $this->isCapsule ? $this->capsule->getModel() : config(
574
                'twill.namespace'
575
            ) . "\Models\\{$modelName}";
576
577
        $this->makeTwillDirectory($modelsDir);
578
579
        $repositoryClassName = $modelName . 'Repository';
580
581
        $activeRepositoryTraits = [];
582
583
        foreach ($activeTraits as $index => $traitIsActive) {
584
            if ($traitIsActive) {
585
                !isset($this->repositoryTraits[$index]) ?: $activeRepositoryTraits[] = $this->repositoryTraits[$index];
586
            }
587
        }
588
589
        $activeRepositoryTraits = array_filter($activeRepositoryTraits);
590
591
        $activeRepositoryTraitsString = empty($activeRepositoryTraits) ? '' : 'use ' . (empty($activeRepositoryTraits) ? "" : rtrim(
592
                    implode(', ', $activeRepositoryTraits),
593
                    ', '
594
                ) . ';');
595
596
        $activeRepositoryTraitsImports = empty($activeRepositoryTraits) ? '' : "use A17\Twill\Repositories\Behaviors\\" . implode(
597
                ";\nuse A17\Twill\Repositories\Behaviors\\",
598
                $activeRepositoryTraits
599
            ) . ";";
600
601
        $stub = str_replace(
602
            [
603
                '{{repositoryClassName}}',
604
                '{{modelName}}',
605
                '{{repositoryTraits}}',
606
                '{{repositoryImports}}',
607
                '{{namespace}}',
608
                '{{modelClass}}',
609
                '{{baseRepository}}',
610
            ],
611
            [
612
                $repositoryClassName,
613
                $modelName,
614
                $activeRepositoryTraitsString,
615
                $activeRepositoryTraitsImports,
616
                $this->namespace('repositories', 'Repositories'),
617
                $modelClass,
618
                config('twill.base_repository'),
619
            ],
620
            $this->files->get(__DIR__ . '/stubs/repository.stub')
621
        );
622
623
        $this->putTwillStub(twill_path("{$modelsDir}/" . $repositoryClassName . '.php'), $stub);
624
625
        $this->info("Repository created successfully! Control all the things!");
626
    }
627
628
    /**
629
     * Create a new controller class file for the given module name and model name.
630
     *
631
     * @param string $moduleName
632
     * @param string $modelName
633
     * @return void
634
     * @throws \Illuminate\Contracts\Filesystem\FileNotFoundException
635
     */
636
    private function createController($moduleName = 'items', $modelName = 'Item')
637
    {
638
        $controllerClassName = $modelName . 'Controller';
639
640
        $dir = $this->isCapsule ? $this->capsule->getControllersDir() : 'Http/Controllers/Admin';
641
642
        if ($this->isSingleton) {
643
            $baseController = config('twill.base_singleton_controller');
644
        } elseif ($this->nestable) {
645
            $baseController = config('twill.base_nested_controller');
646
        } else {
647
            $baseController = config('twill.base_controller');
648
        }
649
650
        $this->makeTwillDirectory($dir);
651
652
        $stub = str_replace(
653
            ['{{moduleName}}', '{{controllerClassName}}', '{{namespace}}', '{{baseController}}'],
654
            [
655
                $moduleName,
656
                $controllerClassName,
657
                $this->namespace('controllers', 'Http\Controllers\Admin'),
658
                $baseController,
659
            ],
660
            $this->files->get(__DIR__ . '/stubs/controller.stub')
661
        );
662
663
        $permalinkOption = '';
664
        $reorderOption = '';
665
666
        if (!$this->sluggable) {
667
            $permalinkOption = "'permalink' => false,";
668
        }
669
670
        if ($this->nestable) {
671
            $reorderOption = "'reorder' => true,";
672
673
            $stub = str_replace(['{{hasNesting}}', '{{/hasNesting}}'], '', $stub);
674
        } else {
675
            $stub = preg_replace('/{{hasNesting}}[\s\S]+?{{\/hasNesting}}/', '', $stub);
676
        }
677
678
        $stub = str_replace(
679
            ['{{permalinkOption}}', '{{reorderOption}}'],
680
            [$permalinkOption, $reorderOption],
681
            $stub
682
        );
683
684
        // Remove lines including only whitespace, leave true empty lines untouched
685
        $stub = preg_replace('/^[\s]+\n/m', '', $stub);
686
687
        $this->putTwillStub(twill_path("$dir/" . $controllerClassName . '.php'), $stub);
688
689
        $this->info("Controller created successfully! Define your index/browser/form endpoints options!");
690
    }
691
692
    /**
693
     * Creates a new request class file for the given model name.
694
     *
695
     * @param string $modelName
696
     * @return void
697
     * @throws \Illuminate\Contracts\Filesystem\FileNotFoundException
698
     */
699
    private function createRequest($modelName = 'Item')
700
    {
701
        $dir = $this->isCapsule ? $this->capsule->getRequestsDir() : 'Http/Requests/Admin';
702
703
        $this->makeTwillDirectory($dir);
704
705
        $requestClassName = $modelName . 'Request';
706
707
        $stub = str_replace(
708
            ['{{requestClassName}}', '{{namespace}}', '{{baseRequest}}'],
709
            [$requestClassName, $this->namespace('requests', 'Http\Requests\Admin'), config('twill.base_request')],
710
            $this->files->get(__DIR__ . '/stubs/request.stub')
711
        );
712
713
        $this->putTwillStub(twill_path("{$dir}/" . $requestClassName . '.php'), $stub);
714
715
        $this->info("Form request created successfully! Add some validation rules!");
716
    }
717
718
    /**
719
     * Creates appropriate module Blade view files.
720
     *
721
     * @param string $moduleName
722
     * @return void
723
     * @throws \Illuminate\Contracts\Filesystem\FileNotFoundException
724
     */
725
    private function createViews($moduleName = 'items')
726
    {
727
        $viewsPath = $this->viewPath($moduleName);
728
729
        $this->makeTwillDirectory($viewsPath);
730
731
        $formView = $this->translatable ? 'form_translatable' : 'form';
732
733
        $this->putTwillStub(
734
            $viewsPath . '/form.blade.php',
735
            $this->files->get(__DIR__ . '/stubs/' . $formView . '.blade.stub')
736
        );
737
738
        $this->info("Form view created successfully! Include your form fields using @formField directives!");
739
    }
740
741
    /**
742
     * Creates a basic routes file for the Capsule.
743
     *
744
     * @param string $moduleName
745
     * @return void
746
     * @throws \Illuminate\Contracts\Filesystem\FileNotFoundException
747
     */
748
    public function createCapsuleRoutes(): void
749
    {
750
        $this->makeDir($this->capsule->getRoutesFile());
751
752
        $stubFile = $this->isSingleton ? 'routes_singleton_admin.stub' : 'routes_admin.stub';
753
754
        $contents = str_replace(
755
            '{{moduleName}}',
756
            $this->capsule->getModule(),
757
            $this->files->get(__DIR__ . '/stubs/' . $stubFile)
758
        );
759
760
        $this->putTwillStub($this->capsule->getRoutesFile(), $contents);
761
762
        $this->info("Routes file created successfully!");
763
    }
764
765
    /**
766
     * Creates a new capsule database seed file.
767
     *
768
     * @param string $moduleName
769
     * @return void
770
     * @throws \Illuminate\Contracts\Filesystem\FileNotFoundException
771
     */
772
    private function createCapsuleSeed(): void
773
    {
774
        $this->makeDir($this->capsule->getSeedsPsr4Path());
775
776
        $stub = $this->files->get(__DIR__ . '/stubs/database_seeder_capsule.stub');
777
778
        $stub = str_replace('{namespace}', $this->capsule->getSeedsNamespace(), $stub);
779
780
        $this->files->put("{$this->capsule->getSeedsPsr4Path()}/DatabaseSeeder.php", $stub);
781
782
        $this->info("Seed created successfully!");
783
    }
784
785
    /**
786
     * Creates a new singleton module database seed file.
787
     *
788
     * @param string $modelName
789
     * @return void
790
     * @throws \Illuminate\Contracts\Filesystem\FileNotFoundException
791
     */
792
    private function createSingletonSeed($modelName = 'Item')
793
    {
794
        $repositoryName = $modelName . 'Repository';
795
        $seederName = $modelName . 'Seeder';
796
797
        $dir = $this->databasePath('seeders');
798
799
        $this->makeTwillDirectory($dir);
800
801
        $stub = $this->files->get(__DIR__ . '/stubs/database_seeder_singleton.stub');
802
803
        $stub = $this->replaceVariables([
804
            'seederNamespace' => 'Database\\Seeders',
805
            'seederClassName' => $seederName,
806
            'modelClass' => "App\\Models\\$modelName",
807
            'modelClassName' => $modelName,
808
            'repositoryClass' => "App\\Repositories\\$repositoryName",
809
            'repositoryClassName' => $repositoryName,
810
        ], $stub);
811
812
        $stub = $this->replaceConditionals([
813
            'hasTranslations' => $this->translatable,
814
            '!hasTranslations' => !$this->translatable,
815
        ], $stub);
816
817
        $stub = $this->removeEmptyLinesWithOnlySpaces($stub);
818
819
        $this->files->put("{$dir}/{$seederName}.php", $stub);
820
821
        $this->info("Seed created successfully!");
822
    }
823
824
    private function createCapsuleSingletonSeeder(): void {
825
        $modelName = $this->capsule->getSingular();
826
        $repositoryName = $this->capsule->getSingular() . 'Repository';
827
        $seederName = $this->capsule->getSingular() . 'Seeder';
828
829
        $dir = $this->databasePath('seeders');
830
831
        $this->makeTwillDirectory($dir);
832
833
        $stub = $this->files->get(__DIR__ . '/stubs/database_seeder_singleton.stub');
834
835
        $stub = $this->replaceVariables([
836
            'seederNamespace' => $this->capsule->getDatabaseNamespace() . '\\Seeders',
837
            'seederClassName' => $seederName,
838
            'modelClass' => $this->capsule->getModelNamespace() . "\\$modelName",
839
            'modelClassName' => $modelName,
840
            'repositoryClass' => $this->capsule->getRepositoryClass(),
841
            'repositoryClassName' => $repositoryName,
842
        ], $stub);
843
844
        $stub = $this->replaceConditionals([
845
            'hasTranslations' => $this->translatable,
846
            '!hasTranslations' => !$this->translatable,
847
        ], $stub);
848
849
        $stub = $this->removeEmptyLinesWithOnlySpaces($stub);
850
851
        $this->files->put("{$dir}/{$seederName}.php", $stub);
852
853
        $this->info("Seed created successfully!");
854
    }
855
856
    private function checkOption($option)
857
    {
858
        if (!$this->hasOption($option)) {
859
            return false;
860
        }
861
862
        if ($this->option($option) || $this->option('all')) {
863
            return true;
864
        }
865
866
        $questions = [
867
            'hasBlocks' => 'Do you need to use the block editor on this module?',
868
            'hasTranslation' => 'Do you need to translate content on this module?',
869
            'hasSlug' => 'Do you need to generate slugs on this module?',
870
            'hasMedias' => 'Do you need to attach images on this module?',
871
            'hasFiles' => 'Do you need to attach files on this module?',
872
            'hasPosition' => 'Do you need to manage the position of records on this module?',
873
            'hasRevisions' => 'Do you need to enable revisions on this module?',
874
            'hasNesting' => 'Do you need to enable nesting on this module?',
875
        ];
876
877
        $defaultAnswers = [
878
            'hasNesting' => 0,
879
        ];
880
881
        $currentDefaultAnswer = $this->defaultsAnswserToNo ? 0 : ($defaultAnswers[$option] ?? 1);
882
883
        return 'yes' === $this->choice($questions[$option], ['no', 'yes'], $currentDefaultAnswer);
884
    }
885
886
    public function createCapsulePath($moduleName, $modelName)
887
    {
888
        if (!$this->isCapsule) {
889
            $this->moduleBasePath = base_path();
890
891
            return;
892
        }
893
894
        $this->checkCapsuleDirectory(
895
            $this->moduleBasePath = $this->capsule->getPsr4Path()
896
        );
897
898
        $this->makeDir($this->moduleBasePath);
899
    }
900
901
    public function createCapsuleNamespace($module, $model)
902
    {
903
        $base = config('twill.capsules.namespace');
904
905
        $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...
906
    }
907
908
    public function databasePath($path = '')
909
    {
910
        if (!$this->isCapsule) {
911
            return database_path($path);
912
        }
913
914
        return $this->capsule->getDatabasePsr4Path() . ($path ? '/' . $path : '');
915
    }
916
917
    public function makeDir($dir)
918
    {
919
        $info = pathinfo($dir);
920
921
        $dir = isset($info['extension']) ? $info['dirname'] : $dir;
922
923
        if (!is_dir($dir)) {
924
            mkdir($dir, 0755, true);
925
        }
926
927
        if (!is_dir($dir)) {
928
            $this->info("It wasn't possible to create capsule directory $dir");
929
930
            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...
931
        }
932
    }
933
934
    public function makeTwillDirectory($path)
935
    {
936
        if ($this->customDirs) {
937
            $this->makeDir($path);
938
        }
939
        else {
940
            make_twill_directory($path);
941
        }
942
    }
943
944
    public function putTwillStub(string $path, string $stub): void
945
    {
946
        if ($this->customDirs) {
947
            $stub = str_replace(
948
                'namespace App\\',
949
                sprintf('namespace %s\\', config('twill.namespace')),
950
                $stub
951
            );
952
953
            file_put_contents($path, $stub);
954
        }
955
        else {
956
            twill_put_stub($path, $stub);
957
        }
958
    }
959
960
    public function namespace($type, $suffix, $class = null)
961
    {
962
        $class = (filled($class) ? "\\$class" : '');
963
964
        if (!$this->isCapsule) {
965
            return "App\\{$suffix}{$class}";
966
        }
967
968
        if ($type === 'models') {
969
            return $this->capsule->getModelNamespace() . $class;
970
        }
971
972
        if ($type === 'repositories') {
973
            return $this->capsule->getRepositoriesNamespace() . $class;
974
        }
975
976
        if ($type === 'controllers') {
977
            return $this->capsule->getControllersNamespace() . $class;
978
        }
979
980
        if ($type === 'requests') {
981
            return $this->capsule->getRequestsNamespace() . $class;
982
        }
983
984
        throw new \Exception('Missing Implementation.');
985
    }
986
987
    public function viewPath($moduleName)
988
    {
989
        if (!$this->isCapsule) {
990
            return $this->config->get('view.paths')[0] . '/admin/' . $moduleName;
991
        }
992
993
        $dir = "$this->moduleBasePath/resources/views/admin";
994
        $this->makeDir($dir);
995
996
        return $dir;
997
    }
998
999
    /**
1000
     * @param array $variables
1001
     * @param string $stub
1002
     * @param array|null $delimiters
1003
     * @return string
1004
     */
1005
    public function replaceVariables($variables, $stub, $delimiters = null)
1006
    {
1007
        $delimiters = $delimiters ?: ['{{', '}}'];
1008
1009
        foreach ($variables as $key => $value) {
1010
            $key = "{$delimiters[0]}{$key}{$delimiters[1]}";
1011
1012
            $stub = str_replace($key, $value, $stub);
1013
        }
1014
1015
        return $stub;
1016
    }
1017
1018
    /**
1019
     * @param array $variables
1020
     * @param string $stub
1021
     * @param array|null $delimiters
1022
     * @return string
1023
     */
1024
    public function replaceConditionals($conditionals, $stub, $delimiters = null)
1025
    {
1026
        $delimiters = $delimiters ?: ['{{', '}}'];
1027
1028
        foreach ($conditionals as $key => $value) {
1029
            $start = "{$delimiters[0]}{$key}{$delimiters[1]}";
1030
            $end = "{$delimiters[0]}\/{$key}{$delimiters[1]}";
1031
1032
            if ((bool)$value) {
1033
                // replace delimiters only
1034
                $stub = preg_replace("/$start/", '', $stub);
1035
                $stub = preg_replace("/$end/", '', $stub);
1036
            } else {
1037
                // replace delimiters and everything between
1038
                $anything = '[\s\S]+?';
1039
                $stub = preg_replace("/{$start}{$anything}{$end}/", '', $stub);
1040
            }
1041
        }
1042
1043
        return $stub;
1044
    }
1045
1046
    /**
1047
     * @param string $stub
1048
     * @return string
1049
     */
1050
    public function removeEmptyLinesWithOnlySpaces($stub)
1051
    {
1052
        return preg_replace('/^ +\n/m', '', $stub);
1053
    }
1054
}
1055