1 | <?php |
||||||
2 | |||||||
3 | namespace A17\Twill\Commands; |
||||||
4 | |||||||
5 | use Illuminate\Config\Repository as Config; |
||||||
6 | use Illuminate\Filesystem\Filesystem; |
||||||
7 | use Illuminate\Support\Collection; |
||||||
8 | use Illuminate\Support\Composer; |
||||||
9 | use Illuminate\Support\Facades\File; |
||||||
10 | use Illuminate\Support\Str; |
||||||
11 | |||||||
12 | class ModuleMake extends Command |
||||||
13 | { |
||||||
14 | /** |
||||||
15 | * The name and signature of the console command. |
||||||
16 | * |
||||||
17 | * @var string |
||||||
18 | */ |
||||||
19 | protected $signature = 'twill:make:module {moduleName} |
||||||
20 | {--B|hasBlocks} |
||||||
21 | {--T|hasTranslation} |
||||||
22 | {--S|hasSlug} |
||||||
23 | {--M|hasMedias} |
||||||
24 | {--F|hasFiles} |
||||||
25 | {--P|hasPosition} |
||||||
26 | {--R|hasRevisions} |
||||||
27 | {--N|hasNesting} |
||||||
28 | {--all}'; |
||||||
29 | |||||||
30 | /** |
||||||
31 | * The console command description. |
||||||
32 | * |
||||||
33 | * @var string |
||||||
34 | */ |
||||||
35 | protected $description = 'Create a new Twill Module'; |
||||||
36 | |||||||
37 | /** |
||||||
38 | * @var Filesystem |
||||||
39 | */ |
||||||
40 | protected $files; |
||||||
41 | |||||||
42 | /** |
||||||
43 | * @var Composer |
||||||
44 | */ |
||||||
45 | protected $composer; |
||||||
46 | |||||||
47 | /** |
||||||
48 | * @var string[] |
||||||
49 | */ |
||||||
50 | protected $modelTraits; |
||||||
51 | |||||||
52 | /** |
||||||
53 | * @var string[] |
||||||
54 | */ |
||||||
55 | protected $repositoryTraits; |
||||||
56 | |||||||
57 | /** |
||||||
58 | * @var Config |
||||||
59 | */ |
||||||
60 | protected $config; |
||||||
61 | |||||||
62 | /** |
||||||
63 | * @var bool |
||||||
64 | */ |
||||||
65 | protected $blockable; |
||||||
66 | |||||||
67 | /** |
||||||
68 | * @var bool |
||||||
69 | */ |
||||||
70 | protected $translatable; |
||||||
71 | |||||||
72 | /** |
||||||
73 | * @var bool |
||||||
74 | */ |
||||||
75 | protected $sluggable; |
||||||
76 | |||||||
77 | /** |
||||||
78 | * @var bool |
||||||
79 | */ |
||||||
80 | protected $mediable; |
||||||
81 | |||||||
82 | /** |
||||||
83 | * @var bool |
||||||
84 | */ |
||||||
85 | protected $fileable; |
||||||
86 | |||||||
87 | /** |
||||||
88 | * @var bool |
||||||
89 | */ |
||||||
90 | protected $sortable; |
||||||
91 | |||||||
92 | /** |
||||||
93 | * @var bool |
||||||
94 | */ |
||||||
95 | protected $revisionable; |
||||||
96 | |||||||
97 | /** |
||||||
98 | * @var bool |
||||||
99 | */ |
||||||
100 | protected $nestable; |
||||||
101 | |||||||
102 | /** |
||||||
103 | * @var bool |
||||||
104 | */ |
||||||
105 | 69 | protected $defaultsAnswserToNo; |
|||||
106 | |||||||
107 | 69 | /** |
|||||
108 | * @var bool |
||||||
109 | 69 | */ |
|||||
110 | 69 | protected $isCapsule = false; |
|||||
111 | 69 | ||||||
112 | /** |
||||||
113 | 69 | * @var bool |
|||||
114 | 69 | */ |
|||||
115 | 69 | protected $isSingleton = false; |
|||||
116 | 69 | ||||||
117 | 69 | /** |
|||||
118 | 69 | * @var string |
|||||
119 | 69 | */ |
|||||
120 | protected $moduleBasePath; |
||||||
121 | 69 | ||||||
122 | /** |
||||||
123 | 69 | * @var string |
|||||
124 | 69 | */ |
|||||
125 | 69 | protected $capsule; |
|||||
126 | |||||||
127 | /** |
||||||
128 | * @param Filesystem $files |
||||||
129 | * @param Composer $composer |
||||||
130 | * @param Config $config |
||||||
131 | */ |
||||||
132 | 1 | public function __construct(Filesystem $files, Composer $composer, Config $config) |
|||||
133 | { |
||||||
134 | 1 | parent::__construct(); |
|||||
135 | |||||||
136 | 1 | $this->files = $files; |
|||||
137 | 1 | $this->composer = $composer; |
|||||
138 | $this->config = $config; |
||||||
139 | |||||||
140 | $this->blockable = false; |
||||||
141 | $this->translatable = false; |
||||||
142 | $this->sluggable = false; |
||||||
143 | $this->mediable = false; |
||||||
144 | 1 | $this->fileable = false; |
|||||
145 | 1 | $this->sortable = false; |
|||||
146 | 1 | $this->revisionable = false; |
|||||
147 | $this->nestable = false; |
||||||
148 | 1 | ||||||
149 | 1 | $this->defaultsAnswserToNo = false; |
|||||
150 | |||||||
151 | $this->modelTraits = ['HasBlocks', 'HasTranslation', 'HasSlug', 'HasMedias', 'HasFiles', 'HasRevisions', 'HasPosition', 'HasNesting']; |
||||||
152 | 1 | $this->repositoryTraits = ['HandleBlocks', 'HandleTranslations', 'HandleSlugs', 'HandleMedias', 'HandleFiles', 'HandleRevisions', '', 'HandleNesting']; |
|||||
153 | 1 | } |
|||||
154 | 1 | ||||||
155 | 1 | protected function checkCapsuleDirectory($dir) |
|||||
156 | 1 | { |
|||||
157 | 1 | if (file_exists($dir)) { |
|||||
158 | 1 | if (!$this->option('force')) { |
|||||
159 | $answer = $this->choice("Capsule path exists ({$dir}). Erase and overwrite?", |
||||||
160 | ['no', 'yes'], $this->defaultsAnswserToNo |
||||||
161 | 1 | ? 0 |
|||||
162 | 1 | : 1); |
|||||
163 | 1 | } |
|||||
164 | 1 | ||||||
165 | 1 | if ('yes' === ($answer ?? 'no') || $this->option('force')) { |
|||||
166 | 1 | File::deleteDirectory($dir); |
|||||
167 | 1 | ||||||
168 | if (file_exists($dir)) { |
||||||
169 | $this->info("Directory could not be deleted. Aborted."); |
||||||
170 | 1 | die; |
|||||
0 ignored issues
–
show
|
|||||||
171 | } |
||||||
172 | 1 | } else { |
|||||
173 | 1 | $this->info("Aborted"); |
|||||
174 | 1 | ||||||
175 | 1 | die; |
|||||
0 ignored issues
–
show
|
|||||||
176 | 1 | } |
|||||
177 | 1 | } |
|||||
178 | } |
||||||
179 | 1 | ||||||
180 | 1 | /** |
|||||
181 | * Executes the console command. |
||||||
182 | 1 | * |
|||||
183 | 1 | * @return mixed |
|||||
184 | 1 | */ |
|||||
185 | 1 | public function handle() |
|||||
186 | { |
||||||
187 | // e.g. newsItems |
||||||
188 | $moduleName = Str::camel(Str::plural(lcfirst($this->argument('moduleName')))); |
||||||
0 ignored issues
–
show
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
Loading history...
|
|||||||
189 | |||||||
190 | 1 | // e.g. newsItem |
|||||
191 | $singularModuleName = Str::camel(lcfirst($this->argument('moduleName'))); |
||||||
192 | 1 | ||||||
193 | // e.g. NewsItems |
||||||
194 | 1 | $moduleTitle = Str::studly($moduleName); |
|||||
195 | 1 | ||||||
196 | // e.g. NewsItem |
||||||
197 | $modelName = Str::studly(Str::singular($moduleName)); |
||||||
198 | |||||||
199 | $this->capsule = app('twill.capsules.manager')->makeCapsule(['name' => $moduleName], config("twill.capsules.path")); |
||||||
0 ignored issues
–
show
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
This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces. This is most likely a typographical error or the method has been renamed.
Loading history...
|
|||||||
200 | |||||||
201 | $enabledOptions = Collection::make($this->options())->only([ |
||||||
202 | 'hasBlocks', |
||||||
203 | 'hasTranslation', |
||||||
204 | 1 | 'hasSlug', |
|||||
205 | 'hasMedias', |
||||||
206 | 1 | 'hasFiles', |
|||||
207 | 1 | 'hasPosition', |
|||||
208 | 'hasRevisions', |
||||||
209 | 1 | 'hasNesting', |
|||||
210 | ])->filter(function ($enabled) { |
||||||
211 | 1 | return $enabled; |
|||||
212 | }); |
||||||
213 | 1 | ||||||
214 | 1 | if (count($enabledOptions) > 0) { |
|||||
215 | $this->defaultsAnswserToNo = true; |
||||||
216 | 1 | } |
|||||
217 | |||||||
218 | 1 | $this->blockable = $this->checkOption('hasBlocks'); |
|||||
219 | 1 | $this->translatable = $this->checkOption('hasTranslation'); |
|||||
220 | 1 | $this->sluggable = $this->checkOption('hasSlug'); |
|||||
221 | 1 | $this->mediable = $this->checkOption('hasMedias'); |
|||||
222 | $this->fileable = $this->checkOption('hasFiles'); |
||||||
223 | $this->sortable = $this->checkOption('hasPosition'); |
||||||
224 | 1 | $this->revisionable = $this->checkOption('hasRevisions'); |
|||||
225 | 1 | $this->nestable = $this->checkOption('hasNesting'); |
|||||
226 | |||||||
227 | if ($this->nestable) { |
||||||
228 | $this->sortable = true; |
||||||
229 | } |
||||||
230 | |||||||
231 | $activeTraits = [ |
||||||
232 | $this->blockable, |
||||||
233 | 1 | $this->translatable, |
|||||
234 | 1 | $this->sluggable, |
|||||
235 | 1 | $this->mediable, |
|||||
236 | 1 | $this->fileable, |
|||||
237 | $this->revisionable, |
||||||
238 | 1 | $this->sortable, |
|||||
239 | $this->nestable, |
||||||
240 | 1 | ]; |
|||||
241 | |||||||
242 | 1 | $this->createCapsuleNamespace($moduleTitle, $modelName); |
|||||
243 | $this->createCapsulePath($moduleTitle, $modelName); |
||||||
244 | 1 | ||||||
245 | $this->createMigration($moduleName); |
||||||
246 | $this->createModels($modelName, $activeTraits); |
||||||
247 | $this->createRepository($modelName, $activeTraits); |
||||||
248 | $this->createController($moduleName, $modelName); |
||||||
249 | $this->createRequest($modelName); |
||||||
250 | $this->createViews($moduleName); |
||||||
251 | |||||||
252 | if ($this->isCapsule) { |
||||||
253 | 1 | $this->createCapsuleRoutes($moduleName); |
|||||
0 ignored issues
–
show
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
This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue. If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.
Loading history...
|
|||||||
254 | $this->createCapsuleSeed($moduleName); |
||||||
255 | 1 | } elseif ($this->isSingleton) { |
|||||
256 | $this->createSingletonSeed($modelName); |
||||||
257 | 1 | $this->info("\nAdd to routes/admin.php:\n"); |
|||||
258 | $this->info(" Route::singleton('{$singularModuleName}');\n"); |
||||||
259 | 1 | } else { |
|||||
260 | 1 | $this->info("\nAdd to routes/admin.php:\n"); |
|||||
261 | $this->info(" Route::module('{$moduleName}');\n"); |
||||||
262 | 1 | } |
|||||
263 | |||||||
264 | 1 | $navModuleName = $this->isSingleton ? $singularModuleName : $moduleName; |
|||||
265 | 1 | $navTitle = $this->isSingleton ? $modelName : $moduleTitle; |
|||||
266 | 1 | $navType = $this->isSingleton ? 'singleton' : 'module'; |
|||||
267 | 1 | ||||||
268 | $this->info("Setup a new CMS menu item in config/twill-navigation.php:\n"); |
||||||
269 | $this->info(" '{$navModuleName}' => ["); |
||||||
270 | 1 | $this->info(" 'title' => '{$navTitle}',"); |
|||||
271 | $this->info(" '{$navType}' => true,"); |
||||||
272 | $this->info(" ],\n"); |
||||||
273 | 1 | ||||||
274 | 1 | if ($this->isCapsule) { |
|||||
275 | $this->info("Setup your new Capsule in config/twill.php:\n"); |
||||||
276 | 1 | $this->info(" 'capsules' => ["); |
|||||
277 | $this->info(" 'list' => ["); |
||||||
278 | 1 | $this->info(" ["); |
|||||
279 | $this->info(" 'name' => '{$this->capsule['name']}',"); |
||||||
280 | 1 | $this->info(" 'enabled' => true,"); |
|||||
281 | $this->info(" ],"); |
||||||
282 | $this->info(" ],"); |
||||||
283 | 1 | $this->info(" ],\n"); |
|||||
284 | 1 | } |
|||||
285 | |||||||
286 | 1 | if ($this->isSingleton) { |
|||||
287 | $this->info("Migrate your database & seed your singleton module:\n"); |
||||||
288 | 1 | $this->info(" php artisan migrate\n"); |
|||||
289 | $this->info(" php artisan db:seed {$modelName}Seeder\n"); |
||||||
290 | 1 | } else { |
|||||
291 | $this->info("Migrate your database.\n"); |
||||||
292 | } |
||||||
293 | 1 | ||||||
294 | $this->info("Enjoy."); |
||||||
295 | 1 | ||||||
296 | 1 | if ($this->nestable && !class_exists('\Kalnoy\Nestedset\NestedSet')) { |
|||||
297 | 1 | $this->warn("\nTo support module nesting, you must install the `kalnoy/nestedset` package:"); |
|||||
298 | $this->warn("\n composer require kalnoy/nestedset\n"); |
||||||
299 | } |
||||||
300 | |||||||
301 | 1 | $this->composer->dumpAutoloads(); |
|||||
302 | } |
||||||
303 | 1 | ||||||
304 | /** |
||||||
305 | 1 | * Creates a new module database migration file. |
|||||
306 | * |
||||||
307 | 1 | * @param string $moduleName |
|||||
308 | 1 | * @return void |
|||||
309 | * @throws \Illuminate\Contracts\Filesystem\FileNotFoundException |
||||||
310 | */ |
||||||
311 | 1 | private function createMigration($moduleName = 'items') |
|||||
312 | 1 | { |
|||||
313 | $table = Str::snake($moduleName); |
||||||
314 | $tableClassName = Str::studly($table); |
||||||
315 | |||||||
316 | $className = "Create{$tableClassName}Tables"; |
||||||
0 ignored issues
–
show
|
|||||||
317 | 1 | ||||||
318 | 1 | $migrationName = 'create_' . $table . '_tables'; |
|||||
319 | 1 | ||||||
320 | 1 | if (!count(glob($this->databasePath('migrations/*' . $migrationName . '.php')))) { |
|||||
321 | 1 | $migrationPath = $this->databasePath() . '/migrations'; |
|||||
322 | |||||||
323 | 1 | $this->makeDir($migrationPath); |
|||||
324 | 1 | ||||||
325 | 1 | $fullPath = $this->laravel['migration.creator']->create($migrationName, $migrationPath); |
|||||
326 | 1 | ||||||
327 | $stub = str_replace( |
||||||
328 | 1 | ['{{table}}', '{{singularTableName}}', '{{tableClassName}}'], |
|||||
329 | [$table, Str::singular($table), $tableClassName], |
||||||
330 | 1 | $this->files->get(__DIR__ . '/stubs/migration.stub') |
|||||
331 | 1 | ); |
|||||
332 | |||||||
333 | 1 | if ($this->translatable) { |
|||||
334 | $stub = preg_replace('/{{!hasTranslation}}[\s\S]+?{{\/!hasTranslation}}/', '', $stub); |
||||||
335 | 1 | } else { |
|||||
336 | 1 | $stub = str_replace([ |
|||||
337 | 1 | '{{!hasTranslation}}', |
|||||
338 | 1 | '{{/!hasTranslation}}', |
|||||
339 | 1 | ], '', $stub); |
|||||
340 | } |
||||||
341 | |||||||
342 | $stub = $this->renderStubForOption($stub, 'hasTranslation', $this->translatable); |
||||||
343 | $stub = $this->renderStubForOption($stub, 'hasSlug', $this->sluggable); |
||||||
344 | 1 | $stub = $this->renderStubForOption($stub, 'hasRevisions', $this->revisionable); |
|||||
345 | $stub = $this->renderStubForOption($stub, 'hasPosition', $this->sortable); |
||||||
346 | $stub = $this->renderStubForOption($stub, 'hasNesting', $this->nestable); |
||||||
347 | |||||||
348 | $stub = preg_replace('/\}\);[\s\S]+?Schema::create/', "});\n\n Schema::create", $stub); |
||||||
349 | |||||||
350 | $this->files->put($fullPath, $stub); |
||||||
351 | |||||||
352 | $this->info("Migration created successfully! Add some fields!"); |
||||||
353 | } |
||||||
354 | } |
||||||
355 | 1 | ||||||
356 | /** |
||||||
357 | 1 | * Creates new model class files for the given model name and traits. |
|||||
358 | * |
||||||
359 | 1 | * @param string $modelName |
|||||
360 | * @param array $activeTraits |
||||||
361 | 1 | * @throws \Illuminate\Contracts\Filesystem\FileNotFoundException |
|||||
362 | */ |
||||||
363 | 1 | private function createModels($modelName = 'Item', $activeTraits = []) |
|||||
364 | 1 | { |
|||||
365 | 1 | $modelClassName = $this->namespace('models', 'Models', $modelName); |
|||||
366 | |||||||
367 | $modelsDir = $this->isCapsule ? $this->capsule['models_dir'] : 'Models'; |
||||||
368 | |||||||
369 | 1 | $this->makeTwillDirectory($modelsDir); |
|||||
370 | |||||||
371 | 1 | if ($this->translatable) { |
|||||
372 | $this->makeTwillDirectory($baseDir = $this->isCapsule ? $modelsDir : "{$modelsDir}/Translations"); |
||||||
373 | 1 | ||||||
374 | $modelTranslationClassName = $modelName . 'Translation'; |
||||||
375 | 1 | ||||||
376 | $stub = str_replace( |
||||||
377 | 1 | ['{{modelTranslationClassName}}', '{{modelClassWithNamespace}}', '{{modelClassName}}', '{{namespace}}', '{{baseTranslationModel}}'], |
|||||
378 | 1 | [$modelTranslationClassName, $modelClassName, $modelName, $this->namespace('models', 'Models\Translations'), config('twill.base_translation_model')], |
|||||
379 | $this->files->get(__DIR__ . '/stubs/model_translation.stub') |
||||||
380 | ); |
||||||
381 | |||||||
382 | twill_put_stub(twill_path("{$baseDir}/" . $modelTranslationClassName . '.php'), $stub); |
||||||
383 | } |
||||||
384 | |||||||
385 | if ($this->sluggable) { |
||||||
386 | $this->makeTwillDirectory($baseDir = $this->isCapsule ? $modelsDir : "{$modelsDir}/Slugs"); |
||||||
387 | |||||||
388 | 1 | $modelSlugClassName = $modelName . 'Slug'; |
|||||
389 | |||||||
390 | 1 | $stub = str_replace( |
|||||
391 | ['{{modelSlugClassName}}', '{{modelClassWithNamespace}}', '{{modelName}}', '{{namespace}}', '{{baseSlugModel}}'], |
||||||
392 | 1 | [$modelSlugClassName, $modelClassName, Str::snake($modelName), $this->namespace('models', 'Models\Slugs'), config('twill.base_slug_model')], |
|||||
393 | $this->files->get(__DIR__ . '/stubs/model_slug.stub') |
||||||
394 | 1 | ); |
|||||
395 | 1 | ||||||
396 | 1 | twill_put_stub(twill_path("{$baseDir}/" . $modelSlugClassName . '.php'), $stub); |
|||||
397 | 1 | } |
|||||
398 | |||||||
399 | if ($this->revisionable) { |
||||||
400 | 1 | $this->makeTwillDirectory($baseDir = $this->isCapsule ? $modelsDir : "{$modelsDir}/Revisions"); |
|||||
401 | |||||||
402 | 1 | $modelRevisionClassName = $modelName . 'Revision'; |
|||||
403 | 1 | ||||||
404 | $stub = str_replace( |
||||||
405 | ['{{modelRevisionClassName}}', '{{modelClassWithNamespace}}', '{{modelName}}', '{{namespace}}', '{{baseRevisionModel}}'], |
||||||
406 | [$modelRevisionClassName, $modelClassName, Str::snake($modelName), $this->namespace('models', 'Models\Revisions'), config('twill.base_revision_model')], |
||||||
407 | $this->files->get(__DIR__ . '/stubs/model_revision.stub') |
||||||
408 | ); |
||||||
409 | |||||||
410 | twill_put_stub(twill_path("{$baseDir}/" . $modelRevisionClassName . '.php'), $stub); |
||||||
411 | } |
||||||
412 | 1 | ||||||
413 | $activeModelTraits = []; |
||||||
414 | 1 | ||||||
415 | foreach ($activeTraits as $index => $traitIsActive) { |
||||||
416 | 1 | if ($traitIsActive) { |
|||||
417 | !isset($this->modelTraits[$index]) ?: $activeModelTraits[] = $this->modelTraits[$index]; |
||||||
418 | 1 | } |
|||||
419 | } |
||||||
420 | 1 | ||||||
421 | $activeModelTraitsString = empty($activeModelTraits) ? '' : 'use ' . rtrim(implode(', ', $activeModelTraits), ', ') . ';'; |
||||||
422 | 1 | ||||||
423 | 1 | $activeModelTraitsImports = empty($activeModelTraits) ? '' : "use A17\Twill\Models\Behaviors\\" . implode(";\nuse A17\Twill\Models\Behaviors\\", $activeModelTraits) . ";"; |
|||||
424 | |||||||
425 | $activeModelImplements = $this->sortable ? 'implements Sortable' : ''; |
||||||
426 | |||||||
427 | if ($this->sortable) { |
||||||
428 | $activeModelTraitsImports .= "\nuse A17\Twill\Models\Behaviors\Sortable;"; |
||||||
429 | } |
||||||
430 | |||||||
431 | $stub = str_replace([ |
||||||
432 | 1 | '{{modelClassName}}', |
|||||
433 | '{{modelTraits}}', |
||||||
434 | 1 | '{{modelImports}}', |
|||||
435 | '{{modelImplements}}', |
||||||
436 | 1 | '{{namespace}}', |
|||||
437 | '{{baseModel}}', |
||||||
438 | 1 | ], [ |
|||||
439 | $modelName, |
||||||
440 | 1 | $activeModelTraitsString, |
|||||
441 | $activeModelTraitsImports, |
||||||
442 | 1 | $activeModelImplements, |
|||||
443 | 1 | $this->namespace('models', 'Models'), |
|||||
444 | config('twill.base_model'), |
||||||
445 | 1 | ], $this->files->get(__DIR__ . '/stubs/model.stub')); |
|||||
446 | |||||||
447 | 1 | $stub = $this->renderStubForOption($stub, 'hasTranslation', $this->translatable); |
|||||
448 | 1 | $stub = $this->renderStubForOption($stub, 'hasSlug', $this->sluggable); |
|||||
449 | $stub = $this->renderStubForOption($stub, 'hasMedias', $this->mediable); |
||||||
450 | $stub = $this->renderStubForOption($stub, 'hasPosition', $this->sortable); |
||||||
451 | |||||||
452 | twill_put_stub(twill_path("{$modelsDir}/" . $modelName . '.php'), $stub); |
||||||
453 | |||||||
454 | $this->info("Models created successfully! Fill your fillables!"); |
||||||
455 | } |
||||||
456 | |||||||
457 | private function renderStubForOption($stub, $option, $enabled) |
||||||
458 | { |
||||||
459 | if ($enabled) { |
||||||
460 | $stub = str_replace([ |
||||||
461 | '{{' . $option . '}}', |
||||||
462 | '{{/' . $option . '}}', |
||||||
463 | ], '', $stub); |
||||||
464 | } else { |
||||||
465 | $stub = preg_replace('/{{' . $option . '}}[\s\S]+?{{\/' . $option . '}}/', '', $stub); |
||||||
466 | } |
||||||
467 | |||||||
468 | return $stub; |
||||||
469 | } |
||||||
470 | |||||||
471 | /** |
||||||
472 | * Creates new repository class file for the given model name. |
||||||
473 | * |
||||||
474 | * @param string $modelName |
||||||
475 | * @param array $activeTraits |
||||||
476 | * @return void |
||||||
477 | * @throws \Illuminate\Contracts\Filesystem\FileNotFoundException |
||||||
478 | */ |
||||||
479 | private function createRepository($modelName = 'Item', $activeTraits = []) |
||||||
480 | { |
||||||
481 | $modelsDir = $this->isCapsule ? $this->capsule['repositories_dir'] : 'Repositories'; |
||||||
482 | |||||||
483 | $modelClass = $this->isCapsule ? $this->capsule['model'] : config('twill.namespace') . "\Models\\{$this->capsule['singular']}"; |
||||||
484 | |||||||
485 | $this->makeTwillDirectory($modelsDir); |
||||||
486 | |||||||
487 | $repositoryClassName = $modelName . 'Repository'; |
||||||
488 | |||||||
489 | $activeRepositoryTraits = []; |
||||||
490 | |||||||
491 | foreach ($activeTraits as $index => $traitIsActive) { |
||||||
492 | if ($traitIsActive) { |
||||||
493 | !isset($this->repositoryTraits[$index]) ?: $activeRepositoryTraits[] = $this->repositoryTraits[$index]; |
||||||
494 | } |
||||||
495 | } |
||||||
496 | |||||||
497 | $activeRepositoryTraits = array_filter($activeRepositoryTraits); |
||||||
498 | |||||||
499 | $activeRepositoryTraitsString = empty($activeRepositoryTraits) ? '' : 'use ' . (empty($activeRepositoryTraits) ? "" : rtrim(implode(', ', $activeRepositoryTraits), ', ') . ';'); |
||||||
500 | |||||||
501 | $activeRepositoryTraitsImports = empty($activeRepositoryTraits) ? '' : "use A17\Twill\Repositories\Behaviors\\" . implode(";\nuse A17\Twill\Repositories\Behaviors\\", $activeRepositoryTraits) . ";"; |
||||||
502 | |||||||
503 | $stub = str_replace( |
||||||
504 | ['{{repositoryClassName}}', '{{modelName}}', '{{repositoryTraits}}', '{{repositoryImports}}', '{{namespace}}', '{{modelClass}}', '{{baseRepository}}'], |
||||||
505 | [$repositoryClassName, $modelName, $activeRepositoryTraitsString, $activeRepositoryTraitsImports, $this->namespace('repositories', 'Repositories'), $modelClass, config('twill.base_repository')], |
||||||
506 | $this->files->get(__DIR__ . '/stubs/repository.stub') |
||||||
507 | ); |
||||||
508 | |||||||
509 | twill_put_stub(twill_path("{$modelsDir}/" . $repositoryClassName . '.php'), $stub); |
||||||
510 | |||||||
511 | $this->info("Repository created successfully! Control all the things!"); |
||||||
512 | } |
||||||
513 | |||||||
514 | /** |
||||||
515 | * Create a new controller class file for the given module name and model name. |
||||||
516 | * |
||||||
517 | * @param string $moduleName |
||||||
518 | * @param string $modelName |
||||||
519 | * @return void |
||||||
520 | * @throws \Illuminate\Contracts\Filesystem\FileNotFoundException |
||||||
521 | */ |
||||||
522 | private function createController($moduleName = 'items', $modelName = 'Item') |
||||||
523 | { |
||||||
524 | $controllerClassName = $modelName . 'Controller'; |
||||||
525 | |||||||
526 | $dir = $this->isCapsule ? $this->capsule['controllers_dir'] : 'Http/Controllers/Admin'; |
||||||
527 | |||||||
528 | if ($this->isSingleton) { |
||||||
529 | $baseController = config('twill.base_singleton_controller'); |
||||||
530 | } elseif ($this->nestable) { |
||||||
531 | $baseController = config('twill.base_nested_controller'); |
||||||
532 | } else { |
||||||
533 | $baseController = config('twill.base_controller'); |
||||||
534 | } |
||||||
535 | |||||||
536 | $this->makeTwillDirectory($dir); |
||||||
537 | |||||||
538 | $stub = str_replace( |
||||||
539 | ['{{moduleName}}', '{{controllerClassName}}', '{{namespace}}', '{{baseController}}'], |
||||||
540 | [$moduleName, $controllerClassName, $this->namespace('controllers', 'Http\Controllers\Admin'), $baseController], |
||||||
541 | $this->files->get(__DIR__ . '/stubs/controller.stub') |
||||||
542 | ); |
||||||
543 | |||||||
544 | $permalinkOption = ''; |
||||||
545 | $reorderOption = ''; |
||||||
546 | |||||||
547 | if (!$this->sluggable) { |
||||||
548 | $permalinkOption = "'permalink' => false,"; |
||||||
549 | } |
||||||
550 | |||||||
551 | if ($this->nestable) { |
||||||
552 | $reorderOption = "'reorder' => true,"; |
||||||
553 | |||||||
554 | $stub = str_replace(['{{hasNesting}}', '{{/hasNesting}}'], '', $stub); |
||||||
555 | } else { |
||||||
556 | $stub = preg_replace('/{{hasNesting}}[\s\S]+?{{\/hasNesting}}/', '', $stub); |
||||||
557 | } |
||||||
558 | |||||||
559 | $stub = str_replace( |
||||||
560 | ['{{permalinkOption}}', '{{reorderOption}}'], |
||||||
561 | [$permalinkOption, $reorderOption], |
||||||
562 | $stub |
||||||
563 | ); |
||||||
564 | |||||||
565 | // Remove lines including only whitespace, leave true empty lines untouched |
||||||
566 | $stub = preg_replace('/^[\s]+\n/m', '', $stub); |
||||||
567 | |||||||
568 | twill_put_stub(twill_path("{$dir}/" . $controllerClassName . '.php'), $stub); |
||||||
569 | |||||||
570 | $this->info("Controller created successfully! Define your index/browser/form endpoints options!"); |
||||||
571 | } |
||||||
572 | |||||||
573 | /** |
||||||
574 | * Creates a new request class file for the given model name. |
||||||
575 | * |
||||||
576 | * @param string $modelName |
||||||
577 | * @return void |
||||||
578 | * @throws \Illuminate\Contracts\Filesystem\FileNotFoundException |
||||||
579 | */ |
||||||
580 | private function createRequest($modelName = 'Item') |
||||||
581 | { |
||||||
582 | $dir = $this->isCapsule ? $this->capsule['requests_dir'] : 'Http/Requests/Admin'; |
||||||
583 | |||||||
584 | $this->makeTwillDirectory($dir); |
||||||
585 | |||||||
586 | $requestClassName = $modelName . 'Request'; |
||||||
587 | |||||||
588 | $stub = str_replace( |
||||||
589 | ['{{requestClassName}}', '{{namespace}}', '{{baseRequest}}'], |
||||||
590 | [$requestClassName, $this->namespace('requests', 'Http\Requests\Admin'), config('twill.base_request')], |
||||||
591 | $this->files->get(__DIR__ . '/stubs/request.stub') |
||||||
592 | ); |
||||||
593 | |||||||
594 | twill_put_stub(twill_path("{$dir}/" . $requestClassName . '.php'), $stub); |
||||||
595 | |||||||
596 | $this->info("Form request created successfully! Add some validation rules!"); |
||||||
597 | } |
||||||
598 | |||||||
599 | /** |
||||||
600 | * Creates appropriate module Blade view files. |
||||||
601 | * |
||||||
602 | * @param string $moduleName |
||||||
603 | * @return void |
||||||
604 | * @throws \Illuminate\Contracts\Filesystem\FileNotFoundException |
||||||
605 | */ |
||||||
606 | private function createViews($moduleName = 'items') |
||||||
607 | { |
||||||
608 | $viewsPath = $this->viewPath($moduleName); |
||||||
609 | |||||||
610 | $this->makeTwillDirectory($viewsPath); |
||||||
611 | |||||||
612 | $formView = $this->translatable ? 'form_translatable' : 'form'; |
||||||
613 | |||||||
614 | twill_put_stub($viewsPath . '/form.blade.php', $this->files->get(__DIR__ . '/stubs/' . $formView . '.blade.stub')); |
||||||
615 | |||||||
616 | $this->info("Form view created successfully! Include your form fields using @formField directives!"); |
||||||
617 | } |
||||||
618 | |||||||
619 | /** |
||||||
620 | * Creates a basic routes file for the Capsule. |
||||||
621 | * |
||||||
622 | * @param string $moduleName |
||||||
623 | * @return void |
||||||
624 | * @throws \Illuminate\Contracts\Filesystem\FileNotFoundException |
||||||
625 | */ |
||||||
626 | public function createCapsuleRoutes() |
||||||
627 | { |
||||||
628 | $this->makeDir($this->capsule['routes_file']); |
||||||
629 | |||||||
630 | $contents = str_replace( |
||||||
631 | '{{moduleName}}', |
||||||
632 | $this->capsule['module'], |
||||||
633 | $this->files->get(__DIR__ . '/stubs/routes_admin.stub') |
||||||
634 | ); |
||||||
635 | |||||||
636 | twill_put_stub($this->capsule['routes_file'], $contents); |
||||||
637 | |||||||
638 | $this->info("Routes file created successfully!"); |
||||||
639 | } |
||||||
640 | |||||||
641 | /** |
||||||
642 | * Creates a new capsule database seed file. |
||||||
643 | * |
||||||
644 | * @param string $moduleName |
||||||
645 | * @return void |
||||||
646 | * @throws \Illuminate\Contracts\Filesystem\FileNotFoundException |
||||||
647 | */ |
||||||
648 | private function createCapsuleSeed($moduleName = 'items') |
||||||
649 | { |
||||||
650 | $this->makeTwillDirectory($this->capsule['seeds_psr4_path']); |
||||||
651 | |||||||
652 | $stub = $this->files->get(__DIR__ . '/stubs/database_seeder_capsule.stub'); |
||||||
653 | |||||||
654 | $stub = str_replace('{moduleName}', $this->capsule['plural'], $stub); |
||||||
655 | |||||||
656 | $this->files->put("{$this->capsule['seeds_psr4_path']}/DatabaseSeeder.php", $stub); |
||||||
657 | |||||||
658 | $this->info("Seed created successfully!"); |
||||||
659 | } |
||||||
660 | |||||||
661 | /** |
||||||
662 | * Creates a new singleton module database seed file. |
||||||
663 | * |
||||||
664 | * @param string $modelName |
||||||
665 | * @return void |
||||||
666 | * @throws \Illuminate\Contracts\Filesystem\FileNotFoundException |
||||||
667 | */ |
||||||
668 | private function createSingletonSeed($modelName = 'Item') |
||||||
669 | { |
||||||
670 | $repositoryName = $modelName . 'Repository'; |
||||||
671 | $seederName = $modelName . 'Seeder'; |
||||||
672 | |||||||
673 | $dir = $this->databasePath('seeders'); |
||||||
674 | |||||||
675 | $this->makeTwillDirectory($dir); |
||||||
676 | |||||||
677 | $stub = $this->files->get(__DIR__ . '/stubs/database_seeder_singleton.stub'); |
||||||
678 | |||||||
679 | $stub = $this->replaceVariables([ |
||||||
680 | 'seederClassName' => $seederName, |
||||||
681 | 'modelClassName' => $modelName, |
||||||
682 | 'repositoryClassName' => $repositoryName, |
||||||
683 | ], $stub); |
||||||
684 | |||||||
685 | $stub = $this->replaceConditionals([ |
||||||
686 | 'hasTranslations' => $this->translatable, |
||||||
687 | '!hasTranslations' => !$this->translatable, |
||||||
688 | ], $stub); |
||||||
689 | |||||||
690 | $stub = $this->removeEmptyLinesWithOnlySpaces($stub); |
||||||
691 | |||||||
692 | $this->files->put("{$dir}/{$seederName}.php", $stub); |
||||||
693 | |||||||
694 | $this->info("Seed created successfully!"); |
||||||
695 | } |
||||||
696 | |||||||
697 | private function checkOption($option) |
||||||
698 | { |
||||||
699 | if (!$this->hasOption($option)) { |
||||||
700 | return false; |
||||||
701 | } |
||||||
702 | |||||||
703 | if ($this->option($option) || $this->option('all')) { |
||||||
704 | return true; |
||||||
705 | } |
||||||
706 | |||||||
707 | $questions = [ |
||||||
708 | 'hasBlocks' => 'Do you need to use the block editor on this module?', |
||||||
709 | 'hasTranslation' => 'Do you need to translate content on this module?', |
||||||
710 | 'hasSlug' => 'Do you need to generate slugs on this module?', |
||||||
711 | 'hasMedias' => 'Do you need to attach images on this module?', |
||||||
712 | 'hasFiles' => 'Do you need to attach files on this module?', |
||||||
713 | 'hasPosition' => 'Do you need to manage the position of records on this module?', |
||||||
714 | 'hasRevisions' => 'Do you need to enable revisions on this module?', |
||||||
715 | 'hasNesting' => 'Do you need to enable nesting on this module?', |
||||||
716 | ]; |
||||||
717 | |||||||
718 | $defaultAnswers = [ |
||||||
719 | 'hasNesting' => 0, |
||||||
720 | ]; |
||||||
721 | |||||||
722 | $currentDefaultAnswer = $this->defaultsAnswserToNo ? 0 : ($defaultAnswers[$option] ?? 1); |
||||||
723 | |||||||
724 | return 'yes' === $this->choice($questions[$option], ['no', 'yes'], $currentDefaultAnswer); |
||||||
725 | } |
||||||
726 | |||||||
727 | public function createCapsulePath($moduleName, $modelName) |
||||||
728 | { |
||||||
729 | if (!$this->isCapsule) { |
||||||
730 | $this->moduleBasePath = base_path(); |
||||||
731 | |||||||
732 | return; |
||||||
733 | } |
||||||
734 | |||||||
735 | $this->checkCapsuleDirectory( |
||||||
736 | $this->moduleBasePath = config('twill.capsules.path') . "/{$moduleName}" |
||||||
737 | ); |
||||||
738 | |||||||
739 | $this->makeDir($this->moduleBasePath); |
||||||
740 | } |
||||||
741 | |||||||
742 | public function createCapsuleNamespace($module, $model) |
||||||
743 | { |
||||||
744 | $base = config('twill.capsules.namespace'); |
||||||
745 | |||||||
746 | $this->capsuleNamespace = "{$base}\\{$module}"; |
||||||
0 ignored issues
–
show
|
|||||||
747 | } |
||||||
748 | |||||||
749 | public function databasePath($path = '') |
||||||
750 | { |
||||||
751 | if (!$this->isCapsule) { |
||||||
752 | return database_path($path); |
||||||
753 | } |
||||||
754 | |||||||
755 | return "{$this->moduleBasePath}/database" . (filled($path) ? "/{$path}" : ''); |
||||||
756 | } |
||||||
757 | |||||||
758 | public function makeDir($dir) |
||||||
759 | { |
||||||
760 | $info = pathinfo($dir); |
||||||
761 | |||||||
762 | $dir = isset($info['extension']) ? $info['dirname'] : $dir; |
||||||
763 | |||||||
764 | if (!is_dir($dir)) { |
||||||
765 | mkdir($dir, 0755, true); |
||||||
766 | } |
||||||
767 | |||||||
768 | if (!is_dir($dir)) { |
||||||
769 | $this->info("It wasn't possible to create capsule directory {$dir}"); |
||||||
770 | |||||||
771 | die; |
||||||
0 ignored issues
–
show
|
|||||||
772 | } |
||||||
773 | } |
||||||
774 | |||||||
775 | public function makeTwillDirectory($path) |
||||||
776 | { |
||||||
777 | make_twill_directory($path); |
||||||
778 | } |
||||||
779 | |||||||
780 | public function namespace ($type, $suffix, $class = null) { |
||||||
781 | $class = (filled($class) ? "\\$class" : ''); |
||||||
782 | |||||||
783 | if (!$this->isCapsule) { |
||||||
784 | return "App\\{$suffix}{$class}"; |
||||||
785 | } |
||||||
786 | |||||||
787 | return $this->capsule[$type] . $class; |
||||||
788 | } |
||||||
789 | |||||||
790 | public function viewPath($moduleName) |
||||||
791 | { |
||||||
792 | if (!$this->isCapsule) { |
||||||
793 | return $viewsPath = $this->config->get('view.paths')[0] . '/admin/' . $moduleName; |
||||||
0 ignored issues
–
show
|
|||||||
794 | } |
||||||
795 | |||||||
796 | $this->makeDir($dir = "{$this->moduleBasePath}/resources/views/admin"); |
||||||
797 | |||||||
798 | return $dir; |
||||||
799 | } |
||||||
800 | |||||||
801 | /** |
||||||
802 | * @param array $variables |
||||||
803 | * @param string $stub |
||||||
804 | * @param array|null $delimiters |
||||||
805 | * @return string |
||||||
806 | */ |
||||||
807 | public function replaceVariables($variables, $stub, $delimiters = null) |
||||||
808 | { |
||||||
809 | $delimiters = $delimiters ?: ['{{', '}}']; |
||||||
810 | |||||||
811 | foreach ($variables as $key => $value) { |
||||||
812 | $key = "{$delimiters[0]}{$key}{$delimiters[1]}"; |
||||||
813 | |||||||
814 | $stub = str_replace($key, $value, $stub); |
||||||
815 | } |
||||||
816 | |||||||
817 | return $stub; |
||||||
818 | } |
||||||
819 | |||||||
820 | /** |
||||||
821 | * @param array $variables |
||||||
822 | * @param string $stub |
||||||
823 | * @param array|null $delimiters |
||||||
824 | * @return string |
||||||
825 | */ |
||||||
826 | public function replaceConditionals($conditionals, $stub, $delimiters = null) |
||||||
827 | { |
||||||
828 | $delimiters = $delimiters ?: ['{{', '}}']; |
||||||
829 | |||||||
830 | foreach ($conditionals as $key => $value) { |
||||||
831 | $start = "{$delimiters[0]}{$key}{$delimiters[1]}"; |
||||||
832 | $end = "{$delimiters[0]}\/{$key}{$delimiters[1]}"; |
||||||
833 | |||||||
834 | if ((bool)$value) { |
||||||
835 | // replace delimiters only |
||||||
836 | $stub = preg_replace("/$start/", '', $stub); |
||||||
837 | $stub = preg_replace("/$end/", '', $stub); |
||||||
838 | } else { |
||||||
839 | // replace delimiters and everything between |
||||||
840 | $anything = '[\s\S]+?'; |
||||||
841 | $stub = preg_replace("/{$start}{$anything}{$end}/", '', $stub); |
||||||
842 | } |
||||||
843 | } |
||||||
844 | |||||||
845 | return $stub; |
||||||
846 | } |
||||||
847 | |||||||
848 | /** |
||||||
849 | * @param string $stub |
||||||
850 | * @return string |
||||||
851 | */ |
||||||
852 | public function removeEmptyLinesWithOnlySpaces($stub) |
||||||
853 | { |
||||||
854 | return preg_replace('/^ +\n/m', '', $stub); |
||||||
855 | } |
||||||
856 | } |
||||||
857 |
In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.