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

ModuleMake::handle()   F

Complexity

Conditions 19
Paths 3841

Size

Total Lines 137
Code Lines 98

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 65
CRAP Score 19.0704

Importance

Changes 3
Bugs 0 Features 0
Metric Value
cc 19
eloc 98
c 3
b 0
f 0
nc 3841
nop 0
dl 0
loc 137
rs 0.3236
ccs 65
cts 69
cp 0.942
crap 19.0704

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

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