Completed
Push — master ( dddb8f...e7c472 )
by Maxim
06:45
created

AbstractForm::__construct()   B

Complexity

Conditions 5
Paths 16

Size

Total Lines 12
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 5
eloc 6
nc 16
nop 4
dl 0
loc 12
rs 8.8571
c 0
b 0
f 0
1
<?php
2
3
namespace WebComplete\form;
4
5
abstract class AbstractForm implements FormInterface
6
{
7
    use TraitErrors;
8
9
    const REQUIRED = 'required';
10
11
    protected $data = [];
12
    protected $defaultError = 'error';
13
    protected $filtersObject;
14
    protected $validatorsObject;
15
    private $rules;
16
    private $filters;
17
18
    /**
19
     * AbstractForm constructor.
20
     *
21
     * @param null|array $rules
22
     * @param null|array $filters
23
     * @param $filtersObject
24
     * @param $validatorsObject
25
     */
26
    public function __construct($rules = null, $filters = null, $validatorsObject = null, $filtersObject = null)
27
    {
28
        if (\is_object($filtersObject)) {
29
            $this->filtersObject = $filtersObject;
30
        }
31
        if (\is_object($validatorsObject)) {
32
            $this->validatorsObject = $validatorsObject;
33
        }
34
35
        $this->rules = \is_array($rules) ? \array_merge($this->rules(), $rules) : $this->rules();
36
37
        $this->filters = \is_array($filters) ? \array_merge($this->filters(), $filters) : $this->filters();
38
    }
39
40
    /**
41
     * @return bool
42
     * @throws \WebComplete\form\FormException
43
     */
44
    public function validate(): bool
45
    {
46
        /** @var array[] $definitions */
47
        $definitions = $this->normalize($this->rules);
48
49
        $this->resetErrors();
50
        $this->validateRequired($definitions);
51
        foreach ($this->getData() as $field => $value) {
52
            if (isset($definitions[$field])) {
53
                foreach ($definitions[$field] as $definition) {
54
                    $defName = \array_shift($definition);
55
                    $defParams = \array_merge([$value], [\array_shift($definition)], [$this]);
56
                    $defMessage = \array_shift($definition) ?: $this->defaultError;
57
58
                    if ($defName !== self::REQUIRED && !$this->isEmpty($value)
59
                        && !$this->call($defName, $defParams, $this->validatorsObject, true)) {
60
                        $this->addError($field, $defMessage);
61
                    }
62
                }
63
            }
64
        }
65
66
        return !$this->hasErrors();
67
    }
68
69
    /**
70
     * @return array
71
     */
72
    public function getData(): array
73
    {
74
        return $this->data;
75
    }
76
77
    /**
78
     * @param array $data
79
     *
80
     * @return $this
81
     * @throws \WebComplete\form\FormException
82
     */
83
    public function setData(array $data)
84
    {
85
        $this->data = $this->filter($data);
86
87
        return $this;
88
    }
89
90
    /**
91
     * @param $field
92
     *
93
     * @return mixed|null
94
     */
95
    public function getValue($field)
96
    {
97
        return $this->data[$field] ?? null;
98
    }
99
100
    /**
101
     * @param $field
102
     * @param $value
103
     * @param bool $filter
104
     *
105
     * @throws \WebComplete\form\FormException
106
     */
107
    public function setValue($field, $value, $filter = true)
108
    {
109
        if ($filter) {
110
            $data = $this->filter([$field => $value]);
111
            $value = $data[$field] ?? null;
112
        }
113
        $this->data[$field] = $value;
114
    }
115
116
    /**
117
     * @param array $data
118
     *
119
     * @return array
120
     * @throws \WebComplete\form\FormException
121
     */
122
    protected function filter(array $data): array
123
    {
124
        $filtersDefinitions = $this->normalize($this->filters);
125
        $rulesDefinitions = $this->normalize($this->rules);
126
127
        foreach ($data as $field => $value) {
128
            if (!isset($rulesDefinitions[$field]) && !isset($filtersDefinitions[$field])) {
129
                unset($data[$field]);
130
                continue;
131
            }
132
133
            $fieldDefinitions = $filtersDefinitions[$field] ?? [];
134
            if (isset($filtersDefinitions['*'])) {
135
                /** @noinspection SlowArrayOperationsInLoopInspection */
136
                $fieldDefinitions = \array_merge($fieldDefinitions, $filtersDefinitions['*']);
137
            }
138
139
            foreach ($fieldDefinitions as $definition) {
140
                $defName = \array_shift($definition);
141
                $defParams = \array_merge([$value], [\array_shift($definition)], [$this]);
142
                $data[$field] = $this->call($defName, $defParams, $this->filtersObject, $value);
143
            }
144
        }
145
146
        return $data;
147
    }
148
149
    /**
150
     * @param $value
151
     *
152
     * @return bool
153
     */
154
    protected function isEmpty($value): bool
155
    {
156
        return $value === null || $value === '' || (\is_array($value) && !\count($value));
157
    }
158
159
    /**
160
     * @param $definitions
161
     */
162
    protected function validateRequired($definitions)
163
    {
164
        /** @var array[] $definitions */
165
        foreach ($definitions as $field => $fieldDefinitions) {
166
            foreach ($fieldDefinitions as $definition) {
167
                if ($definition[0] === self::REQUIRED && $this->isEmpty($this->getValue($field))) {
168
                    $this->addError($field, $definition[3] ?? $this->defaultError);
169
                }
170
            }
171
        }
172
    }
173
174
    /**
175
     * @param $definitions
176
     *
177
     * @return array
178
     */
179
    private function normalize($definitions): array
180
    {
181
        $normalized = [];
182
        /** @var array[] $definitions */
183
        foreach ($definitions as $definition) {
184
            $fields = \array_shift($definition);
185
            $defName = $definition ? \array_shift($definition) : null;
186
            $defParams = $definition ? \array_shift($definition) : [];
187
            $defMessage = $definition ? \array_shift($definition) : '';
188
            $fields = (array)$fields;
189
            foreach ($fields as $field) {
190
                if (!isset($normalized[$field])) {
191
                    $normalized[$field] = [];
192
                }
193
                $normalized[$field][] = [$defName, $defParams, $defMessage];
194
            }
195
        }
196
197
        return $normalized;
198
    }/** @noinspection MoreThanThreeArgumentsInspection */
199
200
    /**
201
     * @param $defName
202
     * @param $defParams
203
     * @param $object
204
     * @param $default
205
     *
206
     * @return mixed|null
207
     * @throws FormException
208
     */
209
    private function call($defName, $defParams, $object, $default)
210
    {
211
        $callable = $defName;
212
        if ($defName) {
213
            if (!\is_array($defName)) {
214
                if (\method_exists($this, $defName)) {
215
                    $callable = [$this, $defName];
216
                } elseif ($object && \method_exists($object, $defName)) {
217
                    $callable = [$object, $defName];
218
                }
219
            }
220
221
            if (!\is_callable($callable)) {
222
                throw new FormException('Callable not found: ' . \json_encode($callable));
223
            }
224
        }
225
226
        return $callable ? \call_user_func_array($callable, $defParams) : $default;
227
    }
228
}
229