Passed
Push — master ( 7c0677...41a7c0 )
by Bruno
09:24
created

FrontendGenerator::createMutation()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 2
Code Lines 0

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 0
c 0
b 0
f 0
dl 0
loc 2
rs 10
cc 1
nc 1
nop 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\Blade\Framework as FrameworkBlade;
11
use Formularium\Frontend\HTML\Element\Button;
12
use Formularium\Frontend\HTML\Element\Table;
13
use Formularium\Frontend\Vue\Framework as FrameworkVue;
14
use Formularium\HTMLNode;
15
use Formularium\Renderable;
16
use Modelarium\GeneratedCollection;
17
use Modelarium\GeneratedItem;
18
use Modelarium\GeneratorInterface;
19
use Modelarium\GeneratorNameTrait;
20
21
use function Safe\file_get_contents;
22
use function Safe\json_encode;
23
24
class FrontendGenerator implements GeneratorInterface
25
{
26
    use GeneratorNameTrait;
27
28
    /**
29
     * @var FrameworkComposer
30
     */
31
    protected $composer = null;
32
33
    /**
34
     * @var Model
35
     */
36
    protected $model = null;
37
38
    /**
39
     * @var GeneratedCollection
40
     */
41
    protected $collection;
42
43
    /**
44
     *
45
     * @var string
46
     */
47
    protected $stubDir = __DIR__ . '/stubs';
48
49
    /**
50
     * String substitution
51
     *
52
     * @var array
53
     */
54
    protected $templateParameters = [];
55
56
    /**
57
     * Card fields
58
     *
59
     * @var Field[]
60
     */
61
    protected $cardFields = [];
62
63
    /**
64
     * Table fields
65
     *
66
     * @var Field[]
67
     */
68
    protected $tableFields = [];
69
70
    public function __construct(FrameworkComposer $composer, Model $model)
71
    {
72
        $this->composer = $composer;
73
        $this->model = $model;
74
        $this->setBaseName($model->getName());
75
        $this->buildTemplateParameters();
76
    }
77
78
    public function generate(): GeneratedCollection
79
    {
80
        $this->collection = new GeneratedCollection();
81
82
        /**
83
         * @var FrameworkVue $vue
84
         */
85
        $vue = $this->composer->getByName('Vue');
86
        // $blade = FrameworkComposer::getByName('Blade');
87
88
        if ($vue !== null) {
89
            $cardFieldNames = array_map(function (Field $f) {
90
                return $f->getName();
91
            }, $this->cardFields);
92
93
            $vue->setFieldModelVariable('model.');
94
            $vue->setExtraProps([
0 ignored issues
show
Bug introduced by
The method setExtraProps() does not exist on Formularium\Frontend\Vue\Framework. ( Ignorable by Annotation )

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

94
            $vue->/** @scrutinizer ignore-call */ 
95
                  setExtraProps([

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...
95
                [
96
                    'name' => 'id',
97
                    'type' => 'String',
98
                    'required' => true
99
                ]
100
            ]);
101
            $this->makeJSModel();
102
            $this->makeVue($vue, 'Card', 'viewable', $cardFieldNames);
103
            $this->makeVue($vue, 'List', 'viewable');
104
            $this->makeVue($vue, 'Table', 'viewable');
105
            $this->makeVue($vue, 'TableItem', 'viewable', $cardFieldNames);
106
            $this->makeVue($vue, 'Show', 'viewable');
107
            $this->makeVue($vue, 'Edit', 'editable');
108
            $this->makeVue($vue, 'Form', 'editable');
109
            $this->makeVueRoutes();
110
            $this->makeVueIndex();
111
        }
112
113
        $this->makeGraphql();
114
115
        return $this->collection;
116
    }
117
118
    protected function buildTemplateParameters(): void
119
    {
120
        $this->cardFields = $this->model->filterField(
121
            function (Field $field) {
122
                return $field->getRenderable('card', false);
123
            }
124
        );
125
        $this->tableFields = $this->model->filterField(
126
            function (Field $field) {
127
                return $field->getRenderable('table', false);
128
            }
129
        );
130
        if (!$this->tableFields) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->tableFields of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
131
            $this->tableFields = $this->cardFields;
132
        }
133
134
        $buttonCreate = $this->composer->nodeElement(
135
            'Button',
136
            [
137
                Button::TYPE => ($this->composer->getByName('Vue') ? 'router-link' : 'a'),
138
                Button::ATTRIBUTES => [':to' => "'/' + type + '/edit'"],
0 ignored issues
show
Bug introduced by
The constant Formularium\Frontend\HTM...ment\Button::ATTRIBUTES was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
139
            ]
140
        )->setContent(
141
            '<i class="fa fa-plus"></i>Add new',
142
            true,
143
            true
144
        )->getRenderHTML();
145
146
        /*
147
         * table
148
         */
149
        $table = $this->composer->nodeElement(
150
            'Table',
151
            [
152
                Table::ROW_NAMES => array_map(
153
                    function (Field $field) {
154
                        return $field->getRenderable(Renderable::LABEL, $field->getName());
155
                    },
156
                    $this->cardFields
157
                ),
158
            ]
159
        );
160
        /**
161
         * @var HTMLNode $tbody
162
         */
163
        $tbody = $table->get('tbody')[0];
164
        $tbody->setContent(
165
            '<' . $this->studlyName . 'TableItem v-for="l in list" :key="l.id" v-bind="l"></' . $this->studlyName . 'TableItem>',
166
            true,
167
            true
168
        );
169
        $titleFields = $this->model->filterField(
170
            function (Field $field) {
171
                return $field->getRenderable('title', false);
172
            }
173
        );
174
175
        $this->templateParameters = [
176
            'buttonSubmit' => $this->composer->element(
177
                'Button',
178
                [
179
                    Button::TYPE => 'submit',
180
                    Element::LABEL => 'Submit'
181
                ]
182
            ),
183
            'buttonCreate' => $buttonCreate,
184
            'tablelist' => $table->getRenderHTML(),
185
            'tableItemFields' => array_keys(array_map(function (Field $f) {
186
                return $f->getName();
187
            }, $this->tableFields)),
188
            'titleField' => array_key_first($titleFields) ?: 'id'
189
        ];
190
    }
191
192
    public function templateCallback(string $stub, FrameworkVue $vue, array $data, Model $m): string
0 ignored issues
show
Unused Code introduced by
The parameter $vue 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

192
    public function templateCallback(string $stub, /** @scrutinizer ignore-unused */ FrameworkVue $vue, array $data, Model $m): string

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...
Unused Code introduced by
The parameter $m 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

192
    public function templateCallback(string $stub, FrameworkVue $vue, array $data, /** @scrutinizer ignore-unused */ Model $m): string

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...
193
    {
194
        $x = $this->templateFile(
195
            $stub,
196
            array_merge(
197
                $this->templateParameters,
198
                $data
199
            )
200
        );
201
        return $x;
202
    }
203
204
    protected function makeVue(FrameworkVue $vue, string $component, string $mode, array $restrictFields = null): void
205
    {
206
        $path = $this->model->getName() . '/' .
207
            $this->model->getName() . $component . '.vue';
208
209
        $stub = $this->stubDir . "/Vue{$component}.mustache.vue";
210
211
        if ($mode == 'editable') {
212
            $vue->setEditableTemplate(
213
                function (FrameworkVue $vue, array $data, Model $m) use ($stub) {
214
                    return $this->templateCallback($stub, $vue, $data, $m);
215
                }
216
            );
217
218
            $this->collection->push(
219
                new GeneratedItem(
220
                    GeneratedItem::TYPE_FRONTEND,
221
                    $this->model->editable($this->composer, [], $restrictFields),
0 ignored issues
show
Unused Code introduced by
The call to Formularium\Model::editable() has too many arguments starting with $restrictFields. ( Ignorable by Annotation )

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

221
                    $this->model->/** @scrutinizer ignore-call */ 
222
                                  editable($this->composer, [], $restrictFields),

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...
222
                    $path
223
                )
224
            );
225
        } else {
226
            $vue->setViewableTemplate(
227
                function (FrameworkVue $vue, array $data, Model $m) use ($stub) {
228
                    return $this->templateCallback($stub, $vue, $data, $m);
229
                }
230
            );
231
232
            $this->collection->push(
233
                new GeneratedItem(
234
                    GeneratedItem::TYPE_FRONTEND,
235
                    $this->model->viewable($this->composer, [], $restrictFields),
0 ignored issues
show
Unused Code introduced by
The call to Formularium\Model::viewable() has too many arguments starting with $restrictFields. ( Ignorable by Annotation )

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

235
                    $this->model->/** @scrutinizer ignore-call */ 
236
                                  viewable($this->composer, [], $restrictFields),

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...
236
                    $path
237
                )
238
            );
239
        }
240
    }
241
242
    protected function getFilters(): array
243
    {
244
        $filters = [];
245
        return $filters;
246
    }
247
248
    protected function makeGraphql(): void
249
    {
250
        $cardFieldNames = array_map(
251
            function (Field $field) {
252
                return $field->getName();
253
            },
254
            $this->cardFields
255
        );
256
        $cardFieldParameters = implode("\n", $cardFieldNames);
257
258
        $listQuery = <<<EOF
259
query (\$page: Int!) {
260
    {$this->lowerNamePlural}(page: \$page) {
261
        data {
262
            id
263
            $cardFieldParameters
264
        }
265
      
266
        paginatorInfo {
267
            currentPage
268
            perPage
269
            total
270
            lastPage
271
        }
272
    }
273
}
274
EOF;
275
276
        $this->collection->push(
277
            new GeneratedItem(
278
                GeneratedItem::TYPE_FRONTEND,
279
                $listQuery,
280
                $this->model->getName() . '/queryList.graphql'
281
            )
282
        );
283
284
        $graphqlQuery = $this->model->toGraphqlQuery();
0 ignored issues
show
Bug introduced by
The method toGraphqlQuery() does not exist on Formularium\Model. ( Ignorable by Annotation )

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

284
        /** @scrutinizer ignore-call */ 
285
        $graphqlQuery = $this->model->toGraphqlQuery();

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...
285
        $itemQuery = <<<EOF
286
query (\$id: ID!) {
287
    {$this->lowerName}(id: \$id) {
288
        id
289
        $graphqlQuery
290
    }
291
}
292
EOF;
293
294
        $this->collection->push(
295
            new GeneratedItem(
296
                GeneratedItem::TYPE_FRONTEND,
297
                $itemQuery,
298
                $this->model->getName() . '/queryItem.graphql'
299
            )
300
        );
301
302
        $createMutation = <<<EOF
303
mutation create(\${$this->lowerName}: Create{$this->studlyName}Input!) {
304
    create{$this->studlyName}(input: \${$this->lowerName}) {
305
        id
306
    }
307
}
308
EOF;
309
        $this->collection->push(
310
            new GeneratedItem(
311
                GeneratedItem::TYPE_FRONTEND,
312
                $createMutation,
313
                $this->model->getName() . '/mutationCreate.graphql'
314
            )
315
        );
316
    }
317
318
    protected function makeVueIndex(): void
319
    {
320
        $path = $this->model->getName() . '/index.js';
321
        $name = $this->studlyName;
322
323
        $items = [
324
            'Card',
325
            'Edit',
326
            'List',
327
            'Show',
328
            'Table',
329
        ];
330
331
        $import = array_map(
332
            function ($i) use ($name) {
333
                return "import {$name}$i from './{$name}$i.vue';";
334
            },
335
            $items
336
        );
337
338
        $export = array_map(
339
            function ($i) use ($name) {
340
                return "    {$name}$i,\n";
341
            },
342
            $items
343
        );
344
345
        $this->collection->push(
346
            new GeneratedItem(
347
                GeneratedItem::TYPE_FRONTEND,
348
                implode("\n", $import) . "\n" .
349
                "export {\n" .
350
                implode("\n", $export) . "\n};\n",
351
                $path
352
            )
353
        );
354
    }
355
356
    protected function makeVueRoutes(): void
357
    {
358
        $path = $this->model->getName() . '/routes.js';
359
360
        $this->collection->push(
361
            new GeneratedItem(
362
                GeneratedItem::TYPE_FRONTEND,
363
                $this->templateFile($this->stubDir . "/routes.mustache.js"),
364
                $path
365
            )
366
        );
367
    }
368
369
    protected function makeJSModel(): void
370
    {
371
        $path = $this->model->getName() . '/model.js';
372
        $modelValues = $this->model->getDefault();
373
        $modelValues['id'] = 0;
374
        $modelJS = 'const model = ' . json_encode($modelValues) .
375
            ";\n\nexport default model;\n";
376
        
377
        $this->collection->push(
378
            new GeneratedItem(
379
                GeneratedItem::TYPE_FRONTEND,
380
                $modelJS,
381
                $path
382
            )
383
        );
384
    }
385
}
386