Passed
Pull Request — 3.x (#1377)
by Harings
10:21
created

ModuleMake::previewViewPath()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 9
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

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

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

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

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

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

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

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

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

256
            $this->/** @scrutinizer ignore-call */ 
257
                   createCapsuleRoutes($moduleName);

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

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

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