Completed
Push — master ( ba35a7...9a9f3c )
by Denis
01:31
created

AbstractElement::generateAttributes()   B

Complexity

Conditions 6
Paths 4

Size

Total Lines 15
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 6
eloc 9
nc 4
nop 0
dl 0
loc 15
rs 8.8571
c 0
b 0
f 0
1
<?php
2
3
namespace Ngtfkx\Laradeck\FormBuilder\Elements;
4
5
6
use Illuminate\Support\Collection;
7
8
abstract class AbstractElement
9
{
10
    /**
11
     * @var string $id Значение атрибута ID элемента
12
     */
13
    protected $id;
14
15
    /**
16
     * @var string $name Значение атрибута name элемента
17
     */
18
    protected $name;
19
20
    /**
21
     * @var Collection|iterable $classes Коллекция классом элемента
22
     */
23
    protected $classes;
24
25
    /**
26
     * @var Collection|iterable $attributes Коллекция дополнительных атрибутов элемента
27
     */
28
    protected $attributes;
29
30
    /**
31
     * @var Collection|iterable $styles Коллекция inline-стилей элемента
32
     */
33
    protected $styles;
34
35
    /**
36
     * @var string $value Значение элемента
37
     */
38
    protected $value;
39
40
    /**
41
     * @var string $tag Тег элемента
42
     */
43
    protected $tag;
44
45
    /**
46
     * @var Collection|iterable $parts Набор атрибутов для генерации html-кода элементов
47
     */
48
    protected $parts;
49
50
    /**
51
     * @var bool $needClose Признак, что надо закрывать тег элемента
52
     */
53
    protected $needClose = false;
54
55
    /**
56
     * @return void
57
     */
58
    abstract public function tag();
59
60
    public function __construct()
61
    {
62
        $this->classes = new Collection();
63
64
        $this->attributes = new Collection();
65
66
        $this->styles = new Collection();
67
68
        $this->parts = new Collection();
69
70
        $this->tag();
71
    }
72
73
    /**
74
     * Сеттер значения элемента
75
     *
76
     * @param string|null $value
77
     * @return AbstractElement
78
     */
79
    public function value(?string $value): self
80
    {
81
        $this->value = $value;
82
83
        return $this;
84
    }
85
86
    /**
87
     * Сеттер атрибута id элемента
88
     *
89
     * @param string|null $value
90
     * @return AbstractElement
91
     */
92
    public function id(?string $value): self
93
    {
94
        $this->id = $value;
95
96
        return $this;
97
    }
98
99
    /**
100
     * Сеттер атрибута name элемента
101
     *
102
     * @param string|null $value
103
     * @return AbstractElement
104
     */
105
    public function name(?string $value): self
106
    {
107
        $this->name = $value;
108
109
        return $this;
110
    }
111
112
    /**
113
     * Добавить элементу один или несколько классов
114
     *
115
     * @param array ...$classes
116
     * @return AbstractElement
117
     */
118
    public function class(...$classes): self
0 ignored issues
show
Coding Style introduced by
Possible parse error: non-abstract method defined as abstract
Loading history...
Coding Style introduced by
It is generally advisable to only define one property per statement.

Only declaring a single property per statement allows you to later on add doc comments more easily.

It is also recommended by PSR2, so it is a common style that many people expect.

Loading history...
119
    {
120
        $this->classes($classes);
0 ignored issues
show
Documentation introduced by
$classes is of type array<integer,array>, but the function expects a object<Ngtfkx\Laradeck\F...lder\Elements\iterable>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
121
122
        return $this;
0 ignored issues
show
Coding Style introduced by
The visibility should be declared for property $this.

The PSR-2 coding standard requires that all properties in a class have their visibility explicitly declared. If you declare a property using

class A {
    var $property;
}

the property is implicitly global.

To learn more about the PSR-2, please see the PHP-FIG site on the PSR-2.

Loading history...
123
    }
124
125
    /**
126
     *  Добавить элементу один или несколько классов из массива
127
     *
128
     * @param iterable $classes
129
     * @return AbstractElement
130
     */
131
    public function classes(iterable $classes): self
132
    {
133
        foreach ($classes as $class) {
134
            if (!$this->classes->contains($class)) {
135
                $this->classes->push($class);
136
            }
137
        }
138
139
        return $this;
140
    }
141
142
    /**
143
     * Добавить к элементу один или несколько inline-стилей
144
     *
145
     * @param string $key
146
     * @param string|null $value
147
     * @return AbstractElement
148
     */
149
    public function style(string $key, string $value = null): self
150
    {
151
        if (empty($value)) {
152
            $pos = strpos($key, ':', 2);
153
            if ($pos !== false) {
154
                $parts = explode(':', strrev($key));
155
                $key = strrev($parts[1]);
156
                $value = strrev($parts[0]);
157
            }
158
        }
159
160
        if ($key && $value) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $value of type null|string is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
161
            $this->styles->put($key, $value);
162
        }
163
164
        return $this;
165
    }
