Passed
Push — 2.x ( b60830...92ccd4 )
by Aleksei
13:13
created

Registry::getDefaults()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
cc 1
eloc 1
c 0
b 0
f 0
nc 1
nop 0
dl 0
loc 3
rs 10
ccs 0
cts 0
cp 0
crap 2
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Cycle\Schema;
6
7
use Cycle\Schema\Definition\Entity;
8
use Cycle\Schema\Exception\RegistryException;
9
use Cycle\Schema\Exception\RelationException;
10
use Cycle\Database\DatabaseProviderInterface;
11
use Cycle\Database\Exception\DBALException;
12
use Cycle\Database\Schema\AbstractTable;
13
use Traversable;
14
15
final class Registry implements \IteratorAggregate
16
{
17
    /** @var Entity[] */
18
    private array $entities = [];
19
    private DatabaseProviderInterface $dbal;
20
    private \SplObjectStorage $tables;
21
    private \SplObjectStorage $children;
22
    private \SplObjectStorage $relations;
23
    private Defaults $defaults;
24
25
    /**
26
     * @param DatabaseProviderInterface $dbal
27 1366
     */
28
    public function __construct(DatabaseProviderInterface $dbal, ?Defaults $defaults = null)
29 1366
    {
30 1366
        $this->dbal = $dbal;
31 1366
        $this->tables = new \SplObjectStorage();
32 1366
        $this->children = new \SplObjectStorage();
33 1366
        $this->relations = new \SplObjectStorage();
34
        $this->defaults = $defaults ?? new Defaults();
35 1310
    }
36
37 1310
    public function register(Entity $entity): self
38 1194
    {
39 8
        foreach ($this->entities as $e) {
40
            if ($e->getRole() == $entity->getRole()) {
41
                throw new RegistryException("Duplicate entity `{$e->getRole()}`");
42
            }
43 1310
        }
44 1310
45 1310
        $this->entities[] = $entity;
46 1310
        $this->tables[$entity] = null;
47
        $this->children[$entity] = [];
48 1310
        $this->relations[$entity] = [];
49
50
        return $this;
51
    }
52
53
    /**
54 1032
     * @param string $role Entity role of class.
55
     */
56 1032
    public function hasEntity(string $role): bool
57 1032
    {
58 1008
        foreach ($this->entities as $entity) {
59
            if ($entity->getRole() === $role || $entity->getClass() === $role) {
60
                return true;
61
            }
62 40
        }
63
64
        return false;
65
    }
66
67
    /**
68
     * Get entity by it's role.
69
     *
70
     * @param string $role Entity role or class name.
71
     *
72 1192
     * @throws RegistryException
73
     */
74 1192
    public function getEntity(string $role): Entity
75 1184
    {
76 1184
        foreach ($this->entities as $entity) {
77
            if ($entity->getRole() == $role || $entity->getClass() === $role) {
78
                return $entity;
79
            }
80 8
        }
81
82
        throw new RegistryException("Undefined entity `{$role}`");
83
    }
84
85
    /**
86 1284
     * @return Entity[]|Traversable
87
     */
88 1284
    public function getIterator(): Traversable
89
    {
90
        return new \ArrayIterator($this->entities);
91
    }
92
93
    /**
94
     * Assign child entity to parent entity.
95
     * Be careful! This method merges the parent and child entity schemas.
96 16
     * If you don't need to merge schemas {@see Registry::registerChildWithoutMerge()}.
97
     *
98 16
     * @throws RegistryException
99 8
     */
100
    public function registerChild(Entity $parent, Entity $child): void
101
    {
102 8
        $this->registerChildWithoutMerge($parent, $child);
103 8
104 8
        // merge parent and child schema
105
        $parent->merge($child);
106
    }
107 8
108 8
    public function registerChildWithoutMerge(Entity $parent, Entity $child): void
109
    {
110
        if (!$this->hasInstance($parent)) {
111
            throw new RegistryException("Undefined entity `{$parent->getRole()}`");
112
        }
113
114
        $children = $this->children[$parent];
115
        $children[] = $child;
116
        $this->children[$parent] = $children;
117
    }
118
119
    /**
120
     * Get all assigned children entities.
121
     *
122
     * @return Entity[]
123
     */
124
    public function getChildren(Entity $entity): array
125
    {
126
        if (!$this->hasInstance($entity)) {
127
            throw new RegistryException("Undefined entity `{$entity->getRole()}`");
128
        }
129
130 1264
        return $this->children[$entity];
131
    }
132 1264
133 8
    /**
134
     * Associate entity with table.
135
     *
136 1256
     * @throws RegistryException
137
     * @throws DBALException
138 1256
     */
139 1256
    public function linkTable(Entity $entity, ?string $database, string $table): self
140 1256
    {
141
        if (!$this->hasInstance($entity)) {
142 1256
            throw new RegistryException("Undefined entity `{$entity->getRole()}`");
143 1256
        }
144
145
        $database = $this->dbal->database($database)->getName();
146
147 1176
        $schema = null;
148 56
        foreach ($this->tables as $other) {
149 56
            $association = $this->tables[$other];
150
151
            if ($association === null) {
152
                continue;
153 1256
            }
154 1256
155
            // avoid schema duplication
156
            if ($association['database'] === $database && $association['table'] === $table) {
157 1256
                $schema = $association['schema'];
158 1256
                break;
159 1256
            }
160 1256
        }
161
162
        if (null === $schema) {
0 ignored issues
show
introduced by
The condition null === $schema is always true.
Loading history...
163 1256
            $schema = $this->dbal->database($database)->table($table)->getSchema();
0 ignored issues
show
Bug introduced by
The method getSchema() does not exist on Cycle\Database\TableInterface. It seems like you code against a sub-type of said class. However, the method does not exist in Spiral\Database\TableInterface. Are you sure you never get one of those? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

163
            $schema = $this->dbal->database($database)->table($table)->/** @scrutinizer ignore-call */ getSchema();
Loading history...
164
        }
165
166
        $this->tables[$entity] = [
167
            'database' => $database,
168
            'table' => $table,
169 960
            'schema' => $schema,
170
        ];
171 960
172 32
        return $this;
173
    }
174
175 928
    /**
176
     * @throws RegistryException
177
     */
178
    public function hasTable(Entity $entity): bool
179
    {
180
        if (!$this->hasInstance($entity)) {
181 896
            throw new RegistryException("Undefined entity `{$entity->getRole()}`");
182
        }
183 896
184
        return $this->tables[$entity] !== null;
185
    }
186
187 888
    /**
188
     * @throws RegistryException
189
     */
190
    public function getDatabase(Entity $entity): string
191
    {
192
        if (!$this->hasTable($entity)) {
193 832
            throw new RegistryException("Entity `{$entity->getRole()}` has no assigned table");
194
        }
195 832
196
        return $this->tables[$entity]['database'];
197
    }
198
199 824
    /**
200
     * @throws RegistryException
201
     */
202
    public function getTable(Entity $entity): string
203
    {
204
        if (!$this->hasTable($entity)) {
205 416
            throw new RegistryException("Entity `{$entity->getRole()}` has no assigned table");
206
        }
207 416
208
        return $this->tables[$entity]['table'];
209
    }
210
211 408
    /**
212
     * @throws RegistryException
213
     */
214
    public function getTableSchema(Entity $entity): AbstractTable
215
    {
216
        if (!$this->hasTable($entity)) {
217
            throw new RegistryException("Entity `{$entity->getRole()}` has no assigned table");
218
        }
219
220 984
        return $this->tables[$entity]['schema'];
221
    }
222 984
223
    /**
224
     * Create entity relation.
225
     *
226 984
     * @throws RegistryException
227 984
     * @throws RelationException
228 984
     */
229 984
    public function registerRelation(Entity $entity, string $name, RelationInterface $relation): void
230
    {
231
        if (!$this->hasInstance($entity)) {
232
            throw new RegistryException("Undefined entity `{$entity->getRole()}`");
233
        }
234 392
235
        $relations = $this->relations[$entity];
236 392
        $relations[$name] = $relation;
237
        $this->relations[$entity] = $relations;
238
    }
239
240 392
    /**
241
     * @throws RegistryException
242
     */
243 392
    public function hasRelation(Entity $entity, string $name): bool
244
    {
245 392
        if (!$this->hasInstance($entity)) {
246
            throw new RegistryException("Undefined entity `{$entity->getRole()}`");
247
        }
248
249 392
        return isset($this->relations[$entity][$name]);
250
    }
251
252
    public function getRelation(Entity $entity, string $name): RelationInterface
253
    {
254
        if (!$this->hasRelation($entity, $name)) {
255
            throw new RegistryException("Undefined relation `{$entity->getRole()}`.`{$name}`");
256
        }
257 816
258
        return $this->relations[$entity][$name];
259 816
    }
260
261
    /**
262
     * Get all relations assigned with given entity.
263 816
     *
264
     * @return RelationInterface[]
265
     */
266 1320
    public function getRelations(Entity $entity): array
267
    {
268 1320
        if (!$this->hasInstance($entity)) {
269
            throw new RegistryException("Undefined entity `{$entity->getRole()}`");
270
        }
271
272
        return $this->relations[$entity];
273
    }
274
275
    public function getDefaults(): Defaults
276
    {
277
        return $this->defaults;
278
    }
279
280
    protected function hasInstance(Entity $entity): bool
281
    {
282
        return array_search($entity, $this->entities, true) !== false;
283
    }
284
}
285