Passed
Branch feature/upgrade-code-quality (f2bae8)
by Csaba
01:55
created

SchemaValidator::objectHasSchemaMethod()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 12
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 7
CRAP Score 2

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 12
ccs 7
cts 7
cp 1
rs 9.4285
cc 2
eloc 6
nc 2
nop 1
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
        if (!empty($diff = array_diff($this->getRequiredFields($resource), array_keys(get_object_vars($resource))))) {
111 4
            array_walk($diff, function ($item, $key) use (&$errors) {
0 ignored issues
show
Unused Code introduced by
The parameter $key is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
112 4
                $errors[$item] = 'Missing required field';
113 4
            });
114
        }
115 13
        return $errors;
116
    }
117
118 12
    private function validateExtraneousFields($resource)
119
    {
120 12
        $errors = [];
121 12
        foreach (array_keys(get_object_vars($resource)) as $fieldName) {
122 10
            if (!isset($resource->schema()[$fieldName])) {
123 10
                $errors[$fieldName] = 'Extraneous field';
124
            }
125
        }
126 12
        return $errors;
127
    }
128
129 14
    private function validateFieldTypes($resource)
130
    {
131 14
        $validatorFactory = new ValidatorFactory;
132 14
        $errors = [];
133 14
        foreach ($resource->schema() as $fieldName => $rules) {
134 14
            if (property_exists($resource, $fieldName)) {
135
                try {
136 11
                    $validatorFactory->create($rules, $this->updateMode())->validate($resource->{$fieldName});
137 3
                } catch (RestException $ex) {
138 3
                    $errors[$fieldName]['error'] = $ex->getMessage();
139 14
                    $errors[$fieldName]['details'] = $ex->getDetails();
140
                }
141
            }
142
        }
143 14
        return $errors;
144
    }
145
146 22
    private function filterFields($resource, $paramKey, $paramValue, $checkParamValue = true)
147
    {
148 22
        $fields = [];
149 22
        foreach ($resource->schema() as $fieldName => $params) {
150 22
            if (array_key_exists($paramKey, $params) && $this->isMatchedValue(
151 21
                $checkParamValue,
152 21
                $params[$paramKey],
153 22
                $paramValue
154
            )) {
155 22
                $fields[$fieldName] = $params;
156
            }
157
        }
158 22
        return $fields;
159
    }
160
161 21
    private function isMatchedValue($checkRequired, $value, $valueToMatch)
162
    {
163 21
        if (!$checkRequired) {
164 2
            return true;
165
        }
166 20
        return ($value == $valueToMatch);
167
    }
168
169 1
    public function getFields($resource)
170
    {
171 1
        return $resource->schema();
172
    }
173
174 13
    public function getRequiredFields($resource)
175
    {
176 13
        return array_keys($this->filterFields($resource, 'required', true));
177
    }
178
179 10
    public function getUniqueFields($resource)
180
    {
181 10
        return array_keys($this->filterFields($resource, 'unique', true));
182
    }
183
184 2
    public function getFieldsWithDefaults($resource)
185
    {
186 2
        return $this->filterFields($resource, 'default', null, false);
187
    }
188
}
189