Completed
Pull Request — master (#12)
by Alexander
01:58
created

Entity   B

Complexity

Total Complexity 36

Size/Duplication

Total Lines 210
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 0

Importance

Changes 0
Metric Value
dl 0
loc 210
rs 8.8
c 0
b 0
f 0
wmc 36
lcom 1
cbo 0

15 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 4 1
A __get() 0 4 2
A __call() 0 5 1
A jsonSerialize() 0 4 1
A getRelatedModel() 0 8 3
A checkIfRelationIsSet() 0 8 3
A with() 0 7 2
B populateRelation() 0 24 6
B describeRelationship() 0 19 6
A generateRelationshipFromShorthand() 0 20 2
A __set() 0 4 1
A __isset() 0 4 1
A data() 0 10 3
A reset() 0 4 1
A to() 0 11 3
1
<?php
2
3
namespace alkemann\h2l;
4
5
/**
6
 * Class Entity
7
 *
8
 * @package alkemann\h2l
9
 */
10
trait Entity
11
{
12
13
    /**
14
     * @var array
15
     */
16
    protected $data = [];
17
18
    protected $relationships = [];
19
20
    /**
21
     * Entity constructor.
22
     *
23
     * @param array $data
24
     */
25
    public function __construct(array $data = [])
26
    {
27
        $this->data = $data;
28
    }
29
30
    /**
31
     * @param $name
32
     * @return mixed
33
     */
34
    public function __get(string $name)
35
    {
36
        return array_key_exists($name, $this->data) ? $this->data[$name] : null;
37
    }
38
39
    public function __call(string $method, array $args = [])
40
    {
41
        $refresh = (bool) array_shift($args);
42
        return $this->getRelatedModel($method, $refresh);
43
    }
44
45
    private function getRelatedModel(string $name, bool $refresh = false)
46
    {
47
        $this->checkIfRelationIsSet($name);
48
        if ($refresh === false && array_key_exists($name, $this->relationships)) {
49
            return $this->relationships[$name];
50
        }
51
        return $this->populateRelation($name);
52
    }
53
54
    private function checkIfRelationIsSet(string $name): void
55
    {
56
        if (isset(static::$relations) === false ||
57
            array_key_exists($name, static::$relations) === false) {
58
            $class = get_class($this);
59
            throw new \Error("Call to undefined method {$class}::{$name}()");
60
        }
61
    }
62
63
    /**
64
     * @param string[] ...$relation_names any list of relations to return
65
     * @return object instance of class that has Entity trait
66
     */
67
    public function with(string ...$relation_names)
68
    {
69
        foreach ($relation_names as $name) {
70
            $this->populateRelation($name);
71
        }
72
        return $this;
73
    }
74
75
    /**
76
     * @param string $relation_name
77
     * @param array|object|null $data
78
     * @return array|object|null array in case of has_many
79
     * @throws \Exception
80
     */
81
    public function populateRelation(string $relation_name, $data = null)
82
    {
83
        if ($data !== null) {
84
            $this->relationships[$relation_name] = $data;
85
            return;
86
        }
87
        $relationship = $this->describeRelationship($relation_name);
88
        $relation_class = $relationship['class'];
89
        $relation_id = $this->{$relationship['local']};
90
        if ($relationship['type'] === 'belongs_to') {
91
            $related = $relation_class::get($relation_id);
92
        } elseif ($relationship['type'] === 'has_one') {
93
            $related_by = $relationship['foreign'];
94
            $result = $relation_class::findAsArray([$related_by => $relation_id], ['limit' => 1]);
95
            $related = $result ? current($result) : null;
96
        } elseif ($relationship['type'] === 'has_many') { // type must be has_many
97
            $related_by = $relationship['foreign'];
98
            $related = $relation_class::findAsArray([$related_by => $relation_id]);
99
        } else {
100
            throw new \Exception("Not a valid relationship type [" . $relationship['type'] . ']');
101
        }
102
        $this->relationships[$relation_name] = $related;
103
        return $related;
104
    }
105
106
    /**
107
     * @param string $name
108
     * @return array
109
     * @throws \Error if $name is for an unspecified relation
110
     */
111
    public function describeRelationship(string $name): array
112
    {
113
        $this->checkIfRelationIsSet($name);
114
        $settings = static::$relations[$name];
115
        if (sizeof($settings) === 1) {
116
            return $this->generateRelationshipFromShorthand($settings);
117
        }
118
        if (!array_key_exists('local', $settings)) {
119
            $settings['local'] = 'id';
120
        }
121
        if (!array_key_exists('foreign', $settings)) {
122
            $settings['foreign'] = 'id';
123
        }
124
        if (!array_key_exists('type', $settings)) {
125
            $settings['type'] = $settings['local'] === 'id' ? 'has_many' : 'belongs_to';
126
        }
127
128
        return $settings;
129
    }
130
131
    private function generateRelationshipFromShorthand(array $settings): array
132
    {
133
        $field = current($settings);
134
        $field_is_local = in_array($field, static::$fields); // @TODO hack to use Model data?
135
        if ($field_is_local) {
136
            return [
137
                'class' => key($settings),
138
                'type' => 'belongs_to',
139
                'local' => $field,
140
                'foreign' => 'id'
141
            ];
142
        } else {
143
            return [
144
                'class' => key($settings),
145
                'type' => 'has_many',
146
                'local' => 'id',
147
                'foreign' => $field
148
            ];
149
        }
150
    }
151
152
    /**
153
     * @param string $name
154
     * @param mixed $value
155
     */
156
    public function __set(string $name, $value): void
157
    {
158
        $this->data[$name] = $value;
159
    }
160
161
    /**
162
     * @param $name
163
     * @return bool
164
     */
165
    public function __isset(string $name): bool
166
    {
167
        return isset($this->data[$name]);
168
    }
169
170
    /**
171
     * @param array|null $data
172
     * @return array
173
     */
174
    public function data(array $data = null): array
175
    {
176
        if (is_null($data)) {
177
            return $this->data;
178
        }
179
        foreach ($data as $key => $value) {
180
            $this->data[$key] = $value;
181
        }
182
        return $this->data;
183
    }
184
185
    /**
186
     * Reset object by removing all data
187
     */
188
    public function reset(): void
189
    {
190
        $this->data = [];
191
    }
192
193
    /**
194
     * Cast the data array to $type and return this
195
     *
196
     * @param $type 'json', 'array'
197
     * @return mixed
198
     * @throws \InvalidArgumentException on unsupported type
199
     */
200
    public function to(string $type)
201
    {
202
        switch ($type) {
203
            case 'array':
204
                return $this->data;
205
            case 'json':
206
                return json_encode($this->data);
207
            default:
208
                throw new \InvalidArgumentException("Unkown type $type");
209
        }
210
    }
211
212
    /**
213
     * @return array
214
     */
215
    public function jsonSerialize(): array
216
    {
217
        return $this->data;
218
    }
219
}
220