Completed
Push — master ( 297d81...f5b8f3 )
by Alexandr
01:26
created

AbstractForm::setData()   A

Complexity

Conditions 2
Paths 1

Size

Total Lines 9
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 9
ccs 5
cts 5
cp 1
rs 9.6666
c 0
b 0
f 0
cc 2
eloc 5
nc 1
nop 1
crap 2
1
<?php
2
3
namespace Bricks;
4
5
use Bricks\Data\Cast;
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 12
    public function __construct()
38
    {
39 12
        $fields = $this->fields();
40
41 12
        if (!is_array($fields) || empty($fields)) {
42 1
            throw new ConfigurationException('form is not configured yet');
43
        }
44
45 11
        $this->parseConfiguration($fields);
46 10
    }
47
48
    /**
49
     * @return array
50
     */
51
    abstract protected function fields(): array;
52
53
    /**
54
     * {@inheritDoc}
55
     */
56
    public function setData(array $data)
57
    {
58 5
        array_walk($data, function ($value, string $key) {
59 5
            if (isset($this->validators[$key])) {
60 5
                $this->data[$key] = $value;
61
            }
62 5
        });
63 5
        return $this;
64
    }
65
66
    /**
67
     * {@inheritDoc}
68
     */
69
    public function getData(): array
70
    {
71 1
        return array_filter($this->data, function (string $field) {
72 1
            return !isset($this->cleanup[$field]);
73 1
        }, ARRAY_FILTER_USE_KEY);
74
    }
75
76
    /**
77
     * {@inheritDoc}
78
     */
79 7
    public function handleRequest(RequestInterface $request)
80
    {
81 7
        $method = strtoupper($request->getMethod());
82
83 7
        if ($method === 'GET' || $method === 'DELETE') {
84 2
            parse_str($request->getUri()->getQuery(), $data);
85
        } else {
86 5
            $data = json_decode($request->getBody()->getContents(), true);
87 5
            if (!isset($data) && json_last_error() !== JSON_ERROR_NONE) {
88 1
                throw new InvalidRequestException('unable to parse json body: ' . json_last_error_msg());
89
            }
90
        }
91 6
        return $this->fetchRequestData($data);
92
    }
93
94
    /**
95
     * {@inheritDoc}
96
     */
97 2
    public function validate()
98
    {
99 2
        $rules = $this->buildValidationRules();
100
101 2
        $validator = Validator::create($this->data);
102 2
        $validator->rules($rules);
103 2
        $validator->validate();
104
105 1
        return $this;
106
    }
107
108
    /**
109
     * @param array $data
110
     * @return AbstractForm
111
     */
112 6
    protected function fetchRequestData(array $data)
113
    {
114 6
        $casted = Cast::create($this->types)->execute($data);
115
116 6
        $fields = array_keys($this->validators);
117
118 6
        foreach ($fields as $field) {
119 6
            if (isset($casted[$field])) {
120 5
                if (isset($this->data[$field]) && is_array($this->data[$field])) {
121 1
                    $this->data[$field] = array_merge($this->data[$field], $casted[$field]);
122
                } else {
123 6
                    $this->data[$field] = $casted[$field];
124
                }
125
            }
126
        }
127
128 6
        $unexpectedFields = array_diff(array_keys($data), $fields);
129 6
        if (!empty($unexpectedFields)) {
130 1
            throw new InvalidRequestException('unexpected fields: ' . implode(', ', $unexpectedFields));
131
        }
132
133 5
        return $this;
134
    }
135
136
    /**
137
     * @param array $fields
138
     */
139 11
    private function parseConfiguration(array $fields)
140
    {
141 11
        $invalidConfigFields = [];
142
143 11
        array_walk($fields, function (array $config, string $field) use (&$invalidConfigFields) {
144 11
            if (isset($config['validators'])) {
145 10
                $this->validators[$field] = $config['validators'];
146 10
                if (isset($config['type'])) {
147 10
                    $this->types[$field] = $config['type'];
148
                }
149 10
                if (isset($config['cleanup']) && $config['cleanup'] === true) {
150 10
                    $this->cleanup[$field] = true;
151
                }
152
            } else {
153 1
                $invalidConfigFields[] = $field;
154
            }
155 11
        });
156
157 11
        if (!empty($invalidConfigFields)) {
158 1
            throw new ConfigurationException(
159 1
                'fields validation rules are not set: ' . implode(',', $invalidConfigFields)
160
            );
161
        }
162 10
    }
163
164
    /**
165
     * Построение правил валидации на основе заданного конфига
166
     *
167
     * @return array
168
     */
169 2
    private function buildValidationRules()
170
    {
171 2
        $rules = [];
172 2
        foreach ($this->validators as $field => $config) {
173 2
            foreach ($config as $type => $data) {
174 2
                $rule = [$field];
175 2
                if (is_callable($data)) {
176 2
                    $rule[] = call_user_func($data);
177 2
                } elseif (!is_bool($data)) {
178 2
                    $rule[] = $data;
179
                }
180 2
                $rules[$type][] = $rule;
181
            }
182
        }
183 2
        return $rules;
184
    }
185
}