166
167
    /**
168
     * Добавить к элементу один или несколько inline-стилей из массива
169
     *
170
     * @param iterable $styles
171
     * @return AbstractElement
172
     */
173
    public function styles(iterable $styles): self
174
    {
175
        foreach ($styles as $key => $value) {
176
            $this->styles->put($key, $value);
177
        }
178
179
        return $this;
180
    }
181
182
    /**
183
     * Добавить аттрибут к элементу
184
     *
185
     * @param string $key
186
     * @param mixed $value
187
     * @return AbstractElement
188
     */
189
    public function attr(string $key, $value = null): self
190
    {
191
        $this->attributes->put($key, $value);
192
193
        return $this;
194
    }
195
196
    /**
197
     * Добавить один или несколько атрибутов из массива к элементу
198
     *
199
     * @param iterable $attributes
200
     * @return AbstractElement
201
     */
202
    public function attrs(iterable $attributes): self
203
    {
204
        foreach ($attributes as $key => $value) {
205
            if ($key === 'class') {
206
                $this->class($value);
207
            } else if ($key === 'style') {
208
                $this->style($value);
209
            } else {
210
                $this->attributes->put($key, $value);
211
            }
212
        }
213
214
        return $this;
215
    }
216
217
    /**
218
     * Очистить классы элемента
219
     *
220
     * @return AbstractElement
221
     */
222
    public function clearClasses(): self
223
    {
224
        $this->classes = new Collection();
225
226
        return $this;
227
    }
228
229
    /**
230
     * Очистить классы стили
231
     *
232
     * @return AbstractElement
233
     */
234
    public function clearStyles(): self
235
    {
236
        $this->styles = new Collection();
237
238
        return $this;
239
    }
240
241
    /**
242
     * Очистить классы атрибуты
243
     *
244
     * @return AbstractElement
245
     */
246
    public function clearAttributes(): self
247
    {
248
        $this->attributes = new Collection();
249
250
        return $this;
251
    }
252
253
    /**
254
     * Преобразовать в строку для вывода в HTML
255
     *
256
     * @return string
257
     */
258
    public function __toString()
259
    {
260
261
262
        $this->classesToParts();
263
264
        $this->stylesToParts();
265
266
        $this->attributesToParts();
267
268
        $attributes = $this->generateAttributes();
269
270
        $this->parts = new Collection();
271
272
        return $this->needClose
273
            ? '<' . $this->tag . $attributes . '>' . $this->value . '</' . $this->tag . '>'
274
            : '<' . $this->tag . $attributes . '>';
275
    }
276
277
    protected function generateAttributes(): string
278
    {
279
        $attributes = '';
280
281
        foreach ($this->parts as $key => $value) {
282
            if (is_null($value) || (is_bool($value) && $value === false)) {
283
                continue;
284
            } else if (is_bool($value)) {
285
                $value = $key;
286
            }
287
            $attributes .= ' ' . $key . '="' . $value . '"';
288
        }
289
290
        return $attributes;
291
    }
292
293
    protected function stylesToParts(): void
294
    {
295
        if ($this->styles->isNotEmpty()) {
296
            $this->parts->put('style', $this->styles->pipe(function ($styles) {
297
                $styleAttr = '';
298
                foreach ($styles as $key => $value) {
299
                    $styleAttr .= $key . ':' . $value . ';';
300
                }
301
302
                return $styleAttr;
303
            }));
304
        }
305
    }
306
307
    protected function classesToParts(): void
308
    {
309
        if ($this->classes->isNotEmpty()) {
310
            $this->parts->put('class', $this->classes->implode(' '));
311
        }
312
    }
313
314
    public function attributesToParts(): void
315
    {
316
        $this->addAttr('id', 'name');
317
318
        if (!$this->needClose) {
319
            $this->addAttr('value');
320
        }
321
322
        foreach ($this->attributes as $key => $value) {
323
            $this->parts->put($key, $value);
324
        }
325
    }
326
327
    protected function addAttr(...$names): self
328
    {
329
        foreach ($names as $name) {
330
            if (!empty($this->$name)) {
331
                $this->parts[$name] = $this->$name;
332
            }
333
        }
334
335
        return $this;
336
    }
337
338
    protected function addAttrAs(...$names): self
339
    {
340
        foreach ($names as $name) {
341
            if (!empty($this->$name)) {
342
                $this->parts[$name] = $name;
343
            }
344
        }
345
346
        return $this;
347
    }
348
}