Schema::setRemainingFieldDefaults()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 10
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

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