Passed
Push — master ( 3af74c...450d12 )
by Bruno
09:47
created

FrontendVueGenerator::getStubDir()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 0
dl 0
loc 3
rs 10
c 0
b 0
f 0
1
<?php declare(strict_types=1);
2
3
namespace Modelarium\Frontend;
4
5
use Formularium\Datatype;
6
use Formularium\Element;
7
use Formularium\Field;
8
use Formularium\Model;
9
use Formularium\FrameworkComposer;
10
use Formularium\Frontend\HTML\Element\Button;
11
use Formularium\Frontend\HTML\Element\Table;
12
use Formularium\Frontend\Vue\Element\Pagination as PaginationVue;
13
use Formularium\Frontend\Vue\Framework as FrameworkVue;
14
use Modelarium\GeneratedCollection;
15
use Modelarium\GeneratedItem;
16
use Modelarium\Options;
17
use Formularium\StringUtil;
18
19
use function Safe\file_get_contents;
20
use function Safe\scandir;
21
use function Safe\substr;
22
23
class FrontendVueGenerator
24
{
25
    /**
26
     * @var FrontendGenerator
27
     */
28
    protected $generator = null;
29
30
    /**
31
     *
32
     * @var Options
33
     */
34
    protected $options = null;
35
36
    /**
37
     * Option defaults
38
     *
39
     * @var array
40
     */
41
    const DEFAULT_VUE_OPTIONS = [
42
        /**
43
         * Use the runtimeValidator JS library
44
         */
45
        'runtimeValidator' => false,
46
47
        /**
48
         * The axios variable name
49
         */
50
        'axios' => [
51
            'importFile' => 'axios',
52
            'method' => 'axios'
53
        ],
54
55
        /**
56
         * Generate action buttons even if we don't have a can field in the model
57
         */
58
        'actionButtonsNoCan' => false,
59
        /**
60
         * cleanIdentifier method
61
         */
62
        'cleanIdentifierBody' => 'return identifier;',
63
        /**
64
         * escapeIdentifier method
65
         */
66
        'escapeIdentifierBody' => 'return identifier;',
67
68
        'messages' => [
69
            'nothingFound' => 'Nothing found'
70
        ]
71
    ];
72
73
    public function __construct(FrontendGenerator $generator)
74
    {
75
        $this->generator = $generator;
76
        $this->options = (new Options())->setSectionDefaults('vue', self::DEFAULT_VUE_OPTIONS);
77
        $this->buildTemplateParameters();
78
    }
79
80
    public function getStubDir()
81
    {
82
        return $this->generator->getStubDir() . '/Vue/';
83
    }
84
85
    protected function getCollection(): GeneratedCollection
86
    {
87
        return $this->generator->getCollection();
88
    }
89
90
    public function generate(): void
91
    {
92
        /**
93
         * @var FrameworkVue $vue
94
         */
95
        $vue = $this->generator->getComposer()->getByName('Vue');
96
        $vueCode = $vue->getVueCode();
97
98
        // set basic data for vue
99
        $extraprops = [
100
            [
101
                'name' => 'id',
102
                'type' => 'String',
103
                'required' => true
104
            ]
105
        ];
106
        $vueCode->setExtraProps($extraprops);
107
108
        // build basic vue components
109
        $this->vuePublish();
110
        $this->makeVuePaginationComponent();
111
112
        $this->vueCard($vue);
113
        $this->vueLink($vue);
114
        $this->vueTableItem($vue);
115
        $this->vueTable($vue);
116
117
        $this->makeVue($vue, 'List', 'viewable');
118
        $this->makeVue($vue, 'Show', 'viewable');
119
        $this->makeVue($vue, 'Edit', 'editable');
120
        $this->vueForm($vue);
121
        $this->makeVueRoutes();
122
        $this->makeVueIndex();
123
    }
124
125
    public function buildTemplateParameters(): void
126
    {
127
        $hasVueRouter = $this->generator != null; //TODO: temporary true condition while we don't have a setting for this
128
129
        $hasCan = $this->generator->getModel()->getExtradataValue('hasCan', 'value', false);
130
        $routeBase = $this->generator->getRouteBase();
131
        $keyAttribute = $this->generator->getKeyAttribute();
132
        $targetAttribute = $hasVueRouter ? 'to' : 'href';
133
134
        $buttonCreate = $this->generator->getComposer()->nodeElement(
135
            'Button',
136
            [
137
                Button::TYPE => ($hasVueRouter ? 'router-link' : 'a'),
138
                Button::ATTRIBUTES => [
139
                    $targetAttribute => "/{$routeBase}/edit",
140
                ] + ($hasCan ? [ "v-if" => 'can(\'create\')' ]: []),
141
            ]
142
        )->setContent(
143
            '<i class="fa fa-plus"></i> Add new',
144
            true,
145
            true
146
        )->getRenderHTML();
147
148
        $buttonEdit = $this->generator->getComposer()->nodeElement(
149
            'Button',
150
            [
151
                Button::TYPE => ($hasVueRouter ? 'router-link' : 'a'),
152
                Button::ATTRIBUTES => [
153
                    $targetAttribute => "'/{$routeBase}/' + model.{$keyAttribute} + '/edit'",
154
                ] + ($hasCan ? [ "v-if" => 'can(\'edit\')' ]: []),
155
            ]
156
        )->setContent(
157
            '<i class="fa fa-pencil"></i> Edit',
158
            true,
159
            true
160
        )->getRenderHTML();
161
162
        $buttonDelete = $this->generator->getComposer()->nodeElement(
163
            'Button',
164
            [
165
                Button::TYPE => 'a',
166
                Button::COLOR => Button::COLOR_WARNING,
167
                Button::ATTRIBUTES => [
168
                    'href' => '#',
169
                    '@click.prevent' => 'remove',
170
                ] + ($hasCan ? [ "v-if" => 'can(\'delete\')' ]: []),
171
            ]
172
        )->setContent(
173
            '<i class="fa fa-trash"></i> Delete',
174
            true,
175
            true
176
        )->getRenderHTML();
177
178
        if (!$hasCan && $this->options->getOption('vue', 'actionButtonsNoCan') === false) {
179
            $this->generator->templateParameters['buttonCreate'] = '';
180
            $this->generator->templateParameters['buttonEdit'] = '';
181
            $this->generator->templateParameters['buttonDelete'] = '';
182
        } else {
183
            $this->generator->templateParameters['buttonCreate'] = $buttonCreate;
184
            $this->generator->templateParameters['buttonEdit'] = $buttonEdit;
185
            $this->generator->templateParameters['buttonDelete'] = $buttonDelete;
186
        }
187
        $this->generator->templateParameters['options'] = $this->options->getSection('vue');
188
189
        $this->generator->templateParameters['tableItemFields'] =
190
            array_values(array_map(function (Field $f) {
191
                if ($f->getDatatype()->getBasetype() === 'relationship') {
192
                    $name = $f->getName();
193
                    return "<{$name}-link v-bind=\"{$name}\"></{$name}-link>";
194
                }
195
                return '{{ ' . $f->getName() . ' }}';
196
            }, $this->generator->getTableFields()));
197
    }
198
199
    /**
200
     * Publishes the Vue component dependencies
201
     *
202
     * @return void
203
     */
204
    protected function vuePublish(): void
205
    {
206
        $this->getCollection()->push(
207
            new GeneratedItem(
208
                GeneratedItem::TYPE_FRONTEND,
209
                $this->generator->templateCallback(
210
                    __DIR__ . "/Vue/Renderable/RelationshipAutocomplete.vue",
211
                    $this->generator->getComposer()->getByName('Vue'),
0 ignored issues
show
Bug introduced by
It seems like $this->generator->getComposer()->getByName('Vue') can also be of type null; however, parameter $f of Modelarium\Frontend\Fron...tor::templateCallback() does only seem to accept Formularium\Framework, 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

211
                    /** @scrutinizer ignore-type */ $this->generator->getComposer()->getByName('Vue'),
Loading history...
212
                    [],
213
                    $this->generator->getModel()
214
                ),
215
                "Modelarium/RelationshipAutocomplete.vue"
216
            )
217
        );
218
        $this->getCollection()->push(
219
            new GeneratedItem(
220
                GeneratedItem::TYPE_FRONTEND,
221
                $this->generator->templateCallback(
222
                    __DIR__ . "/Vue/Renderable/RelationshipSelect.vue",
223
                    $this->generator->getComposer()->getByName('Vue'),
224
                    [],
225
                    $this->generator->getModel()
226
                ),
227
                "Modelarium/RelationshipSelect.vue"
228
            )
229
        );
230
        // $this->getCollection()->push(
231
        //     new GeneratedItem(
232
        //         GeneratedItem::TYPE_FRONTEND,
233
        //         file_get_contents(__DIR__ . "/Vue/Renderable/RelationshipSelectMultiple.vue"),
234
        //         "Modelarium/RelationshipSelectMultiple.vue"
235
        //     )
236
        // );
237
    }
238
239
    protected function makeVuePaginationComponent(): void
240
    {
241
        // TODO: this is called once per type
242
        $pagination = $this->generator->getComposer()->nodeElement(
243
            'Pagination',
244
            [
245
            ]
246
        );
247
        $html = $pagination->getRenderHTML();
248
        $script = PaginationVue::script();
249
250
        $this->getCollection()->push(
251
            new GeneratedItem(
252
                GeneratedItem::TYPE_FRONTEND,
253
                "<template>\n$html\n</template>\n<script>\n$script\n</script>\n",
254
                "Modelarium/Pagination.vue"
255
            )
256
        );
257
    }
258
259
    /**
260
     * Appends computed item
261
     *
262
     * @param FrameworkVue $vue
263
     * @return void
264
     */
265
    protected function vueCodeLinkItem(FrameworkVue $vue): void
266
    {
267
        $vue->getVueCode()->appendComputed(
268
            'link',
269
            'return "/' . $this->generator->getRouteBase() .
270
                '/" + this.escapeIdentifier(this.' . $this->generator->getKeyAttribute() . ')'
271
        );
272
    }
273
274
    protected function vueCard(FrameworkVue $vue): void
275
    {
276
        $vueCode = $vue->getVueCode();
277
        // set basic data for vue
278
        $extraprops = [
279
            [
280
                'name' => 'id',
281
                'type' => 'String',
282
                'required' => true
283
            ]
284
        ];
285
        $cardFieldNames = array_map(function (Field $f) {
286
            return $f->getName();
287
        }, $this->generator->getCardFields());
288
        $vueCode->setExtraProps($extraprops);
289
        $this->vueCodeLinkItem($vue);
290
291
        foreach ($this->generator->getCardFields() as $f) {
292
            $vueCode->appendExtraProp($f->getName(), [
293
                'name' => $f->getName(),
294
                'type' => $vueCode->mapTypeToJs($f->getDatatype()),
295
                'required' => true
296
            ]);
297
        }
298
        $vueCode->appendMethod(
299
            'escapeIdentifier(identifier)',
300
            $this->options->getOption('vue', 'escapeIdentifierBody')
301
        );
302
303
        $this->makeVue($vue, 'Card', 'viewable', $cardFieldNames);
304
    }
305
306
    protected function vueLink(FrameworkVue $vue): void
307
    {
308
        $vueCode = $vue->getVueCode();
309
        // set basic data for vue
310
        $vueCode->setExtraProps([]);
311
        $cardFieldNames = array_map(function (Field $f) {
312
            return $f->getName();
313
        }, $this->generator->getCardFields());
314
        foreach ($this->generator->getCardFields() as $f) {
315
            $vueCode->appendExtraProp($f->getName(), [
316
                'name' => $f->getName(),
317
                'type' => $vueCode->mapTypeToJs($f->getDatatype()),
318
                'required' => true
319
            ]);
320
        }
321
        foreach ($this->generator->getTitleFields() as $f) {
322
            $vueCode->appendExtraProp($f->getName(), [
323
                'name' => $f->getName(),
324
                'type' => $vueCode->mapTypeToJs($f->getDatatype()),
325
                'required' => true
326
            ]);
327
        }
328
329
        if (!$vueCode->getExtraProps()) {
330
            $vueCode->appendExtraProp('id', [
331
                'name' => 'id',
332
                'type' => 'String',
333
                'required' => true
334
            ]);
335
        }
336
337
        $this->vueCodeLinkItem($vue);
338
        $this->makeVue($vue, 'Link', 'viewable', $cardFieldNames);
339
    }
340
341
    public function vueTableItem(FrameworkVue $vue): void
342
    {
343
        $vueCode = $vue->getVueCode();
344
        $tableFieldNames = array_map(function (Field $f) {
345
            return $f->getName();
346
        }, $this->generator->getTableFields());
347
        $vueCode->setExtraProps([]);
348
        $vueCode->appendExtraProp(
349
            'id',
350
            [
351
                'name' => 'id',
352
                'type' => 'String',
353
                'required' => true
354
            ]
355
        );
356
357
        foreach ($this->generator->getTableFields() as $f) {
358
            $vueCode->appendExtraProp($f->getName(), [
359
                'name' => $f->getName(),
360
                'type' => $vueCode->mapTypeToJs($f->getDatatype()),
361
                'required' => true
362
            ]);
363
        }
364
        $this->makeVue($vue, 'TableItem', 'viewable', $tableFieldNames);
365
    }
366
367
    public function vueTable(FrameworkVue $vue): void
368
    {
369
        $this->makeVue($vue, 'Table', 'viewable');
370
    }
371
372
    public function vueForm(FrameworkVue $vue): void
373
    {
374
        $this->makeVue(
375
            $vue,
376
            'Form',
377
            'editable',
378
            function (Field $f) {
379
                if (!$f->getExtradata('modelFillable')) {
380
                    return false;
381
                }
382
                return true;
383
            }
384
        );
385
    }
386
387
    /**
388
     * @param FrameworkVue $vue
389
     * @param string $component
390
     * @param string $mode
391
     * @param string[]|callable $restrictFields
392
     * @return void
393
     */
394
    protected function makeVue(FrameworkVue $vue, string $component, string $mode, $restrictFields = null): void
395
    {
396
        $path = $this->generator->getModel()->getName() . '/' .
397
            $this->generator->getModel()->getName() . $component . '.vue';
398
399
        $stub = $this->getStubDir() . "/Vue{$component}.mustache.vue";
400
401
        if ($mode == 'editable') {
402
            $vue->setEditableTemplate(
403
                function (FrameworkVue $vue, array $data, Model $m) use ($stub) {
404
                    return $this->generator->templateCallback($stub, $vue, $data, $m);
405
                }
406
            );
407
408
            $this->getCollection()->push(
409
                new GeneratedItem(
410
                    GeneratedItem::TYPE_FRONTEND,
411
                    $this->generator->getModel()->editable($this->generator->getComposer(), [], $restrictFields),
412
                    $path
413
                )
414
            );
415
        } else {
416
            $vue->setViewableTemplate(
417
                function (FrameworkVue $vue, array $data, Model $m) use ($stub) {
418
                    return $this->generator->templateCallback($stub, $vue, $data, $m);
419
                }
420
            );
421
422
            $this->getCollection()->push(
423
                new GeneratedItem(
424
                    GeneratedItem::TYPE_FRONTEND,
425
                    $this->generator->getModel()->viewable($this->generator->getComposer(), [], $restrictFields),
426
                    $path
427
                )
428
            );
429
        }
430
        $vue->resetVueCode();
431
        $vue->getVueCode()->setFieldModelVariable('model.');
432
    }
433
434
    protected function makeVueIndex(): void
435
    {
436
        $path = $this->generator->getModel()->getName() . '/index.js';
437
        $name = $this->generator->getStudlyName();
438
439
        $contents = function ($basepath, $element) use ($name) {
0 ignored issues
show
Unused Code introduced by
The parameter $element is not used and could be removed. ( Ignorable by Annotation )

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

439
        $contents = function ($basepath, /** @scrutinizer ignore-unused */ $element) use ($name) {

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
440
            $dir = $basepath . '/' . $name;
441
            $import = [];
442
            $export = [];
443
            foreach (scandir($dir) as $i) {
444
                if (StringUtil::endsWith($i, '.vue')) {
445
                    $name = substr($i, 0, -4);
446
                    $import[] = "import $name from './$name.vue';";
447
                    $export[] = "    {$name},";
448
                }
449
            }
450
            return implode("\n", $import) . "\n\n" .
451
                "export default {\n" .
452
                implode("\n", $export) . "\n};\n";
453
        };
454
455
        // $items = [
456
        //     'Card',
457
        //     'Edit',
458
        //     'Link',
459
        //     'List',
460
        //     'Show',
461
        //     'Table',
462
        // ];
463
464
        // $import = array_map(
465
        //     function ($i) use ($name) {
466
        //         return "import {$name}$i from './{$name}$i.vue';";
467
        //     },
468
        //     $items
469
        // );
470
471
        // $export = array_map(
472
        //     function ($i) use ($name) {
473
        //         return "    {$name}$i,";
474
        //     },
475
        //     $items
476
        // );
477
478
        $this->getCollection()->push(
479
            new GeneratedItem(
480
                GeneratedItem::TYPE_FRONTEND,
481
                $contents,
482
                $path
483
            )
484
        );
485
    }
486
487
    protected function makeVueRoutes(): void
488
    {
489
        $path = $this->generator->getModel()->getName() . '/routes.js';
490
491
        $this->getCollection()->push(
492
            new GeneratedItem(
493
                GeneratedItem::TYPE_FRONTEND,
494
                $this->generator->templateFile(
495
                    $this->getStubDir() . "/routes.mustache.js",
496
                    [
497
                        'routeName' => $this->generator->getRouteBase(),
498
                        'keyAttribute' => $this->generator->getKeyAttribute()
499
                    ]
500
                ),
501
                $path
502
            )
503
        );
504
    }
505
}
506