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

ModuleMake::replaceConditionals()   A

Complexity

Conditions 4
Paths 3

Size

Total Lines 20
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 20

Importance

Changes 0
Metric Value
cc 4
eloc 11
nc 3
nop 3
dl 0
loc 20
ccs 0
cts 0
cp 0
crap 20
rs 9.9
c 0
b 0
f 0
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