Passed
Push — master ( fb7384...4df908 )
by Anton
01:34
created

Builder::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 4
nc 1
nop 1
dl 0
loc 6
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 Spiral\Database\DatabaseManager;
15
use Spiral\Database\Exception\DBALException;
16
use Spiral\Database\Schema\AbstractTable;
17
18
final class Builder implements \IteratorAggregate
19
{
20
    /** @var DatabaseManager */
21
    private $dbal;
22
23
    /** @var Entity[] */
24
    private $entities = [];
25
26
    /** @var \SplObjectStorage */
27
    private $tables;
28
29
    /** @var \SplObjectStorage */
30
    private $children;
31
32
    /** @var \SplObjectStorage */
33
    private $relations;
34
35
    /**
36
     * @param DatabaseManager $dbal
37
     */
38
    public function __construct(DatabaseManager $dbal)
39
    {
40
        $this->dbal = $dbal;
41
        $this->tables = new \SplObjectStorage();
42
        $this->children = new \SplObjectStorage();
43
        $this->relations = new \SplObjectStorage();
44
    }
45
46
    /**
47
     * @param Entity $entity
48
     */
49
    public function register(Entity $entity)
50
    {
51
        $this->entities[] = $entity;
52
        $this->tables[$entity] = null;
53
        $this->children[$entity] = [];
54
        $this->relations[$entity] = [];
55
    }
56
57
    /**
58
     * @param string $role
59
     * @return bool
60
     */
61
    public function hasRole(string $role): bool
62
    {
63
        foreach ($this->entities as $entity) {
64
            if ($entity->getRole() == $role) {
65
                return true;
66
            }
67
        }
68
69
        return false;
70
    }
71
72
    /**
73
     * @param Entity $entity
74
     * @return bool
75
     */
76
    public function hasEntity(Entity $entity): bool
77
    {
78
        return array_search($entity, $this->entities, true) !== false;
79
    }
80
81
    /**
82
     * Get entity by it's role.
83
     *
84
     * @param string $role
85
     * @return Entity
86
     *
87
     * @throws BuilderException
88
     */
89
    public function getEntity(string $role): Entity
90
    {
91
        foreach ($this->entities as $entity) {
92
            if ($entity->getRole() == $role || $entity->getClass() === $role) {
93
                return $entity;
94
            }
95
        }
96
97
        throw new BuilderException("Undefined entity `{$role}`");
98
    }
99
100
    /**
101
     * @return Entity[]|\Traversable
102
     */
103
    public function getIterator()
104
    {
105
        return $this->entities;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->entities returns the type Cycle\Schema\Definition\Entity[] which is incompatible with the return type mandated by IteratorAggregate::getIterator() of Traversable.

In the issue above, the returned value is violating the contract defined by the mentioned interface.

Let's take a look at an example:

interface HasName {
    /** @return string */
    public function getName();
}

class Name {
    public $name;
}

class User implements HasName {
    /** @return string|Name */
    public function getName() {
        return new Name('foo'); // This is a violation of the ``HasName`` interface
                                // which only allows a string value to be returned.
    }
}
Loading history...
106
    }
107
108
    /**
109
     * Assign child entity to parent entity.
110
     *
111
     * @param Entity $parent
112
     * @param Entity $child
113
     *
114
     * @throws BuilderException
115
     */
116
    public function registerChild(Entity $parent, Entity $child)
117
    {
118
        if (!$this->hasEntity($parent)) {
119
            throw new BuilderException("Undefined entity `{$parent->getRole()}`");
120
        }
121
122
        $this->children[$parent][] = $child;
123
124
        // merge parent and child schema
125
        $parent->merge($child);
126
    }
127
128
    /**
129
     * Get all assigned children entities.
130
     *
131
     * @param Entity $entity
132
     * @return array
133
     */
134
    public function getChildren(Entity $entity): array
135
    {
136
        if (!$this->hasEntity($entity)) {
137
            throw new BuilderException("Undefined entity `{$entity->getRole()}`");
138
        }
139
140
        return $this->children[$entity];
141
    }
142
143
    /**
144
     * Associate entity with table.
145
     *
146
     * @param Entity      $entity
147
     * @param string|null $database
148
     * @param string      $table
149
     *
150
     * @throws BuilderException
151
     * @throws DBALException
152
     */
153
    public function linkTable(Entity $entity, ?string $database, string $table)
154
    {
155
        if (!$this->hasEntity($entity)) {
156
            throw new BuilderException("Undefined entity `{$entity->getRole()}`");
157
        }
158
159
        $this->tables[$entity] = $this->dbal->database($database)->table($table)->getSchema();
160
    }
161
162
    /**
163
     * @param Entity $entity
164
     * @return bool
165
     *
166
     * @throws BuilderException
167
     */
168
    public function hasTable(Entity $entity): bool
169
    {
170
        if (!$this->hasEntity($entity)) {
171
            throw new BuilderException("Undefined entity `{$entity->getRole()}`");
172
        }
173
174
        return $this->tables[$entity] !== null;
175
    }
176
177
    /**
178
     * @param Entity $entity
179
     * @return AbstractTable
180
     *
181
     * @throws BuilderException
182
     */
183
    public function getTable(Entity $entity): AbstractTable
184
    {
185
        if (!$this->hasTable($entity)) {
186
            throw new BuilderException("Entity `{$entity->getRole()}` has no assigned table");
187
        }
188
189
        return $this->tables[$entity];
190
    }
191
192
    /**
193
     * Create entity relation.
194
     *
195
     * @param Entity            $entity
196
     * @param string            $name
197
     * @param RelationInterface $relation
198
     *
199
     * @throws BuilderException
200
     */
201
    public function registerRelation(Entity $entity, string $name, RelationInterface $relation)
202
    {
203
        if (!$this->hasEntity($entity)) {
204
            throw new BuilderException("Undefined entity `{$entity->getRole()}`");
205
        }
206
207
        $this->relations[$entity][$name] = $relation;
208
    }
209
210
    /**
211
     * @param Entity $entity
212
     * @param string $name
213
     * @return bool
214
     *
215
     * @throws BuilderException
216
     */
217
    public function hasRelation(Entity $entity, string $name): bool
218
    {
219
        if (!$this->hasEntity($entity)) {
220
            throw new BuilderException("Undefined entity `{$entity->getRole()}`");
221
        }
222
223
        return isset($this->relations[$entity][$name]);
224
    }
225
226
    /**
227
     * @param Entity $entity
228
     * @param string $name
229
     * @return RelationInterface
230
     */
231
    public function getRelation(Entity $entity, string $name): RelationInterface
232
    {
233
        if (!$this->hasRelation($entity, $name)) {
234
            throw new BuilderException("Undefined relation `{$entity->getRole()}`.`{$name}`");
235
        }
236
237
        return $this->relations[$entity][$name];
238
    }
239
240
    /**
241
     * Get all relations assigned with given entity.
242
     *
243
     * @param Entity $entity
244
     * @return array
245
     */
246
    public function getRelations(Entity $entity): array
247
    {
248
        if (!$this->hasEntity($entity)) {
249
            throw new BuilderException("Undefined entity `{$entity->getRole()}`");
250
        }
251
252
        return $this->relations[$entity];
253
    }
254
255
    /**
256
     * Iterate over all entities in order to fill missed data,
257
     * inverse relations and do other pre-calculations.
258
     *
259
     * @param VisitorInterface $visitor
260
     */
261
    public function compute(VisitorInterface $visitor)
262
    {
263
        foreach ($this->entities as $entity) {
264
            $visitor->compute($this, $entity);
265
        }
266
    }
267
268
    /**
269
     * Compile entity schema into packed schema representation.
270
     *
271
     * @return array
272
     */
273
    public function compile(): array
274
    {
275
276
    }
0 ignored issues
show
Bug Best Practice introduced by
In this branch, the function will implicitly return null which is incompatible with the type-hinted return array. Consider adding a return statement or allowing null as return value.

For hinted functions/methods where all return statements with the correct type are only reachable via conditions, ?null? gets implicitly returned which may be incompatible with the hinted type. Let?s take a look at an example:

interface ReturnsInt {
    public function returnsIntHinted(): int;
}

class MyClass implements ReturnsInt {
    public function returnsIntHinted(): int
    {
        if (foo()) {
            return 123;
        }
        // here: null is implicitly returned
    }
}
Loading history...
277
}