Passed
Pull Request — develop (#30)
by
unknown
01:49
created

SchemaValidator::objectIsValidSchemaClass()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 15
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 9
CRAP Score 3

Importance

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