Passed
Push — master ( 7b13b9...d7718c )
by Bruno
06:23
created

Framework::counter()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 1
Bugs 1 Features 0
Metric Value
cc 1
eloc 2
c 1
b 1
f 0
nc 1
nop 0
dl 0
loc 4
ccs 0
cts 4
cp 0
crap 2
rs 10
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\HTMLElement;
9
use Formularium\Model;
10
11
class Framework extends \Formularium\Framework
12
{
13
    const VUE_MODE_SINGLE_FILE = 'VUE_MODE_SINGLE_FILE';
14
    const VUE_MODE_EMBEDDED = 'VUE_MODE_EMBEDDED';
15
16
    const VUE_PROP = 'VUE_PROP';
17
18
19
    /**
20
     * @var string
21
     */
22
    protected $mode = self::VUE_MODE_EMBEDDED;
23
24
    /**
25
    * The tag used as container for fields in viewable()
26
    *
27
    * @var string
28
    */
29
    protected $viewableContainerTag = 'div';
30
31
    /**
32
     * The tag used as container for fields in editable()
33
     *
34
     * @var string
35
     */
36
    protected $editableContainerTag = 'div';
37
38
    /**
39
     * The viewable template.
40
     *
41
     * The following variables are replaced:
42
     *
43
     * {{form}}
44
     * {{jsonData}}
45
     * {{containerTag}}
46
     *
47
     * @var string
48
     */
49
    protected $viewableTemplate = '';
50
51
    /**
52
     *
53
     *
54
     * @var string
55
     */
56
    protected $editableTemplate = '';
57
58
    public function __construct(string $name = 'Vue')
59
    {
60
        parent::__construct($name);
61
    }
62
63
    /**
64
     * Static counter to generate unique ids.
65
     *
66
     * @return integer
67
     */
68
    public static function counter(): int
69
    {
70
        static $counter = 0;
71
        return $counter++;
72
    }
73
74
    /**
75
     * Get the tag used as container for fields in viewable()
76
     *
77
     * @return  string
78
     */
79
    public function getViewableContainerTag(): string
80
    {
81
        return $this->viewableContainerTag;
82
    }
83
84
    /**
85
     * Set the tag used as container for fields in viewable()
86
     *
87
     * @param  string  $viewableContainerTag  The tag used as container for fields in viewable()
88
     *
89
     * @return  self
90
     */
91
    public function setViewableContainerTag(string $viewableContainerTag): Framework
92
    {
93
        $this->viewableContainerTag = $viewableContainerTag;
94
        return $this;
95
    }
96
97
    public function getEditableContainerTag(): string
98
    {
99
        return $this->editableContainerTag;
100
    }
101
    
102
    /**
103
     * @param string $tag
104
     * @return self
105
     */
106
    public function setEditableContainerTag(string $tag): Framework
107
    {
108
        $this->editableContainerTag = $tag;
109
        return $this;
110
    }
111
112
    /**
113
     * Get the value of editableTemplate
114
     */
115
    public function getEditableTemplate(): string
116
    {
117
        return $this->editableTemplate;
118
    }
119
120
    /**
121
     * Set the value of editableTemplate
122
     *
123
     * @return self
124
     */
125
    public function setEditableTemplate(string $editableTemplate): Framework
126
    {
127
        $this->editableTemplate = $editableTemplate;
128
129
        return $this;
130
    }
131
    
132
    /**
133
     * Get {{containerTag}}
134
     *
135
     * @return  string
136
     */
137
    public function getViewableTemplate()
138
    {
139
        return $this->viewableTemplate;
140
    }
141
142
    /**
143
     * Set {{containerTag}}
144
     *
145
     * @param  string  $viewableTemplate  {{containerTag}}
146
     *
147
     * @return  self
148
     */
149
    public function setViewableTemplate(string $viewableTemplate)
150
    {
151
        $this->viewableTemplate = $viewableTemplate;
152
153
        return $this;
154
    }
155
    
156
    /**
157
     * Sets the vue render mode, single file component or embedded
158
     *
159
     * @param string $mode self::VUE_MODE_EMBEDDED or self::VUE_MODE_SINGLE_FILE
160
     * @return Framework
161
     */
162
    public function setMode(string $mode): Framework
163
    {
164
        $this->mode = $mode;
165
        return $this;
166
    }
167
168
    /**
169
     * Get the value of mode
170
     *
171
     * @return string
172
     */
173
    public function getMode(): string
174
    {
175
        return $this->mode;
176
    }
177
178
    public function htmlHead(HTMLElement &$head)
179
    {
180
        $head->prependContent(
181
            HTMLElement::factory('script', ['src' => "https://cdn.jsdelivr.net/npm/vue/dist/vue.js"])
182
        );
183
    }
184
185
    protected function mapType(Datatype $type): string
186
    {
187
        if ($type instanceof Datatype_number) {
188
            return 'Number';
189
        } elseif ($type instanceof Datatype_bool) {
190
            return 'Boolean';
191
        }
192
        return 'String';
193
    }
194
195
    protected function props(Model $m): array
196
    {
197
        $props = [];
198
        foreach ($m->getFields() as $field) {
199
            if ($field->getExtension(self::VUE_PROP, false)) { // TODO
200
                $p = [
201
                    'type' => $this->mapType($field->getDatatype()),
202
                ];
203
                if ($field->getExtension(Datatype::REQUIRED)) {
0 ignored issues
show
Bug introduced by
The call to Formularium\Field::getExtension() has too few arguments starting with default. ( Ignorable by Annotation )

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

203
                if ($field->/** @scrutinizer ignore-call */ getExtension(Datatype::REQUIRED)) {

This check compares calls to functions or methods with their respective definitions. If the call has less 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...
204
                    $p['required'] = true;
205
                }
206
                $props[$field->getName()] = $p;
207
            }
208
        }
209
        return $props;
210
    }
211
212
    public function viewableCompose(Model $m, array $elements, string $previousCompose): string
213
    {
214
        $data = $m->getDefault(); // TODO: load data
215
        
216
        $viewableForm = join('', $elements);
217
        $jsonData = json_encode($data);
218
        $templateData = [
219
            'containerTag' => $this->getViewableContainerTag(),
220
            'form' => $viewableForm,
221
            'jsonData' => $jsonData,
222
            'props' => $this->props($m)
223
        ];
224
225
        if ($this->viewableTemplate) {
226
            return $this->fillTemplate(
227
                $this->viewableTemplate,
228
                $templateData,
229
                $m
230
            );
231
        } elseif ($this->mode === self::VUE_MODE_SINGLE_FILE) {
232
            $viewableTemplate = <<<EOF
0 ignored issues
show
Unused Code introduced by
The assignment to $viewableTemplate is dead and can be removed.
Loading history...
233
<template>
234
<{{containerTag}}>
235
    {{form}}
236
</{{containerTag}}>
237
</template>
238
<script>
239
module.exports = {
240
    data: function () {
241
        return {{jsonData}};
242
    }
243
};
244
</script>
245
<style>
246
</style>
247
EOF;
248
            return $this->fillTemplate(
249
                $this->viewableTemplate,
250
                $templateData,
251
                $m
252
            );
253
        } else {
254
            $id = 'vueapp' . static::counter();
255
            $t = new HTMLElement(self::getViewableContainerTag(), ['id' => $id], $viewableForm, true);
0 ignored issues
show
Bug Best Practice introduced by
The method Formularium\Frontend\Vue...tViewableContainerTag() is not static, but was called statically. ( Ignorable by Annotation )

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

255
            $t = new HTMLElement(self::/** @scrutinizer ignore-call */ getViewableContainerTag(), ['id' => $id], $viewableForm, true);
Loading history...
256
            $script = <<<EOF
257
var app = new Vue({
258
    el: '#$id',
259
    data: $jsonData
260
});
261
EOF;
262
            $s = new HTMLElement('script', [], $script, true);
263
            return HTMLElement::factory('div', [], [$t, $s])->getRenderHTML();
264
        }
265
    }
266
267
    public function editableCompose(Model $m, array $elements, string $previousCompose): string
268
    {
269
        $data = $m->getDefault(); // TODO: load data
270
        $editableContainerTag = $this->getEditableContainerTag();
271
        $editableForm = join('', $elements);
272
        $jsonData = json_encode($data);
273
        $templateData = [
274
            'containerTag' => $this->getViewableContainerTag(),
275
            'form' => $editableForm,
276
            'jsonData' => $jsonData,
277
            'props' => $this->props($m)
278
        ];
279
280
        if ($this->viewableTemplate) {
281
            return $this->fillTemplate(
282
                $this->editableTemplate,
283
                $templateData,
284
                $m
285
            );
286
        } elseif ($this->mode === self::VUE_MODE_SINGLE_FILE) {
287
            $editableTemplate = <<<EOF
288
<template>
289
<{{containerTag}}>
290
    {{form}}
291
</{{containerTag}}>
292
</template>
293
<script>
294
module.exports = {
295
    data: function () {
296
        return {{jsonData}};
297
    },
298
    methods: {
299
        changedFile(name, event) {
300
            console.log(name, event);
301
        }
302
    }
303
};
304
</script>
305
<style>
306
</style>
307
EOF;
308
            return $this->fillTemplate(
309
                $editableTemplate,
310
                $templateData,
311
                $m
312
            );
313
        } else {
314
            $id = 'vueapp' . static::counter();
315
            $t = new HTMLElement($editableContainerTag, ['id' => $id], $editableForm, true);
316
            $script = <<<EOF
317
var app = new Vue({
318
    el: '#$id',
319
    data: $jsonData,
320
    methods: {
321
        changedFile(name, event) {
322
            console.log(name, event);
323
        }
324
    }
325
});
326
EOF;
327
            $s = new HTMLElement('script', [], $script, true);
328
            return HTMLElement::factory('div', [], [$t, $s])->getRenderHTML();
329
        }
330
    }
331
332
    protected function fillTemplate(string $template, array $data, Model $m): string
333
    {
334
        $template = str_replace(
335
            '{{form}}',
336
            $data['form'],
337
            $template
338
        );
339
        $template = str_replace(
340
            '{{modelName}}',
341
            $m->getName(),
342
            $template
343
        );
344
        $template = str_replace(
345
            '{{modelNameLower}}',
346
            mb_strtolower($m->getName()),
347
            $template
348
        );
349
        $template = str_replace(
350
            '{{jsonData}}',
351
            $data['jsonData'],
352
            $template
353
        );
354
        $template = str_replace(
355
            '{{containerTag}}',
356
            $data['containerTag'],
357
            $template
358
        );
359
        return $template;
360
    }
361
}
362