Completed
Push — master ( e4121e...ad129c )
by Filipe
09:07
created

AddElements::create()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 9
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 7
CRAP Score 2

Importance

Changes 1
Bugs 0 Features 1
Metric Value
c 1
b 0
f 1
dl 0
loc 9
ccs 7
cts 7
cp 1
rs 9.6666
cc 2
eloc 6
nc 2
nop 1
crap 2
1
<?php
2
3
/**
4
 * This file is part of slick/form package
5
 *
6
 * For the full copyright and license information, please view the LICENSE
7
 * file that was distributed with this source code.
8
 */
9
10
namespace Slick\Form\Parser\Worker;
11
12
use Slick\Filter\StaticFilter;
13
use Slick\Form\Element\Button;
14
use Slick\Form\Element\ChoiceAwareElementInterface;
15
use Slick\Form\Element\ContainerInterface;
16
use Slick\Form\Element\FieldSet;
17
use Slick\Form\Element\Label;
18
use Slick\Form\Element\Reset;
19
use Slick\Form\Element\Submit;
20
use Slick\Form\ElementInterface;
21
use Slick\Form\Exception\InvalidArgumentException;
22
use Slick\Form\Input\Checkbox;
23
use Slick\Form\Input\File;
24
use Slick\Form\Input\Filter\Boolean;
25
use Slick\Form\Input\Hidden;
26
use Slick\Form\Input\Password;
27
use Slick\Form\Input\Select;
28
use Slick\Form\Input\Text;
29
use Slick\Form\Input\TextArea;
30
use Slick\Form\InputInterface;
31
use Slick\Form\Parser\WorkerInterface;
32
33
/**
34
 * Add Elements to the form
35
 *
36
 * @package Slick\Form\Parser\Worker
37
 * @author  Filipe Silva <[email protected]>
38
 */
