Passed
Push — master ( d9754b...57c7cf )
by Bruno
03:28
created

FrontendVueGenerator::vueForm()   A

Complexity

Conditions 2
Paths 1

Size

Total Lines 11
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 8
c 0
b 0
f 0
nc 1
nop 1
dl 0
loc 11
rs 10
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
17
use function Safe\file_get_contents;
18
use function Safe\json_encode;
19
20
class FrontendVueGenerator
21
{
22
    /**
23
     * @var FrontendGenerator
24
     */
25
    protected $generator = null;
26
27
    public function __construct(FrontendGenerator $generator)
28
    {
29
        $this->generator = $generator;
30
        $this->buildTemplateParameters();
31
    }
32
33
    protected function getCollection(): GeneratedCollection
34
    {
35
        return $this->generator->getCollection();
36
    }
37
38
    public function generate(): void
39
    {
40
        /**
41
         * @var FrameworkVue $vue
42
         */
43
        $vue = $this->generator->getComposer()->getByName('Vue');
44
        $vueCode = $vue->getVueCode();
45
46
        // set basic data for vue
47
        $extraprops = [
48
            [
49
                'name' => 'id',
50
                'type' => 'String',
51
                'required' => true
52
            ]
53
        ];
54
        $vueCode->setExtraProps($extraprops);
55
56
        // build basic vue components
57
        $this->vuePublish();
58
        $this->makeVuePaginationComponent();
59
60
        $this->vueCard($vue);
61
        $this->vueLink($vue);
62
        $this->vueTableItem($vue);
63
        $this->vueTable($vue);
64
65
        $this->makeVue($vue, 'List', 'viewable');
66
        $this->makeVue($vue, 'Show', 'viewable');
67
        $this->makeVue($vue, 'Edit', 'editable');
68
        $this->vueForm($vue);
69
        $this->makeVueRoutes();
70
        $this->makeVueIndex();
71
    }
72
73
    public function buildTemplateParameters(): void
74
    {
75
        $hasVueRouter = $this->generator != null; //TODO: temporary true condition while we don't have a setting for this
76
77
        $hasCan = $this->generator->getModel()->getExtradataValue('hasCan', 'value', false);
78
        $routeBase = $this->generator->getRouteBase();
79
        $keyAttribute = $this->generator->getKeyAttribute();
80
        $targetAttribute = $hasVueRouter ? 'to' : 'href';
81
        $buttonCreate = $this->generator->getComposer()->nodeElement(
82
            'Button',
83
            [
84
                Button::TYPE => ($hasVueRouter ? 'router-link' : 'a'),
85
                Button::ATTRIBUTES => [
86
                    $targetAttribute => "/{$routeBase}/edit",
87
                ] + ($hasCan ? [ "v-if" => 'can.create' ]: []),
88
            ]
89
        )->setContent(
90
            '<i class="fa fa-plus"></i> Add new',
91
            true,
92
            true
93
        )->getRenderHTML();
94
95
        $buttonEdit = $this->generator->getComposer()->nodeElement(
96
            'Button',
97
            [
98
                Button::TYPE => ($hasVueRouter ? 'router-link' : 'a'),
99
                Button::ATTRIBUTES => [
100
                    $targetAttribute => "'/{$routeBase}/' + model.{$keyAttribute} + '/edit'",
101
                ] + ($hasCan ? [ "v-if" => 'can.edit' ]: []),
102
            ]
103
        )->setContent(
104
            '<i class="fa fa-pencil"></i> Edit',
105
            true,
106
            true
107
        )->getRenderHTML();
108
109
        $buttonDelete = $this->generator->getComposer()->nodeElement(
110
            'Button',
111
            [
112
                Button::TYPE => 'a',
113
                Button::COLOR => Button::COLOR_WARNING,
114
                Button::ATTRIBUTES => [
115
                    'href' => '#',
116
                    '@click.prevent' => 'remove',
117
                ] + ($hasCan ? [ "v-if" => 'can.delete' ]: []),
118
            ]
119
        )->setContent(
120
            '<i class="fa fa-trash"></i> Delete',
121
            true,
122
            true
123
        )->getRenderHTML();
124
125
        $this->generator->templateParameters['buttonCreate'] = $buttonCreate;
126
        $this->generator->templateParameters['buttonEdit'] = $buttonEdit;
127
        $this->generator->templateParameters['buttonDelete'] = $buttonDelete;
128
129
        $this->generator->templateParameters['tableItemFields'] =
130
            array_values(array_map(function (Field $f) {
131
                if ($f->getDatatype()->getBasetype() === 'relationship') {
132
                    $name = $f->getName();
133
                    return "<{$name}-link v-bind=\"{$name}\"></{$name}-link>";
134
                }
135
                return '{{ ' . $f->getName() . ' }}';
136
            }, $this->generator->getTableFields()));
137
    }
138
139
    /**
140
     * Publishes the Vue component dependencies
141
     *
142
     * @return void
143
     */
144
    protected function vuePublish(): void
145
    {
146
        $this->getCollection()->push(
147
            new GeneratedItem(
148
                GeneratedItem::TYPE_FRONTEND,
149
                file_get_contents(__DIR__ . "/Vue/Renderable/RelationshipAutocomplete.vue"),
150
                "Modelarium/RelationshipAutocomplete.vue"
151
            )
152
        );
153
        $this->getCollection()->push(
154
            new GeneratedItem(
155
                GeneratedItem::TYPE_FRONTEND,
156
                file_get_contents(__DIR__ . "/Vue/Renderable/RelationshipSelect.vue"),
157
                "Modelarium/RelationshipSelect.vue"
158
            )
159
        );
160
        // $this->getCollection()->push(
161
        //     new GeneratedItem(
162
        //         GeneratedItem::TYPE_FRONTEND,
163
        //         file_get_contents(__DIR__ . "/Vue/Renderable/RelationshipSelectMultiple.vue"),
164
        //         "Modelarium/RelationshipSelectMultiple.vue"
165
        //     )
166
        // );
167
    }
168
169
    protected function makeVuePaginationComponent(): void
170
    {
171
        // TODO: this is called once per type
172
        $pagination = $this->generator->getComposer()->nodeElement(
173
            'Pagination',
174
            [
175
            ]
176
        );
177
        $html = $pagination->getRenderHTML();
178
        $script = PaginationVue::script();
179
180
        $this->getCollection()->push(
181
            new GeneratedItem(
182
                GeneratedItem::TYPE_FRONTEND,
183
                "<template>\n$html\n</template>\n<script>\n$script\n</script>\n",
184
                "Modelarium/Pagination.vue"
185
            )
186
        );
187
    }
188
189
    /**
190
     * Appends computed item
191
     *
192
     * @param FrameworkVue $vue
193
     * @return void
194
     */
195
    protected function vueCodeLinkItem(FrameworkVue $vue): void
196
    {
197
        $vue->getVueCode()->appendComputed(
198
            'link',
199
            'return "/' . $this->generator->getRouteBase() .
200
                '/" + this.' . $this->generator->getKeyAttribute()
201
        );
202
    }
203
204
    protected function vueCard(FrameworkVue $vue): void
205
    {
206
        $vueCode = $vue->getVueCode();
207
        // set basic data for vue
208
        $extraprops = [
209
            [
210
                'name' => 'id',
211
                'type' => 'String',
212
                'required' => true
213
            ]
214
        ];
215
        $cardFieldNames = array_map(function (Field $f) {
216
            return $f->getName();
217
        }, $this->generator->getCardFields());
218
        $vueCode->setExtraProps($extraprops);
219
        $this->vueCodeLinkItem($vue);
220
221
        foreach ($this->generator->getCardFields() as $f) {
222
            $vueCode->appendExtraProp([
223
                'name' => $f->getName(),
224
                'type' => $vueCode->mapTypeToJs($f->getDatatype()),
225
                'required' => true
226
            ]);
227
        }
228
        $this->makeVue($vue, 'Card', 'viewable', $cardFieldNames);
229
    }
230
231
    protected function vueLink(FrameworkVue $vue): void
232
    {
233
        $vueCode = $vue->getVueCode();
234
        // set basic data for vue
235
        $extraprops = [
236
            [
237
                'name' => 'id',
238
                'type' => 'String',
239
                'required' => true
240
            ]
241
        ];
242
        $vueCode->setExtraProps($extraprops);
243
        $cardFieldNames = array_map(function (Field $f) {
244
            return $f->getName();
245
        }, $this->generator->getCardFields());
246
        foreach ($this->generator->getCardFields() as $f) {
247
            $vueCode->appendExtraProp([
248
                'name' => $f->getName(),
249
                'type' => $vueCode->mapTypeToJs($f->getDatatype()),
250
                'required' => true
251
            ]);
252
        }
253
        foreach ($this->generator->getTitleFields() as $f) {
254
            $vueCode->appendExtraProp([
255
                'name' => $f->getName(),
256
                'type' => $vueCode->mapTypeToJs($f->getDatatype()),
257
                'required' => true
258
            ]);
259
        }
260
261
        $this->vueCodeLinkItem($vue);
262
        $this->makeVue($vue, 'Link', 'viewable', $cardFieldNames);
263
    }
264
265
    public function vueTableItem(FrameworkVue $vue): void
266
    {
267
        $vueCode = $vue->getVueCode();
268
        $tableFieldNames = array_map(function (Field $f) {
269
            return $f->getName();
270
        }, $this->generator->getTableFields());
271
        $extraprops = [
272
            [
273
                'name' => 'id',
274
                'type' => 'String',
275
                'required' => true
276
            ]
277
        ];
278
        $vueCode->setExtraProps($extraprops);
279
        foreach ($this->generator->getTableFields() as $f) {
280
            $vueCode->appendExtraProp([
281
                'name' => $f->getName(),
282
                'type' => $vueCode->mapTypeToJs($f->getDatatype()),
283
                'required' => true
284
            ]);
285
        }
286
        $this->makeVue($vue, 'TableItem', 'viewable', $tableFieldNames);
287
    }
288
289
    public function vueTable(FrameworkVue $vue): void
290
    {
291
        $this->makeVue($vue, 'Table', 'viewable');
292
    }
293
294
    public function vueForm(FrameworkVue $vue): void
295
    {
296
        $this->makeVue(
297
            $vue,
298
            'Form',
299
            'editable',
300
            function (Field $f) {
301
                if (!$f->getExtradata('modelFillable')) {
302
                    return false;
303
                }
304
                return true;
305
            }
306
        );
307
    }
308
309
    /**
310
     * @param FrameworkVue $vue
311
     * @param string $component
312
     * @param string $mode
313
     * @param string[]|callable $restrictFields
314
     * @return void
315
     */
316
    protected function makeVue(FrameworkVue $vue, string $component, string $mode, $restrictFields = null): void
317
    {
318
        $path = $this->generator->getModel()->getName() . '/' .
319
            $this->generator->getModel()->getName() . $component . '.vue';
320
321
        $stub = $this->generator->getStubDir() . "/Vue{$component}.mustache.vue";
322
323
        if ($mode == 'editable') {
324
            $vue->setEditableTemplate(
325
                function (FrameworkVue $vue, array $data, Model $m) use ($stub) {
326
                    return $this->generator->templateCallback($stub, $vue, $data, $m);
327
                }
328
            );
329
330
            $this->getCollection()->push(
331
                new GeneratedItem(
332
                    GeneratedItem::TYPE_FRONTEND,
333
                    $this->generator->getModel()->editable($this->generator->getComposer(), [], $restrictFields),
334
                    $path
335
                )
336
            );
337
        } else {
338
            $vue->setViewableTemplate(
339
                function (FrameworkVue $vue, array $data, Model $m) use ($stub) {
340
                    return $this->generator->templateCallback($stub, $vue, $data, $m);
341
                }
342
            );
343
344
            $this->getCollection()->push(
345
                new GeneratedItem(
346
                    GeneratedItem::TYPE_FRONTEND,
347
                    $this->generator->getModel()->viewable($this->generator->getComposer(), [], $restrictFields),
348
                    $path
349
                )
350
            );
351
        }
352
        $vue->resetVueCode();
353
        $vue->getVueCode()->setFieldModelVariable('model.');
354
    }
355
356
    protected function makeVueIndex(): void
357
    {
358
        $path = $this->generator->getModel()->getName() . '/index.js';
359
        $name = $this->generator->getStudlyName();
360
361
        $items = [
362
            'Card',
363
            'Edit',
364
            'Link',
365
            'List',
366
            'Show',
367
            'Table',
368
        ];
369
370
        $import = array_map(
371
            function ($i) use ($name) {
372
                return "import {$name}$i from './{$name}$i.vue';";
373
            },
374
            $items
375
        );
376
377
        $export = array_map(
378
            function ($i) use ($name) {
379
                return "    {$name}$i,";
380
            },
381
            $items
382
        );
383
384
        $this->getCollection()->push(
385
            new GeneratedItem(
386
                GeneratedItem::TYPE_FRONTEND,
387
                implode("\n", $import) . "\n" .
388
                "export default {\n" .
389
                implode("\n", $export) . "\n};\n",
390
                $path
391
            )
392
        );
393
    }
394
395
    protected function makeVueRoutes(): void
396
    {
397
        $path = $this->generator->getModel()->getName() . '/routes.js';
398
399
        $this->getCollection()->push(
400
            new GeneratedItem(
401
                GeneratedItem::TYPE_FRONTEND,
402
                $this->generator->templateFile(
403
                    $this->generator->getStubDir() . "/routes.mustache.js",
404
                    [
405
                        'routeName' => $this->generator->getRouteBase(),
406
                        'keyAttribute' => $this->generator->getKeyAttribute()
407
                    ]
408
                ),
409
                $path
410
            )
411
        );
412
    }
413
}
414