Completed
Push — master ( 56f0f7...9c7a3f )
by Alexandr
01:26
created

AbstractForm::assertFieldConfigHasValidators()   A

Complexity

Conditions 3
Paths 2

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 3

Importance

Changes 0
Metric Value
dl 0
loc 6
ccs 4
cts 4
cp 1
rs 9.4285
c 0
b 0
f 0
cc 3
eloc 3
nc 2
nop 2
crap 3
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 13
    public function __construct()
38
    {
39 13
        $fields = $this->fields();
40
41 13
        if (!is_array($fields) || empty($fields)) {
42 1
            throw new ConfigurationException('form is not configured yet');
43
        }
44
45 12
        $this->parseConfiguration($fields);
46 11
    }
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 5
        array_walk($data, function ($value, string $key) {
67 5
            if (isset($this->validators[$key])) {
68 5
                $this->data[$key] = $value;
69
            }
70 5
        });
71 5
        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 2
    public function validate()
106
    {
107 2
        $rules = $this->buildValidationRules();
108
109 2
        $validator = Validator::create($this->data);
110 2
        $validator->rules($rules);
111 2
        $validator->validate();
112
113 1
        return $this;
114
    }
115
116
    /**
117
     * @param array $data
118
     * @return AbstractForm
119
     */
120 6
    protected function fetchRequestData(array $data)
121
    {
122 6
        $processed = Transducer::create($this->types)->execute($data);
123
124 6
        $fields = array_keys($this->validators);
125
126 6
        foreach ($fields as $field) {
127 6
            if (isset($processed[$field])) {
128 6
                if (isset($this->data[$field]) && is_array($this->data[$field])) {
129 2
                    $this->data[$field] = array_merge($this->data[$field], $processed[$field]);
130
                } else {
131 6
                    $this->data[$field] = $processed[$field];
132
                }
133
            }
134
        }
135
136 6
        $unexpectedFields = array_diff(array_keys($data), $fields);
137 6
        if (!empty($unexpectedFields)) {
138 1
            throw new InvalidRequestException('unexpected fields: ' . implode(', ', $unexpectedFields));
139
        }
140
141 5
        return $this;
142
    }
143
144
    /**
145
     * @param array $fields
146
     */
147
    private function parseConfiguration(array $fields)
148
    {
149 12
        array_walk($fields, function (array $config, string $field) {
150 12
            $this->assertFieldConfigHasValidators($field, $config);
151 11
            $this->validators[$field] = $config['validators'];
152 11
            if (isset($config['type'])) {
153 11
                $this->types[$field] = $config['type'];
154
            }
155 11
            if (isset($config['cleanup']) && $config['cleanup'] === true) {
156 10
                $this->cleanup[$field] = true;
157
            }
158 12
        });
159 11
    }
160
161
    /**
162
     * @param string $field
163
     * @param array $config
164
     */
165 12
    private function assertFieldConfigHasValidators(string $field, array $config)
166
    {
167 12
        if (!isset($config['validators']) || !is_array($config['validators'])) {
168 1
            throw new ConfigurationException('fields validation rules are not set: ' . $field);
169
        }
170 11
    }
171
172
    /**
173
     * @return array
174
     */
175 2
    private function buildValidationRules(): array
176
    {
177 2
        $rules = [];
178 2
        foreach ($this->validators as $field => $config) {
179 2
            foreach ($config as $type => $data) {
180 2
                $rule = [$field];
181 2
                if (is_callable($data)) {
182 2
                    $rule[] = call_user_func($data);
183 2
                } elseif (!is_bool($data)) {
184 2
                    $rule[] = $data;
185
                }
186 2
                $rules[$type][] = $rule;
187
            }
188
        }
189 2
        return $rules;
190
    }
191
}