39
class AddElements implements WorkerInterface
40
{
41
    /**
42
     * @var array Available form elements
43
     */
44
    public static $elements = [
45
        'button'   => Button::class,
46
        'fieldset' => FieldSet::class,
47
        'label'    => Label::class,
48
        'reset'    => Reset::class,
49
        'submit'   => Submit::class,
50
        'checkbox' => Checkbox::class,
51
        'hidden'   => Hidden::class,
52
        'password' => Password::class,
53
        'select'   => Select::class,
54
        'text'     => Text::class,
55
        'textarea' => TextArea::class,
56
        'file'     => File::class
57
    ];
58
59
    /**
60
     * @var array List of validators that adds required attribute to input
61
     */
62
    public static $triggerRequired = [
63
        'notEmpty', 'email', 'url'
64
    ];
65
    
66
    private static $form;
67
68
    /**
69
     * Adds or changes a specific aspect of provided from
70
     *
71
     * @param ContainerInterface $form
72
     * @param array $data
73
     *
74
     * @return void
75
     */
76 36
    public static function execute(ContainerInterface $form, array $data)
77
    {
78 36
        self::$form = $form;
79 36
        $hasElements = isset($data['elements']) && is_array($data['elements']);
80 36
        if (!$hasElements) {
81 2
            return;
82
        }
83
84 36
        foreach ($data['elements'] as $name => $element) {
85 36
            $input = self::create($element);
86 36
            $input->setName($name);
87 36
            self::populateInputs($input, $element);
88 36
            $form->add($input);
89 36
        }
90 36
    }
91
92
    /**
93
     * Recursively creates the elements to add to the form
94
     *
95
     * @param array $element
96
     *
97
     * @return ElementInterface
98
     */
99 36
    protected static function create(array $element)
100
    {
101 36
        $class = self::getClassName($element['type']);
102 36
        $object = new $class;
103 36
        if ($object instanceof ContainerInterface) {
104 30
            static::execute($object, $element);
105 30
        }
106 36
        return $object;
107
    }
108
109
    /**
110
     * Sets the properties and dependencies for input elements
111
     *
112
     * @param ElementInterface $input
113
     * @param array $data
114
     */
115 36
    protected static function populateInputs(
116
        ElementInterface $input, array $data
117
    ) {
118 36
        if ($input instanceof InputInterface) {
119 36
            self::addLabel($input, $data);
120 36
            self::addValidators($input, $data);
121 36
            self::setFilters($input, $data);
122 36
            self::addOptions($input, $data);
123 36
            if (isset($data['required'])) {
124 30
                $input->setRequired(
125 30
                    StaticFilter::filter(Boolean::class, $data['required'])
126 30
                );
127 30
            }
128 36
        }
129
130 36
        self::populateElement($input, $data);
131 36
    }
132
133
    /**
134
     * Sets the properties and dependencies for HTML elements
135
     *
136
     * @param ElementInterface $elm
137
     * @param array $data
138
     */
139 36
    protected static function populateElement(ElementInterface $elm, array $data)
140
    {
141 36
        self::setValue($elm, $data);
142
143 36
        $hasAttributes = isset($data['attributes'])
144 36
            && is_array($data['attributes']);
145 36
        if (! $hasAttributes) {
146 30
            return;
147
        }
148
149 36
        foreach ($data['attributes'] as $attribute => $value) {
150 36
            $elm->setAttribute($attribute, $value);
151 36
        }
152 36
    }
153
154
    /**
155
     * Adds the value to the element
156
     *
157
     * @param ElementInterface $elm
158
     * @param array $data
159
     */
160 36
    protected static function setValue(ElementInterface $elm, $data)
161
    {
162 36
        if (isset($data['value'])) {
163 30
            $elm->setValue($data['value']);
164 30
        }
165 36
    }
166
167
    /**
168
     * Check the element alias or FQ class name
169
     *
170
     * @param string $type
171
     *
172
     * @return string The Element class name
173
     */
174 36
    protected static function getClassName($type)
175
    {
176 36
        if (in_array($type, array_keys(self::$elements))) {
177 36
            $type = self::$elements[$type];
178 36
        }
179
180 36
        if (!class_exists($type)) {
181 2
            throw new InvalidArgumentException(
182 2
                "Input class '{$type}' does not exists."
183 2
            );
184
        }
185
186 36
        if (! is_subclass_of($type, ElementInterface::class)) {
187 2
            throw new InvalidArgumentException(
188 2
                "The class '{$type}' does not implement the " .
189
                "Slick\\Form\\ElementInterface interface."
190 2
            );
191
        }
192
193 36
        return $type;
194
    }
195
196
    /**
197
     * Add filter to the input filter chain
198
     *
199
     * @param InputInterface $input
200
     * @param array $data
201
     */
202 36
    protected static function setFilters(InputInterface $input, array $data)
203
    {
204 36
        $hasFilters = isset($data['filters']) && is_array($data['filters']);
205 36
        if (!$hasFilters) {
206 36
            return;
207
        }
208
209 30
        foreach ($data['filters'] as $filter) {
210 30
            $input->addFilter($filter);
211 30
        }
212 30
    }
213
214
    /**
215
     * Add validators to the input validator chain
216
     *
217
     * @param InputInterface $input
218
     * @param array $data
219
     */
220 36
    protected static function addValidators(InputInterface $input, array $data)
221
    {
222 36
        $hasValidators = isset($data['validates']) && is_array($data['validates']);
223 36
        if (! $hasValidators) {
224 36
            return;
225
        }
226
227 30
        foreach ($data['validates'] as $validator => $message) {
228 30
            self::checkIfRequired($validator, $input);
229 30
            if (!is_array($message)) {
230 30
                $message = ['message' => $message];
231 30
            }
232 30
            $message['form'] = self::$form;
233 30
            $input->addValidator($validator, $message);
0 ignored issues
show
Documentation introduced by
$message is of type array<string,object<Slic...\ContainerInterface>"}>, but the function expects a string|null.

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...
234 30
        }
235 30
    }
236
237
    /**
238
     * Adds the html Label element to input
239
     *
240
     * @param InputInterface $input
241
     * @param array $data
242
     */
243 36
    protected static function addLabel(InputInterface $input, array $data)
244
    {
245 36
        if (!isset($data['label'])) {
246 2
            return;
247
        }
248 36
        if (is_string($data['label'])) {
249 36
            $input->setLabel($data['label']);
250 36
            return;
251
        }
252
253 30
        $label = new Label('', $data['label']['value']);
254 30
        self::populateElement($label, $data['label']);
255 30
        $input->setLabel($label);
256 30
    }
257
258
    /**
259
     * Check if validator triggers the required attribute
260
     *
261
     * @param string $name Validator name
262
     * @param InputInterface $input
263
     */
264 30
    protected static function checkIfRequired($name, InputInterface $input)
265
    {
266 30
        if (in_array($name, self::$triggerRequired)) {
267 30
            $input->setAttribute('required');
268 30
        }
269 30
    }
270
271
    /**
272
     * Set options for ChoiceAwareElementInterface input types like Select
273
     *
274
     * @param InputInterface $input
275
     * @param $data
276
     */
277 36
    protected static function addOptions(InputInterface $input, $data)
278
    {
279
        if (
280 36
            ! $input instanceof ChoiceAwareElementInterface ||
281 30
            ! isset($data['options'])
282 36
        ) {
283 36
            return;
284
        }
285
286 30
        $input->setOptions($data['options']);
287 30
    }
288
289
}