Completed
Push — master ( f5f4dc...878208 )
by Alexandr
9s
created

AbstractForm::buildCustomValidationMessages()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 14
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 8
CRAP Score 4

Importance

Changes 0
Metric Value
dl 0
loc 14
ccs 8
cts 8
cp 1
rs 9.2
c 0
b 0
f 0
cc 4
eloc 8
nc 4
nop 0
crap 4
1
<?php
2
3
namespace Bricks;
4
5
use Bricks\Data\Transducer;
6
use Bricks\Data\Validator;
7
use Bricks\Exception\ConfigurationException;
8
use Bricks\Exception\InvalidRequestException;
9
use Psr\Http\Message\RequestInterface;
10
11
/**
12
 * Class AbstractForm
13
 * @package Bricks
14
 */
15
abstract class AbstractForm implements FormInterface
16
{
17
    /**
18
     * @var array
19
     */
20
    protected $data = [];
21
22
    /**
23
     * @var array
24
     */
25
    private $types = [];
26
27
    /**
28
     * @var array
29
     */
30
    private $validators = [];
31
32
    /**
33
     * @var array
34
     */
35
    private $cleanup = [];
36
37 14
    public function __construct()
38
    {
39 14
        $fields = $this->fields();
40
41 14
        if (!is_array($fields) || empty($fields)) {
42 1
            throw new ConfigurationException('form is not configured yet');
43
        }
44
45 13
        $this->parseConfiguration($fields);
46 12
    }
47
48
    /**
49
     * @return static
50
     */
51 1
    public static function create()
52
    {
53 1
        return new static();
54
    }
55
56
    /**
57
     * @return array
58
     */
59
    abstract protected function fields(): array;
60
61
    /**
62
     * {@inheritDoc}
63
     */
64
    public function setData(array $data)
65
    {
66 6
        array_walk($data, function ($value, string $key) {
67 6
            if (isset($this->validators[$key])) {
68 6
                $this->data[$key] = $value;
69
            }
70 6
        });
71 6
        return $this;
72
    }
73
74
    /**
75
     * {@inheritDoc}
76
     */
77
    public function getData(): array
78
    {
79 1
        return array_filter($this->data, function (string $field) {
80 1
            return !isset($this->cleanup[$field]);
81 1
        }, ARRAY_FILTER_USE_KEY);
82
    }
83
84
    /**
85
     * {@inheritDoc}
86
     */
87 7
    public function handleRequest(RequestInterface $request)
88
    {
89 7
        $method = strtoupper($request->getMethod());
90
91 7
        if ($method === 'GET' || $method === 'DELETE') {
92 2
            parse_str($request->getUri()->getQuery(), $data);
93
        } else {
94 5
            $data = json_decode($request->getBody()->getContents(), true);
95 5
            if (!isset($data) && json_last_error() !== JSON_ERROR_NONE) {
96 1
                throw new InvalidRequestException('unable to parse json body: ' . json_last_error_msg());
97
            }
98
        }
99 6
        return $this->fetchRequestData($data);
100
    }
101
102
    /**
103
     * {@inheritDoc}
104
     */
105 3
    public function validate()
106
    {
107 3
        $rules = $this->buildValidationRules();
108 3
        $messages = $this->buildCustomValidationMessages();
109
110 3
        $validator = Validator::create($this->data);
111 3
        $validator->rules($rules, $messages);
112 3
        $validator->validate();
113
114 1
        return $this;
115
    }
116
117
    /**
118
     * @param array $data
119
     * @return AbstractForm
120
     */
121 6
    protected function fetchRequestData(array $data)
122
    {
123 6
        $processed = Transducer::create($this->types)->execute($data);
124
125 6
        $fields = array_keys($this->validators);
126
127 6
        foreach ($fields as $field) {
128 6
            if (isset($processed[$field])) {
129 6
                if (isset($this->data[$field]) && is_array($this->data[$field])) {
130 2
                    $this->data[$field] = array_merge($this->data[$field], $processed[$field]);
131
                } else {
132 6
                    $this->data[$field] = $processed[$field];
133
                }
134
            }
135
        }
136
137 6
        $unexpectedFields = array_diff(array_keys($data), $fields);
138 6
        if (!empty($unexpectedFields)) {
139 1
            throw new InvalidRequestException('unexpected fields: ' . implode(', ', $unexpectedFields));
140
        }
141
142 5
        return $this;
143
    }
144
145
    /**
146
     * @param array $fields
147
     */
148
    private function parseConfiguration(array $fields)
149
    {
150 13
        array_walk($fields, function (array $config, string $field) {
151 13
            $this->assertFieldConfigHasValidators($field, $config);
152 12
            $this->validators[$field] = $config['validators'];
153 12
            if (isset($config['type'])) {
154 11
                $this->types[$field] = $config['type'];
155
            }
156 12
            if (isset($config['cleanup']) && $config['cleanup'] === true) {
157 10
                $this->cleanup[$field] = true;
158
            }
159 13
        });
160 12
    }
161
162
    /**
163
     * @param string $field
164
     * @param array $config
165
     */
166 13
    private function assertFieldConfigHasValidators(string $field, array $config)
167
    {
168 13
        if (!isset($config['validators']) || !is_array($config['validators'])) {
169 1
            throw new ConfigurationException('fields validation rules are not set: ' . $field);
170
        }
171 12
    }
172
173
    /**
174
     * @return array
175
     */
176 3
    private function buildValidationRules(): array
177
    {
178 3
        $rules = [];
179 3
        foreach ($this->validators as $field => $config) {
180 3
            foreach ($config as $type => $data) {
181 3
                $rule = [$field];
182 3
                if (is_callable($data)) {
183 2
                    $rule[] = call_user_func($data);
184 3
                } elseif (!is_bool($data)) {
185 3
                    $rule[] = $data;
186
                }
187 3
                $rules[$type][] = $rule;
188
            }
189
        }
190 3
        return $rules;
191
    }
192
193
    /**
194
     * @return array
195
     */
196 3
    private function buildCustomValidationMessages(): array
197
    {
198 3
        $fields = $this->fields();
199 3
        $messages = [];
200 3
        foreach ($this->validators as $field => $config) {
201 3
            foreach ($config as $type => $data) {
202 3
                if (isset($fields[$field]['messages'][$type])) {
203 3
                    $messages[$type][$field] = $fields[$field]['messages'][$type];
204
                }
205
            }
206
        }
207
208 3
        return $messages;
209
    }
210
}