Passed
Push — master ( 450d12...f65752 )
by Bruno
07:03
created

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

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

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