Issues (19)

src/Registry.php (1 issue)

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