Completed
Push — master ( 18247e...9315b5 )
by Ivan
03:45
created

Mapper::delete()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 12

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 9
CRAP Score 3

Importance

Changes 0
Metric Value
dl 0
loc 12
ccs 9
cts 9
cp 1
rs 9.8666
c 0
b 0
f 0
cc 3
crap 3
nc 3
nop 1
1
<?php
2
namespace vakata\database\schema;
3
4
use vakata\collection\Collection;
5
use vakata\database\DBInterface;
6
7
/**
8
 * A basic mapper to enable relation traversing and basic create / update / delete functionality
9
 */
10
class Mapper
11
{
12
    protected $db;
13
    protected $objects;
14
15 8
    public function __construct(DBInterface $db)
16
    {
17 8
        $this->db = $db;
18 8
    }
19
    /**
20
     * Create an entity from an array of data
21
     *
22
     * @param Table $definition
23
     * @param array $data
24
     * @param boolean $empty
25
     * @return object
26
     */
27 72
    public function entity(Table $definition, array $data, bool $empty = false)
28
    {
29 72
        $primary = [];
30 72
        if (!$empty) {
31 72
            foreach ($definition->getPrimaryKey() as $column) {
32 72
                $primary[$column] = $data[$column];
33
            }
34 72
            if (isset($this->objects[$definition->getName()][base64_encode(serialize($primary))])) {
35 72
                return $this->objects[$definition->getName()][base64_encode(serialize($primary))];
36
            }
37
        }
38
        $entity = new class ($this, $definition, $data) extends \StdClass {
39
            protected $mapper;
40
            protected $empty;
41
            protected $definition;
42
            protected $initial = [];
43
            protected $changed = [];
44
            protected $fetched = [];
45
46 38
            public function __construct($mapper, $definition, array $data = [])
47
            {
48 38
                $this->mapper = $mapper;
49 38
                $this->definition = $definition;
50 38
                $this->initial = $data;
51 38
            }
52 44
            public function __lazyProperty(string $property, $resolve)
53
            {
54 44
                $this->fetched[$property] = $resolve;
55 44
                return $this;
56
            }
57 68
            public function &__get($property)
58
            {
59 68
                if (isset($this->fetched[$property])) {
60 32
                    if (is_callable($this->fetched[$property])) {
61 12
                        $this->fetched[$property] = call_user_func($this->fetched[$property]);
62
                    }
63 32
                    return $this->fetched[$property];
64
                }
65 68
                if (isset($this->changed[$property])) {
66 4
                    return $this->changed[$property];
67
                }
68 68
                if (isset($this->initial[$property])) {
69 68
                    return $this->initial[$property];
70
                }
71
                $null = null;
72
                return $null;
73
            }
74 12
            public function __set($property, $value)
75
            {
76 12
                $this->changed[$property] = $value;
77 12
            }
78
            public function __call($method, $args)
79
            {
80
                if (isset($this->definition->getRelations()[$method])) {
81
                    if (isset($this->fetched[$method])) {
82
                        return is_callable($this->fetched[$method]) ?
83
                            $this->fetched[$method] = call_user_func($this->fetched[$method], $args[0] ?? null) :
84
                            $this->fetched[$method];
85
                    }
86
                }
87
                return null;
88
            }
89 48
            public function definition()
90
            {
91 48
                return $this->definition;
92
            }
93 12
            public function toArray(bool $fetch = false)
94
            {
95 12
                $data = [];
96 12
                foreach ($this->definition->getColumns() as $k) {
97 12
                    if (isset($this->fetched[$k])) {
98
                        if ($fetch) {
99
                            $this->fetched[$k] = call_user_func($this->fetched[$k]);
100
                        }
101
                        if (!is_callable($this->fetched[$k])) {
102
                            $data[$k] = $this->fetched[$k];
103
                        }
104
                    }
105 12
                    if (isset($this->initial[$k])) {
106 8
                        $data[$k] = $this->initial[$k];
107
                    }
108 12
                    if (isset($this->changed[$k])) {
109 12
                        $data[$k] = $this->changed[$k];
110
                    }
111
                }
112 12
                return $data;
113
            }
114 4
            public function fromArray(array $data)
115
            {
116 4
                foreach ($this->definition->getColumns() as $k) {
117 4
                    if (isset($data[$k])) {
118 4
                        $this->changed[$k] = $data[$k];
119
                    }
120
                }
121 4
                return $this;
122
            }
123 48
            public function id()
124
            {
125 48
                $primary = [];
126 48
                foreach ($this->definition->getPrimaryKey() as $k) {
127 48
                    $primary[$k] = $this->initial[$k] ?? null;
128
                }
129 48
                return $primary;
130
            }
131 12
            public function save()
132
            {
133 12
                $this->mapper->save($this);
134 12
                return $this->flatten();
135
            }
136 4
            public function delete()
137
            {
138 4
                $this->mapper->delete($this);
139 4
            }
140
            public function refresh()
141
            {
142
                $this->mapper->refresh($this);
143
                return $this->flatten();
144
            }
145 12
            public function flatten()
146
            {
147 12
                $this->initial = $this->toArray();
148 12
                $this->changed = [];
149 12
                return $this;
150
            }
151
        };
152 38
        if ($empty) {
153 4
            return $entity;
154
        }
155 35
        $this->lazy($entity, $data);
156 35
        return $this->objects[$definition->getName()][base64_encode(serialize($primary))] = $entity;
157
    }
158
    /**
159
     * Get a collection of entities
160
     *
161
     * @param TableQuery $iterator
162
     * @param Table $definition
163
     * @return Collection
164
     */
165 72
    public function collection(TableQueryIterator $iterator, Table $definition) : Collection
166
    {
167 72
        return Collection::from($iterator)
168 72
            ->map(function ($v) use ($definition) {
169 72
                return $this->entity($definition, $v);
170 72
            });
171
    }
172
    /**
173
     * Persist all changes to an entity in the DB. Does not include modified relation collections.
174
     *
175
     * @param object $entity
176
     * @return object
177
     */
178 12
    public function save($entity)
179
    {
180 12
        $query = $this->db->table($entity->definition()->getName());
181 12
        $primary = $entity->id();
182 12
        if (!isset($this->objects[$entity->definition()->getName()][base64_encode(serialize($primary))])) {
183 4
            $new = $query->insert($entity->toArray());
184 4
            $entity->fromArray($new);
185 4
            $this->objects[$entity->definition()->getName()][base64_encode(serialize($new))] = $entity;
186
        } else {
187 8
            foreach ($primary as $k => $v) {
188 8
                $query->filter($k, $v);
189
            }
190 8
            $query->update($entity->toArray());
191 8
            $new = [];
192 8
            foreach ($primary as $k => $v) {
193 8
                $new[$k] = $entity->{$k};
194
            }
195 8
            if (base64_encode(serialize($new)) !== base64_encode(serialize($primary))) {
196 4
                unset($this->objects[$entity->definition()->getName()][base64_encode(serialize($primary))]);
197 4
                $this->objects[$entity->definition()->getName()][base64_encode(serialize($new))] = $entity;
198
            }
199
        }
200 12
        return $this->lazy($entity, $entity->toArray());
201
    }
202
    /**
203
     * Delete an entity from the database
204
     *
205
     * @param object $entity
206
     * @return void
207
     */
208 4
    public function delete($entity)
209
    {
210 4
        $query = $this->db->table($entity->definition()->getName());
211 4
        $primary = $entity->id();
212 4
        if (isset($this->objects[$entity->definition()->getName()][base64_encode(serialize($primary))])) {
213 4
            foreach ($primary as $k => $v) {
214 4
                $query->filter($k, $v);
215
            }
216 4
            $query->delete();
217 4
            unset($this->objects[$entity->definition()->getName()][base64_encode(serialize($primary))]);
218
        }
219 4
    }
220
    /**
221
     * Refresh an entity from the DB (includes own columns and relations).
222
     *
223
     * @param object $entity
224
     * @return object
225
     */
226
    public function refresh($entity)
227
    {
228
        $query = $this->db->table($entity->definition()->getName());
229
        $primary = $entity->id();
230
        foreach ($primary as $k => $v) {
231
            $query->filter($k, $v);
232
        }
233
        $data = $query[0] ?? [];
234
        $entity->fromArray($data);
235
        return $this->lazy($entity, $data);
236
    }
237 44
    protected function lazy($entity, $data)
238
    {
239 44
        $primary = $entity->id();
240 44
        $definition = $entity->definition();
241 44
        foreach ($definition->getColumns() as $column) {
242 44
            if (!isset($data[$column])) {
243 44
                $entity->__lazyProperty($column, function () use ($entity, $definition, $primary, $column) {
244
                    $query = $this->db->table($definition->getName());
245
                    foreach ($primary as $k => $v) {
246
                        $query->filter($k, $v);
247
                    }
248
                    return $query->select([$column])[0][$column] ?? null;
249 44
                });
250
            }
251
        }
252 44
        foreach ($definition->getRelations() as $name => $relation) {
253 44
            $entity->__lazyProperty(
254 44
                $name,
255 44
                isset($data[$name]) ?
256
                    ($relation->many ? 
257
                        array_map(function ($v) use ($relation) {
258
                            return $this->entity($relation->table, $v);
259
                        }, $data[$name]) :
260
                        $this->entity($relation->table, $data[$name])
261
                    ) :
262 44
                    function (array $columns = null) use ($entity, $definition, $primary, $relation, $data) {
263 12
                        $query = $this->db->table($relation->table->getName(), true);
264 12
                        if ($columns !== null) {
265
                            $query->columns($columns);
266
                        }
267 12
                        if ($relation->sql) {
268
                            $query->where($relation->sql, $relation->par);
269
                        }
270 12
                        if ($relation->pivot) {
271 8
                            $nm = null;
272 8
                            foreach ($relation->table->getRelations() as $rname => $rdata) {
273 8
                                if ($rdata->pivot && $rdata->pivot->getName() === $relation->pivot->getName()) {
274 8
                                    $nm = $rname;
275
                                }
276
                            }
277 8
                            if (!$nm) {
278
                                $nm = $definition->getName();
279
                                $relation->table->manyToMany(
280
                                    $this->db->table($definition->getName()),
281
                                    $relation->pivot,
282
                                    $nm,
283
                                    array_flip($relation->keymap),
284
                                    $relation->pivot_keymap
285
                                );
286
                            }
287 8
                            foreach ($definition->getPrimaryKey() as $v) {
288 8
                                $query->filter($nm . '.' . $v, $data[$v] ?? null);
289
                            }
290
                        } else {
291 12
                            foreach ($relation->keymap as $k => $v) {
292 12
                                $query->filter($v, $entity->{$k} ?? null);
293
                            }
294
                        }
295 12
                        return $relation->many ?
296 12
                            $query->iterator() :
297 12
                            $query[0];
298 44
                    }
299
            );
300
        }
301 44
        return $entity;
302
    }
303
}