Passed
Push — master ( 86ecbf...ecae23 )
by
unknown
01:53
created

Schema::cast()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 4
c 0
b 0
f 0
ccs 2
cts 2
cp 1
rs 10
cc 1
eloc 2
nc 1
nop 2
crap 1
1
<?php
2
namespace Fathomminds\Rest;
3
4
use Fathomminds\Rest\Contracts\ISchema;
5
use Fathomminds\Rest\Helpers\ObjectMapper;
6
use Fathomminds\Rest\Schema\SchemaValidator;
7
use Fathomminds\Rest\Schema\TypeValidators\ValidatorFactory;
8
use Fathomminds\Rest\Exceptions\RestException;
9
10
abstract class Schema implements ISchema
11
{
12
    const REPLACE_MODE = 'replace';
13
    const UPDATE_MODE = 'update';
14
15 37
    public static function cast($object, $skipExtraneous = false)
16
    {
17 37
        return new static($object, $skipExtraneous);
18
    }
19
20 3
    public static function map($object, $map, $skipExtraneous = false)
21
    {
22 3
        $mappedObject = ObjectMapper::map($object, $map);
23 1
        return new static($mappedObject, $skipExtraneous);
24
    }
25
26 90
    public function __construct($object = null, $skipExtraneous = false)
27
    {
28 90
        if ($object === null) {
29 73
            return;
30
        }
31 53
        if (gettype($object) !== 'object') {
32 1
            throw new RestException('Schema constructor expects object or null as parameter', [
33 1
                'parameter' => $object,
34
            ]);
35
        }
36 52
        if (!is_bool($skipExtraneous)) {
37 1
            $skipExtraneous = false;
38
        }
39 52
        if ($skipExtraneous) {
40 2
            $this->castPropertiesWithoutExtraneous($object, $this->schema());
41 2
            return;
42
        }
43 52
        $this->castProperties($object, $this->schema());
44 52
    }
45
46 2
    private function castPropertiesWithoutExtraneous($object, $schema)
47
    {
48 2
        foreach (get_object_vars($object) as $name => $value) {
49 2
            list($propertyExists, $propertyValue) = $this->castPropertyWithoutExtraneous($schema, $name, $value);
50 2
            if ($propertyExists) {
51 2
                $this->{$name} = $propertyValue;
52
            }
53
        }
54 2
    }
55
56 2
    private function castPropertyWithoutExtraneous($schema, $name, $value)
57
    {
58 2
        if (!array_key_exists($name, $schema)) {
59 2
            return [false, null];
60
        }
61 2
        if (isset($schema[$name]['type']) && $schema[$name]['type'] === 'schema') {
62 2
            return [true, $schema[$name]['validator']['class']::cast($value, true)];
63
        }
64 2
        $params = empty($schema[$name]['validator']['params'])
65 2
            ? null
66 2
            : $schema[$name]['validator']['params'];
67 2
        return [true, $schema[$name]['validator']['class']::cast($value, $params)];
68
    }
69
70 52
    private function castProperties($object, $schema)
71
    {
72 52
        foreach (get_object_vars($object) as $name => $value) {
73 32
            $this->{$name} = $this->castProperty($schema, $name, $value);
74
        }
75 52
    }
76
77 32
    private function castProperty($schema, $name, $value)
78
    {
79 32
        if (!array_key_exists($name, $schema)) {
80 2
            return $value;
81
        }
82 32
        if (isset($schema[$name]['type']) && $schema[$name]['type'] === 'schema') {
83 13
            return $schema[$name]['validator']['class']::cast($value);
84
        }
85 32
        $params = empty($schema[$name]['validator']['params'])
86 32
            ? null
87 32
            : $schema[$name]['validator']['params'];
88 32
        return $schema[$name]['validator']['class']::cast($value, $params);
89
    }
90
91 2
    public function __get($name)
92
    {
93 2
        if (!isset($this->{$name})) {
94 1
            throw new RestException(
95 1
                'Trying to access undefined property ' . $name,
96 1
                []
97
            );
98
        }
99 1
        return $this->{$name};
100
    }
101
102
    abstract public function schema();
103
104 4
    public function toArray()
105
    {
106 4
        return json_decode(json_encode($this), true);
107
    }
108
109 5
    public function setFieldDefaults()
110
    {
111 5
        $schemaValidator = new SchemaValidator(static::class);
112 5
        $schemaFields = $schemaValidator->getSchemaFieldsWithDetails($this);
113 5
        $defaultFields = $schemaValidator->getFieldsWithDefaults($this);
114 5
        $this->setNestedFieldDefaults($schemaFields);
115 5
        $this->setRemainingFieldDefaults($defaultFields);
116 5
        return $this;
117
    }
118
119 5
    protected function setNestedFieldDefaults($schemaFields)
120
    {
121 5
        foreach ($schemaFields as $schemaFieldName => $schemaFieldDetails) {
122 5
            $propertyExists = property_exists($this, $schemaFieldName);
123 5
            if (isset($schemaFieldDetails['default']) && !$propertyExists) {
124 5
                $this->setFieldDefaultValue($schemaFieldName, $schemaFieldDetails['default']);
125 5
                $propertyExists = true;
126
            }
127 5
            if ($propertyExists) {
128 5
                $this->{$schemaFieldName}->setFieldDefaults();
129
            }
130
        }
131 5
    }
132
133 5
    protected function setRemainingFieldDefaults($defaultFields)
134
    {
135 5
        $properties = array_diff_key(
136 5
            $defaultFields,
137 5
            get_object_vars($this)
138
        );
139 5
        foreach ($properties as $fieldName => $field) {
140 5
            $this->setFieldDefaultValue($fieldName, $field['default']);
141
        }
142 5
    }
143
144 5
    protected function setFieldDefaultValue($fieldName, $value)
145
    {
146 5
        if (gettype($value) === 'object' && is_callable($value)) {
147 5
            $this->{$fieldName} = $value();
148 5
            return;
149
        }
150 5
        $this->{$fieldName} = $value;
151 5
    }
152
153 6
    public function validate($mode = null)
154
    {
155 6
        $schemaValidator = new SchemaValidator(static::class);
156
        switch ($mode) {
157 6
            case self::REPLACE_MODE:
158 2
                $schemaValidator->replaceMode(true);
159 2
                $schemaValidator->updateMode(false);
160 2
                break;
161 4
            case self::UPDATE_MODE:
162 1
                $schemaValidator->replaceMode(false);
163 1
                $schemaValidator->updateMode(true);
164 1
                break;
165
            default:
166 3
                break;
167
        }
168 6
        $schemaValidator->validate($this);
169 3
        return $this;
170
    }
171
172 1
    public function removeExtraneous()
173
    {
174 1
        $schema = $this->schema();
175 1
        foreach (get_object_vars($this) as $name => $value) {
176 1
            if (!array_key_exists($name, $schema)) {
177 1
                unset($this->{$name});
178 1
                continue;
179
            }
180 1
            if (isset($schema[$name]['type']) && $schema[$name]['type'] === 'schema') {
181 1
                $this->{$name}->removeExtraneous();
182
            }
183
        }
184 1
    }
185
}
186