Passed
Pull Request — develop (#43)
by
unknown
04:42
created

SchemaValidator::isMatchedValue()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 7
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 2

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 7
ccs 4
cts 4
cp 1
rs 9.4285
cc 2
eloc 4
nc 2
nop 3
crap 2
1
<?php
2
namespace Fathomminds\Rest\Schema;
3
4
use Fathomminds\Rest\Schema\TypeValidators\ValidatorFactory;
5
use Fathomminds\Rest\Exceptions\RestException;
6
7
class SchemaValidator
8
{
9
    protected $fields = [];
10
    protected $allowExtraneous = false;
11
    protected $requiredSchemaClass = null;
12
    private $updateMode = false;
13
14 48
    public function __construct($requiredSchemaClass = null)
15
    {
16 48
        $this->requiredSchemaClass = $requiredSchemaClass;
17 48
    }
18
19 14
    public function updateMode($updateMode = null)
20
    {
21 14
        if (is_bool($updateMode)) {
22 11
            $this->updateMode = $updateMode;
23
        }
24 14
        return $this->updateMode;
25
    }
26
27 14
    public function validate($resource)
28
    {
29 14
        $this->validateResourceType($resource);
30 14
        $extraneousCheck = [];
31 14
        if (!$this->allowExtraneous) {
32 12
            $extraneousCheck = $this->validateExtraneousFields($resource);
33
        }
34 14
        $errors = array_merge(
35 14
            $this->validateRequiredFields($resource),
36 14
            $extraneousCheck,
37 14
            $this->validateFieldTypes($resource)
38
        );
39 14
        if (!empty($errors)) {
40 8
            throw new RestException(
41 8
                'Invalid structure',
42
                [
43 8
                    'schema' => get_class($resource),
44 8
                    'errors' => $errors,
45
                ]
46
            );
47
        }
48 6
    }
49
50 43
    public function allowExtraneous($value)
51
    {
52 43
        $this->allowExtraneous = $value;
53 43
    }
54
55 14
    private function validateResourceType($resource)
56
    {
57 14
        $this->expectObject($resource);
58 14
        $this->objectHasSchemaMethod($resource);
59 14
        $this->objectIsValidSchemaClass($resource);
60 14
    }
61
62 15
    private function expectObject($resource)
63
    {
64 15
        if (gettype($resource) !== 'object') {
65 1
            throw new RestException(
66 1
                'Object expected',
67
                [
68 1
                    'schema' => static::class,
69 1
                    'type' => gettype($resource),
70
                ]
71
            );
72
        }
73 14
    }
74
75 14
    private function objectHasSchemaMethod($resource)
76
    {
77 14
        if (!method_exists($resource, 'schema')) {
78 1
            throw new RestException(
79 1
                'Object must be a correct RestSchema object',
80
                [
81 1
                    'schema' => static::class,
82 1
                    'type' => gettype($resource),
83
                ]
84
            );
85
        }
86 14
    }
87
88 14
    private function objectIsValidSchemaClass($resource)
89
    {
90 14
        if ($this->requiredSchemaClass === null) {
91 14
            return;
92
        }
93 3
        if (get_class($resource) !== $this->requiredSchemaClass) {
94 1
            throw new RestException(
95 1
                'Object must be an instance of the defined SchemaClass',
96
                [
97 1
                    'schema' => static::class,
98 1
                    'type' => gettype($resource),
99
                ]
100
            );
101
        }
102 2
    }
103
104 14
    private function validateRequiredFields($resource)
105
    {
106 14
        $errors = [];
107 14
        if ($this->updateMode()) {
108 1
            return $errors;
109
        }
110 13
        $missingFields = array_diff($this->getRequiredFields($resource), array_keys(get_object_vars($resource)));
111 13
        array_walk($missingFields, function ($item) use (&$errors) {
112 4
            $errors[$item] = 'Missing required field';
113 13
        });
114 13
        return $errors;
115
    }
116
117 12
    private function validateExtraneousFields($resource)
118
    {
119 12
        $errors = [];
120 12
        $extraFields = array_diff(array_keys(get_object_vars($resource)), array_keys($resource->schema()));
121 12
        array_walk($extraFields, function ($item) use (&$errors) {
122 1
            $errors[$item] = 'Extraneous field';
123 12
        });
124 12
        return $errors;
125
    }
126
127 14
    private function validateFieldTypes($resource)
128
    {
129 14
        $validatorFactory = new ValidatorFactory;
130 14
        $errors = [];
131 14
        foreach ($resource->schema() as $fieldName => $rules) {
132 14
            if (property_exists($resource, $fieldName)) {
133
                try {
134 11
                    $validatorFactory->create($rules, $this->updateMode())->validate($resource->{$fieldName});
135 3
                } catch (RestException $ex) {
136 3
                    $errors[$fieldName]['error'] = $ex->getMessage();
137 14
                    $errors[$fieldName]['details'] = $ex->getDetails();
138
                }
139
            }
140
        }
141 14
        return $errors;
142
    }
143
144 22
    private function filterFields($resource, $paramKey, $paramValue, $checkParamValue = true)
145
    {
146 22
        $fields = [];
147 22
        foreach ($resource->schema() as $fieldName => $params) {
148 22
            if (array_key_exists($paramKey, $params) && $this->isMatchedValue(
149 21
                $checkParamValue,
150 21
                $params[$paramKey],
151 22
                $paramValue
152
            )) {
153 22
                $fields[$fieldName] = $params;
154
            }
155
        }
156 22
        return $fields;
157
    }
158
159 21
    private function isMatchedValue($checkRequired, $value, $valueToMatch)
160
    {
161 21
        if (!$checkRequired) {
162 2
            return true;
163
        }
164 20
        return ($value == $valueToMatch);
165
    }
166
167 1
    public function getFields($resource)
168
    {
169 1
        return $resource->schema();
170
    }
171
172 13
    public function getRequiredFields($resource)
173
    {
174 13
        return array_keys($this->filterFields($resource, 'required', true));
175
    }
176
177 10
    public function getUniqueFields($resource)
178
    {
179 10
        return array_keys($this->filterFields($resource, 'unique', true));
180
    }
181
182 2
    public function getFieldsWithDefaults($resource)
183
    {
184 2
        return $this->filterFields($resource, 'default', null, false);
185
    }
186
}
187