Passed
Push — master ( 03056a...8132a2 )
by Bruno
05:28
created

VueCode::getExtraProps()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 1
c 1
b 0
f 0
dl 0
loc 3
rs 10
cc 1
nc 1
nop 0
1
<?php declare(strict_types=1);
2
3
namespace Formularium\Frontend\Vue;
4
5
use Formularium\Datatype;
6
use Formularium\Datatype\Datatype_bool;
7
use Formularium\Datatype\Datatype_number;
8
use Formularium\Exception\Exception;
9
use Formularium\Field;
10
use Formularium\HTMLNode;
11
use Formularium\Model;
12
13
class VueCode
14
{
15
16
    /**
17
     * Appended to the field variable names to handle models stored in an object field.
18
     *
19
     * Allows you to declare the model like this:
20
     *
21
     * data() {
22
     *   return {
23
     *       model: model,
24
     *   };
25
     * },
26
     *
27
     * @var string
28
     */
29
    protected $fieldModelVariable = '';
30
31
    /**
32
     * Extra props.
33
     *
34
     * @var array
35
     */
36
    protected $extraProps = [];
37
38
    /**
39
     * extra data fields
40
     *
41
     * @var string[]
42
     */
43
    protected $extraData = [];
44
45
    /**
46
     * The list of imports to add: import 'key' from 'value'
47
     *
48
     * @var string[]
49
     */
50
    protected $imports = [];
51
    
52
    /**
53
     * @var string[]
54
     */
55
    protected $computed = [];
56
57
    /**
58
     * @var string[]
59
     */
60
    protected $methods = [];
61
62
    /**
63
     * @var string[]
64
     */
65
    protected $other = [];
66
67
    /**
68
     * @param string $name
69
     * @param string $code
70
     * @return self
71
     */
72
    public function appendMethod($name, $code): self
73
    {
74
        $this->methods[$name] = $code;
75
        return $this;
76
    }
77
78
    /**
79
     * @param string $name
80
     * @param string $code
81
     * @return self
82
     */
83
    public function appendOther($name, $code): self
84
    {
85
        $this->other[$name] = $code;
86
        return $this;
87
    }
88
89
    /**
90
     * @return array
91
     */
92
    public function getExtraProps(): array
93
    {
94
        return $this->extraProps;
95
    }
96
97
    /**
98
     *
99
     * @param array $extraProps
100
     *
101
     * @return  self
102
     */
103
    public function setExtraProps(array $extraProps): self
104
    {
105
        $this->extraProps = $extraProps;
106
107
        return $this;
108
    }
109
110
    /**
111
     *
112
     * @param array $extra Array of props. 'name' and 'type' keys are required for each element.
113
     *
114
     * @return  self
115
     */
116
    public function appendExtraProp(array $extra): self
117
    {
118
        $this->extraProps[] = $extra;
119
120
        return $this;
121
    }
122
123
    /**
124
     * Appends to the `data` field.
125
     *
126
     * @param string $name
127
     * @param string $value
128
     * @return self
129
     */
130
    public function appendExtraData(string $name, string $value): self
131
    {
132
        $this->extraData[$name] = $value;
133
        return $this;
134
    }
135
136
    /**
137
     * The list of imports to add: import 'key' from 'value'
138
     *
139
     * @param string $key
140
     * @param string $value
141
     * @return self
142
     */
143
    public function appendImport(string $key, string $value): self
144
    {
145
        $this->imports[$key] = $value;
146
147
        return $this;
148
    }
149
150
    /**
151
     * The list of computed to add: $ke() => $code
152
     *
153
     * @param string $key
154
     * @param string $code
155
     * @return self
156
     */
157
    public function appendComputed(string $key, string $code): self
158
    {
159
        $this->computed[$key] = $code;
160
161
        return $this;
162
    }
163
164
    /**
165
     * Get appended to the field variable names to handle models stored in an object field.
166
     *
167
     * @return  string
168
     */
169
    public function getFieldModelVariable(): string
170
    {
171
        return $this->fieldModelVariable;
172
    }
173
174
    /**
175
     * Set appended to the field variable names to handle models stored in an object field.
176
     *
177
     * @param  string  $fieldModelVariable  Appended to the field variable names to handle models stored in an object field.
178
     *
179
     * @return  self
180
     */
181
    public function setFieldModelVariable(string $fieldModelVariable): self
182
    {
183
        $this->fieldModelVariable = $fieldModelVariable;
184
185
        return $this;
186
    }
187
188
    /**
189
     * Converts a Datatype to a JS type
190
     *
191
     * @param Datatype $type
192
     * @return string
193
     */
194
    public function mapType(Datatype $type): string
195
    {
196
        if ($type instanceof Datatype_number) {
197
            return 'Number';
198
        } elseif ($type instanceof Datatype_bool) {
199
            return 'Boolean';
200
        }
201
        return 'String';
202
    }
203
204
    public function props(Model $m): array
205
    {
206
        $props = [];
207
        foreach ($m->getFields() as $field) {
208
            /**
209
             * @var Field $field
210
             */
211
            if ($field->getRenderable(Framework::VUE_PROP, false)) {
212
                $p = [
213
                    'name' => $field->getName(),
214
                    'type' => $this->mapType($field->getDatatype()),
215
                ];
216
                if ($field->getRenderable(Datatype::REQUIRED, false)) {
217
                    $p['required'] = true;
218
                }
219
                $props[] = $p;
220
            }
221
        }
222
        foreach ($this->extraProps as $p) {
223
            if (!array_key_exists('name', $p)) {
224
                throw new Exception('Missing prop name');
225
            }
226
            $props[] = $p;
227
        }
228
        
229
        return $props;
230
    }
231
232
    /**
233
     * Generates valid JS code for the props.
234
     *
235
     * @param array $props
236
     * @return string
237
     */
238
    protected function serializeProps(array $props): string
239
    {
240
        $s = array_map(function ($p) {
241
            return "'{$p['name']}': { 'type': {$p['type']}" . ($p['required'] ?? false ? ", 'required': true" : '') . " } ";
242
        }, $props);
243
        return "{\n        " . implode(",\n        ", $s) . "\n    }\n";
244
    }
245
246
    /**
247
     * Generates template data for rendering
248
     *
249
     * @param Model $m
250
     * @param HTMLNode[] $elements $elements
251
     * @return array
252
     */
253
    protected function getTemplateData(Model $m, array $elements): array
0 ignored issues
show
Unused Code introduced by
The parameter $elements 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

253
    protected function getTemplateData(Model $m, /** @scrutinizer ignore-unused */ array $elements): array

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...
254
    {
255
        $data = array_merge($m->getDefault(), $m->getData());
256
        $jsonData = json_encode($data);
257
        $props = $this->props($m);
258
        $propsBind = array_map(
259
            function ($p) {
260
                return 'v-bind:' . $p . '="model.' . $p . '"';
261
            },
262
            array_keys($props)
263
        );
264
        $templateData = [
265
            'jsonData' => $jsonData,
266
            'propsCode' => $this->serializeProps($props),
267
            'propsBind' => implode(' ', $propsBind),
268
            'imports' => implode(
269
                "\n",
270
                array_map(function ($key, $value) {
271
                    // TODO: array
272
                    return "import $key from \"$value\";";
273
                }, array_keys($this->imports), $this->imports)
274
            ),
275
            'computedCode' => implode(
276
                "\n",
277
                array_map(function ($key, $value) {
278
                    // TODO: array
279
                    return "$key() { $value },";
280
                }, array_keys($this->computed), $this->computed)
281
            ),
282
            'otherData' => implode(
283
                "\n",
284
                array_map(function ($key, $value) {
285
                    return "$key: " . json_encode($value) . ",\n";
286
                }, array_keys($this->other), $this->other)
287
            ),
288
            'methodsCode' => '{}', // TODO
289
            'extraData' => implode(
290
                "\n",
291
                array_map(function ($key, $value) {
292
                    return "  $key: $value,";
293
                }, array_keys($this->extraData), $this->extraData)
294
            )
295
        ];
296
297
        return $templateData;
298
    }
299
300
    protected function fillTemplate(string $template, array $data, Model $m): string
301
    {
302
        foreach ($data as $name => $value) {
303
            $template = str_replace(
304
                '{{' . $name . '}}',
305
                $value,
306
                $template
307
            );
308
        }
309
310
        $template = str_replace(
311
            '{{modelName}}',
312
            $m->getName(),
313
            $template
314
        );
315
        $template = str_replace(
316
            '{{modelNameLower}}',
317
            mb_strtolower($m->getName()),
318
            $template
319
        );
320
        return $template;
321
    }
322
323
    /**
324
     * Generates the javascript code.
325
     *
326
     * @param Model $m
327
     * @param HTMLNode[] $elements
328
     * @return string
329
     */
330
    public function toScript(Model $m, array $elements)
331
    {
332
        $templateData = $this->getTemplateData($m, $elements);
333
334
        $viewableTemplate = <<<EOF
335
{{imports}}
336
337
export default {
338
    {{otherData}}
339
    data: function () {
340
        return {{jsonData}};
341
    },
342
    computed: { {{computedCode}} },
343
    props: {{propsCode}},
344
    methods: {{methodsCode}}
345
};
346
EOF;
347
            
348
        return $this->fillTemplate(
349
            $viewableTemplate,
350
            $templateData,
351
            $m
352
        );
353
    }
354
355
    /**
356
     * Generates the javascript code.
357
     *
358
     * @param Model $m
359
     * @param HTMLNode[] $elements
360
     * @return string
361
     */
362
    public function toVariable(Model $m, array $elements)
363
    {
364
        $templateData = $this->getTemplateData($m, $elements);
365
366
        $viewableTemplate = <<<EOF
367
    {{otherData}}
368
    data() {
369
        return {{jsonData}};
370
    },
371
    computed: { {{computedCode}} },
372
    props: {{propsCode}},
373
    methods: {{methodsCode}}
374
EOF;
375
            
376
        return $this->fillTemplate(
377
            $viewableTemplate,
378
            $templateData,
379
            $m
380
        );
381
    }
382
383
    /**
384
     * Get the value of other
385
     * @return array
386
     */
387
    public function &getOther(): array
388
    {
389
        return $this->other;
390
    }
391
}
392