Passed
Branch master (2bdc71)
by Csaba
06:03 queued 04:17
created

SchemaValidator::updateMode()   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
dl 0
loc 7
c 0
b 0
f 0
ccs 4
cts 4
cp 1
rs 9.4285
cc 2
eloc 4
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 47
    public function __construct($requiredSchemaClass = null)
15
    {
16 47
        $this->requiredSchemaClass = $requiredSchemaClass;
17 47
    }
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 42
    public function allowExtraneous($value)
51
    {
52 42
        $this->allowExtraneous = $value;
53 42
    }
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
        $requiredFields = $this->getRequiredFields($resource);
111 13
        foreach ($requiredFields as $fieldName) {
112 13
            if (!property_exists($resource, $fieldName)) {
113 13
                $errors[$fieldName] = 'Missing required field';
114
            }
115
        }
116 13
        return $errors;
117
    }
118
119 12
    private function validateExtraneousFields($resource)
120
    {
121 12
        $errors = [];
122 12
        foreach (array_keys(get_object_vars($resource)) as $fieldName) {
123 10
            if (!isset($resource->schema()[$fieldName])) {
124 10
                $errors[$fieldName] = 'Extraneous field';
125
            }
126
        }
127 12
        return $errors;
128
    }
129
130 14
    private function validateFieldTypes($resource)
131
    {
132 14
        $validatorFactory = new ValidatorFactory;
133 14
        $errors = [];
134 14
        foreach ($resource->schema() as $fieldName => $rules) {
135 14
            if (property_exists($resource, $fieldName)) {
136
                try {
137 11
                    $validatorFactory->create($rules, $this->updateMode())->validate($resource->{$fieldName});
138 3
                } catch (RestException $ex) {
139 3
                    $errors[$fieldName]['error'] = $ex->getMessage();
140 14
                    $errors[$fieldName]['details'] = $ex->getDetails();
141
                }
142
            }
143
        }
144 14
        return $errors;
145
    }
146
147 21
    private function filterFields($resource, $paramKey, $paramValue)
148
    {
149 21
        $fields = [];
150 21
        foreach ($resource->schema() as $fieldName => $params) {
151 21
            if (isset($params[$paramKey]) && $params[$paramKey] == $paramValue) {
152 21
                $fields[] = $fieldName;
153
            }
154
        }
155 21
        return $fields;
156
    }
157
158 2
    public function getFields($resource)
159
    {
160 2
        return $resource->schema();
161
    }
162
163 13
    public function getRequiredFields($resource)
164
    {
165 13
        return $this->filterFields($resource, 'required', true);
166
    }
167
168 10
    public function getUniqueFields($resource)
169
    {
170 10
        return $this->filterFields($resource, 'unique', true);
171
    }
172
}
173