Completed
Push — master ( e4d56c...8e58c0 )
by Ivan
02:47
created

Mapper.php$0 ➔ flatten()   A

Complexity

Conditions 1

Size

Total Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 6
ccs 4
cts 4
cp 1
rs 10
c 0
b 0
f 0
cc 1
crap 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 6
    public function __construct(DBInterface $db)
16
    {
17 6
        $this->db = $db;
18 6
    }
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 54
    public function entity(Table $definition, array $data, bool $empty = false)
28
    {
29 54
        $primary = [];
30 54
        if (!$empty) {
31 54
            foreach ($definition->getPrimaryKey() as $column) {
32 54
                $primary[$column] = $data[$column];
33
            }
34 54
            if (isset($this->objects[$definition->getName()][base64_encode(serialize($primary))])) {
35 54
                return $this->objects[$definition->getName()][base64_encode(serialize($primary))];
36
            }
37
        }
38 29
        $entity = new Entity($this, $definition, $data);
39 29
        if ($empty) {
40 3
            return $entity;
41
        }
42 27
        $this->lazy($entity, $data);
43 27
        return $this->objects[$definition->getName()][base64_encode(serialize($primary))] = $entity;
44
    }
45
    /**
46
     * Get a collection of entities
47
     *
48
     * @param TableQueryIterator $iterator
49
     * @param Table $definition
50
     * @return Collection
51
     */
52 54
    public function collection(TableQueryIterator $iterator, Table $definition) : Collection
53
    {
54 54
        return Collection::from($iterator)
55 54
            ->map(function ($v) use ($definition) {
56 54
                return $this->entity($definition, $v);
57 54
            });
58
    }
59
    /**
60
     * Persist all changes to an entity in the DB. Does not include modified relation collections.
61
     *
62
     * @param Entity $entity
63
     * @return object
64
     */
65 9
    public function save(Entity $entity)
66
    {
67 9
        $query = $this->db->table($entity->definition()->getName());
68 9
        $primary = $entity->id();
69 9
        if (!isset($this->objects[$entity->definition()->getName()][base64_encode(serialize($primary))])) {
70 3
            $new = $query->insert($entity->toArray());
71 3
            $entity->fromArray($new);
72 3
            $this->objects[$entity->definition()->getName()][base64_encode(serialize($new))] = $entity;
73
        } else {
74 6
            foreach ($primary as $k => $v) {
75 6
                $query->filter($k, $v);
76
            }
77 6
            $query->update($entity->toArray());
78 6
            $new = [];
79 6
            foreach ($primary as $k => $v) {
80 6
                $new[$k] = $entity->{$k};
81
            }
82 6
            if (base64_encode(serialize($new)) !== base64_encode(serialize($primary))) {
83 3
                unset($this->objects[$entity->definition()->getName()][base64_encode(serialize($primary))]);
84 3
                $this->objects[$entity->definition()->getName()][base64_encode(serialize($new))] = $entity;
85
            }
86
        }
87 9
        return $this->lazy($entity, $entity->toArray());
88
    }
89
    /**
90
     * Delete an entity from the database
91
     *
92
     * @param Entity $entity
93
     * @return void
94
     */
95 3
    public function delete(Entity $entity)
96
    {
97 3
        $query = $this->db->table($entity->definition()->getName());
98 3
        $primary = $entity->id();
99 3
        if (isset($this->objects[$entity->definition()->getName()][base64_encode(serialize($primary))])) {
100 3
            foreach ($primary as $k => $v) {
101 3
                $query->filter($k, $v);
102
            }
103 3
            $query->delete();
104 3
            unset($this->objects[$entity->definition()->getName()][base64_encode(serialize($primary))]);
105
        }
106 3
    }
107
    /**
108
     * Refresh an entity from the DB (includes own columns and relations).
109
     *
110
     * @param Entity $entity
111
     * @return object
112
     */
113
    public function refresh(Entity $entity)
114
    {
115
        $query = $this->db->table($entity->definition()->getName());
116
        $primary = $entity->id();
117
        foreach ($primary as $k => $v) {
118
            $query->filter($k, $v);
119
        }
120
        $data = $query[0] ?? [];
121
        $entity->fromArray($data);
122
        return $this->lazy($entity, $data);
123
    }
124 33
    protected function lazy(Entity $entity, $data)
125
    {
126 33
        $primary = $entity->id();
127 33
        $definition = $entity->definition();
128 33
        foreach ($definition->getColumns() as $column) {
129 33
            if (!array_key_exists($column, $data)) {
130
                $entity->__lazyProperty($column, function () use ($definition, $primary, $column) {
131
                    $query = $this->db->table($definition->getName());
132
                    foreach ($primary as $k => $v) {
133
                        $query->filter($k, $v);
134
                    }
135
                    return $query->select([$column])[0][$column] ?? null;
136
                });
137
            }
138
        }
139 33
        foreach ($definition->getRelations() as $name => $relation) {
140 33
            $entity->__lazyProperty(
141 33
                $name,
142 33
                array_key_exists($name, $data) && isset($data[$name]) ?
143
                    ($relation->many ?
144
                        array_map(function ($v) use ($relation) {
145
                            return $this->entity($relation->table, $v);
146
                        }, $data[$name]) :
147
                        $this->entity($relation->table, $data[$name])
148
                    ) :
149 33
                    function (array $columns = null) use ($entity, $definition, $relation, $data) {
150 9
                        $query = $this->db->table($relation->table->getName(), true);
151 9
                        if ($columns !== null) {
152
                            $query->columns($columns);
153
                        }
154 9
                        if ($relation->sql) {
155
                            $query->where($relation->sql, $relation->par);
156
                        }
157 9
                        if ($relation->pivot) {
158 6
                            $nm = null;
159 6
                            foreach ($relation->table->getRelations() as $rname => $rdata) {
160 6
                                if ($rdata->pivot && $rdata->pivot->getName() === $relation->pivot->getName()) {
161 6
                                    $nm = $rname;
162
                                }
163
                            }
164 6
                            if (!$nm) {
165
                                $nm = $definition->getName();
166
                                $relation->table->manyToMany(
167
                                    $this->db->table($definition->getName()),
168
                                    $relation->pivot,
169
                                    $nm,
170
                                    array_flip($relation->keymap),
171
                                    $relation->pivot_keymap
172
                                );
173
                            }
174 6
                            foreach ($definition->getPrimaryKey() as $v) {
175 6
                                $query->filter($nm . '.' . $v, $data[$v] ?? null);
176
                            }
177
                        } else {
178 9
                            foreach ($relation->keymap as $k => $v) {
179 9
                                $query->filter($v, $entity->{$k} ?? null);
180
                            }
181
                        }
182 9
                        return $relation->many ?
183 9
                            $query->iterator() :
184 9
                            $query[0];
185 33
                    }
186
            );
187
        }
188 33
        return $entity;
189
    }
190
}
191