Passed
Push — master ( 9bffc1...46953a )
by Anton
01:43
created

Registry::run()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 7
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 3
nc 2
nop 1
dl 0
loc 7
rs 10
c 0
b 0
f 0
1
<?php
2
declare(strict_types=1);
3
/**
4
 * Spiral Framework.
5
 *
6
 * @license   MIT
7
 * @author    Anton Titov (Wolfy-J)
8
 */
9
10
namespace Cycle\Schema;
11
12
use Cycle\Schema\Definition\Entity;
13
use Cycle\Schema\Exception\BuilderException;
14
use Cycle\Schema\Exception\RelationException;
15
use Spiral\Database\DatabaseManager;
16
use Spiral\Database\Exception\DBALException;
17
use Spiral\Database\Schema\AbstractTable;
18
19
final class Registry implements \IteratorAggregate
20
{
21
    /** @var DatabaseManager */
22
    private $dbal;
23
24
    /** @var Entity[] */
25
    private $entities = [];
26
27
    /** @var \SplObjectStorage */
28
    private $tables;
29
30
    /** @var \SplObjectStorage */
31
    private $children;
32
33
    /** @var \SplObjectStorage */
34
    private $relations;
35
36
    /**
37
     * @param DatabaseManager $dbal
38
     */
39
    public function __construct(DatabaseManager $dbal)
40
    {
41
        $this->dbal = $dbal;
42
        $this->tables = new \SplObjectStorage();
43
        $this->children = new \SplObjectStorage();
44
        $this->relations = new \SplObjectStorage();
45
    }
46
47
    /**
48
     * @param Entity $entity
49
     * @return Registry
50
     */
51
    public function register(Entity $entity): Registry
52
    {
53
        $this->entities[] = $entity;
54
        $this->tables[$entity] = null;
55
        $this->children[$entity] = [];
56
        $this->relations[$entity] = [];
57
58
        return $this;
59
    }
60
61
    /**
62
     * @param string $role
63
     * @return bool
64
     */
65
    public function hasRole(string $role): bool
66
    {
67
        foreach ($this->entities as $entity) {
68
            if ($entity->getRole() == $role) {
69
                return true;
70
            }
71
        }
72
73
        return false;
74
    }
75
76
    /**
77
     * @param Entity $entity
78
     * @return bool
79
     */
80
    public function hasEntity(Entity $entity): bool
81
    {
82
        return array_search($entity, $this->entities, true) !== false;
83
    }
84
85
    /**
86
     * Get entity by it's role.
87
     *
88
     * @param string $role Entity role or class name.
89
     * @return Entity
90
     *
91
     * @throws BuilderException
92
     */
93
    public function getEntity(string $role): Entity
94
    {
95
        foreach ($this->entities as $entity) {
96
            if ($entity->getRole() == $role || $entity->getClass() === $role) {
97
                return $entity;
98
            }
99
        }
100
101
        throw new BuilderException("Undefined entity `{$role}`");
102
    }
103
104
    /**
105
     * @return Entity[]|\Traversable
106
     */
107
    public function getIterator()
108
    {
109
        return new \ArrayIterator($this->entities);
110
    }
111
112
    /**
113
     * Assign child entity to parent entity.
114
     *
115
     * @param Entity $parent
116
     * @param Entity $child
117
     *
118
     * @throws BuilderException
119
     */
120
    public function registerChild(Entity $parent, Entity $child)
121
    {
122
        if (!$this->hasEntity($parent)) {
123
            throw new BuilderException("Undefined entity `{$parent->getRole()}`");
124
        }
125
126
        $children = $this->children[$parent];
127
        $children[] = $child;
128
        $this->children[$parent] = $children;
129
130
        // merge parent and child schema
131
        $parent->merge($child);
132
    }
133
134
    /**
135
     * Get all assigned children entities.
136
     *
137
     * @param Entity $entity
138
     * @return Entity[]
139
     */
140
    public function getChildren(Entity $entity): array
141
    {
142
        if (!$this->hasEntity($entity)) {
143
            throw new BuilderException("Undefined entity `{$entity->getRole()}`");
144
        }
145
146
        return $this->children[$entity];
147
    }
148
149
    /**
150
     * Associate entity with table.
151
     *
152
     * @param Entity      $entity
153
     * @param string|null $database
154
     * @param string      $table
155
     * @return Registry
156
     *
157
     * @throws BuilderException
158
     * @throws DBALException
159
     */
160
    public function linkTable(Entity $entity, ?string $database, string $table): Registry
161
    {
162
        if (!$this->hasEntity($entity)) {
163
            throw new BuilderException("Undefined entity `{$entity->getRole()}`");
164
        }
165
166
        $this->tables[$entity] = [
167
            'database' => $database,
168
            'table'    => $table,
169
            'schema'   => $this->dbal->database($database)->table($table)->getSchema()
170
        ];
171
172
        return $this;
173
    }
174
175
    /**
176
     * @param Entity $entity
177
     * @return bool
178
     *
179
     * @throws BuilderException
180
     */
181
    public function hasTable(Entity $entity): bool
182
    {
183
        if (!$this->hasEntity($entity)) {
184
            throw new BuilderException("Undefined entity `{$entity->getRole()}`");
185
        }
186
187
        return $this->tables[$entity] !== null;
188
    }
189
190
    /**
191
     * @param Entity $entity
192
     * @return string
193
     *
194
     * @throws BuilderException
195
     */
196
    public function getDatabase(Entity $entity): string
197
    {
198
        if (!$this->hasTable($entity)) {
199
            throw new BuilderException("Entity `{$entity->getRole()}` has no assigned table");
200
        }
201
202
        return $this->tables[$entity]['database'];
203
    }
204
205
    /**
206
     * @param Entity $entity
207
     * @return string
208
     *
209
     * @throws BuilderException
210
     */
211
    public function getTable(Entity $entity): string
212
    {
213
        if (!$this->hasTable($entity)) {
214
            throw new BuilderException("Entity `{$entity->getRole()}` has no assigned table");
215
        }
216
217
        return $this->tables[$entity]['table'];
218
    }
219
220
    /**
221
     * @param Entity $entity
222
     * @return AbstractTable
223
     *
224
     * @throws BuilderException
225
     */
226
    public function getTableSchema(Entity $entity): AbstractTable
227
    {
228
        if (!$this->hasTable($entity)) {
229
            throw new BuilderException("Entity `{$entity->getRole()}` has no assigned table");
230
        }
231
232
        return $this->tables[$entity]['schema'];
233
    }
234
235
    /**
236
     * Create entity relation.
237
     *
238
     * @param Entity            $entity
239
     * @param string            $name
240
     * @param RelationInterface $relation
241
     *
242
     * @throws BuilderException
243
     * @throws RelationException
244
     */
245
    public function registerRelation(Entity $entity, string $name, RelationInterface $relation)
246
    {
247
        if (!$this->hasEntity($entity)) {
248
            throw new BuilderException("Undefined entity `{$entity->getRole()}`");
249
        }
250
251
        $relations = $this->relations[$entity];
252
        $relations[$name] = $relation;
253
        $this->relations[$entity] = $relations;
254
    }
255
256
    /**
257
     * @param Entity $entity
258
     * @param string $name
259
     * @return bool
260
     *
261
     * @throws BuilderException
262
     */
263
    public function hasRelation(Entity $entity, string $name): bool
264
    {
265
        if (!$this->hasEntity($entity)) {
266
            throw new BuilderException("Undefined entity `{$entity->getRole()}`");
267
        }
268
269
        return isset($this->relations[$entity][$name]);
270
    }
271
272
    /**
273
     * @param Entity $entity
274
     * @param string $name
275
     * @return RelationInterface
276
     */
277
    public function getRelation(Entity $entity, string $name): RelationInterface
278
    {
279
        if (!$this->hasRelation($entity, $name)) {
280
            throw new BuilderException("Undefined relation `{$entity->getRole()}`.`{$name}`");
281
        }
282
283
        return $this->relations[$entity][$name];
284
    }
285
286
    /**
287
     * Get all relations assigned with given entity.
288
     *
289
     * @param Entity $entity
290
     * @return RelationInterface[]
291
     */
292
    public function getRelations(Entity $entity): array
293
    {
294
        if (!$this->hasEntity($entity)) {
295
            throw new BuilderException("Undefined entity `{$entity->getRole()}`");
296
        }
297
298
        return $this->relations[$entity];
299
    }
300
301
    /**
302
     * Iterate over all entities in order to fill missed data,
303
     * inverse relations and do other pre-calculations.
304
     *
305
     * @param GeneratorInterface $generator
306
     * @return Registry
307
     */
308
    public function iterate(GeneratorInterface $generator): Registry
309
    {
310
        foreach ($this->entities as $entity) {
311
            $generator->compute($this, $entity);
312
        }
313
314
        return $this;
315
    }
316
}