Issues (590)

src/Relations/EntityRelation.php (3 issues)

Labels
Severity
1
<?php
2
3
namespace Bdf\Prime\Relations;
4
5
use Bdf\Prime\Exception\PrimeException;
6
use Bdf\Prime\Query\Contract\ReadOperation;
7
use Bdf\Prime\Query\Contract\WriteOperation;
8
use Bdf\Prime\Query\QueryInterface;
9
use Bdf\Prime\Query\ReadCommandInterface;
10
11
/**
12
 * EntityRelation
13
 *
14
 * Relation wrapper. Link an entity to an relation object
15
 *
16
 * @template E as object
17
 * @template R as object
18
 *
19
 * @api
20
 *
21
 * @psalm-method QueryInterface<\Bdf\Prime\Connection\ConnectionInterface, R> with(string|string[] $relations) Relations to load
22
 * @psalm-method QueryInterface<\Bdf\Prime\Connection\ConnectionInterface, R> by(string $attribute, bool $combine = false) Indexing entities by an attribute value. Use combine for multiple entities with same attribute value
23
 * @psalm-method QueryInterface<\Bdf\Prime\Connection\ConnectionInterface, R> where(string|array|callable $column, mixed|null $operator = null, mixed $value = null)
24
 * @psalm-method R|null get($pk) Get one entity by its identifier
25
 * @psalm-method R getOrFail($pk) Get one entity or throws when entity is not found
26
 * @psalm-method R getOrNew($pk) Get one entity or return a new one if not found in repository
27
 * @psalm-method int count()
28
 *
29
 * @mixin ReadCommandInterface<\Bdf\Prime\Connection\ConnectionInterface, R>
30
 *
31
 * @noinspection PhpHierarchyChecksInspection
32
 */
33
class EntityRelation
34
{
35
    /**
36
     * The entity owner of the relation
37
     *
38
     * @var E
39
     */
40
    protected $owner;
41
42
    /**
43
     * The relation
44
     *
45
     * @var RelationInterface<E, R>
46
     */
47
    protected $relation;
48
49
    /**
50
     * EntityRelation constructor.
51
     *
52
     * @param E $owner     The relation owner
53
     * @param RelationInterface<E, R> $relation  The relation
54
     */
55 166
    public function __construct($owner, RelationInterface $relation)
56
    {
57 166
        $this->owner    = $owner;
58 166
        $this->relation = $relation;
59
    }
60
61
    /**
62
     * Associate an entity to the owner entity
63
     *
64
     * This method will set the foreign key value on the owner entity and attach the entity
65
     * If the relation is detached, only the foreign key will be updated
66
     *
67
     * Note: This method will not perform any write operation on database :
68
     *       the related entity id must be generated before, and the owner must be updated manually
69
     *
70
     * <code>
71
     * $entity->relation('foo')->associate($foo);
72
     * $entity->getFoo() === $foo; // should be true
73
     * </code>
74
     *
75
     * Only foreign key barrier can associate an entity
76
     *
77
     * @param R $entity The related entity data
78
     *
79
     * @return E Returns the owner entity instance
80
     */
81 16
    public function associate($entity)
82
    {
83 16
        return $this->relation->associate($this->owner, $entity);
84
    }
85
86
    /**
87
     * Remove the relation from owner entity
88
     * This is the reverse operation of associate : will detach the related entity and set the foreign key to null
89
     *
90
     * Note: This method will not perform any write operation on database :
91
     *       the related entity id must be generated before, and the owner must be updated manually
92
     *
93
     * Only foreign key barrier can dissociate an entity
94
     *
95
     * @return E Returns the owner entity instance
96
     */
97 17
    public function dissociate()
98
    {
99 17
        return $this->relation->dissociate($this->owner);
100
    }
101
102
    /**
103
     * Add a relation entity on the given entity owner
104
     * This method will not attach the created entity to the owner
105
     *
106
     * Note: This method will not perform any write operation on database, it will only instantiate the entity
107
     *
108
     * <code>
109
     * $foo = $entity->relation('foo')->create(['foo' => 'bar']);
110
     * $foo->getOwnerId() === $entity->id(); // Should be true
111
     * $foo->getFoo() === 'bar'; // Should be true
112
     * </code>
113
     *
114
     * Only non foreign key barrier can create an entity
115
     *
116
     * @param array $data The related entity data
117
     *
118
     * @return R Returns the related entity instance
119
     */
120 6
    public function create(array $data = [])
121
    {
122 6
        return $this->relation->create($this->owner, $data);
123
    }
124
125
    /**
126
     * Add a relation entity on the given entity owner
127
     * This method will set the foreign key value on the related entity but not attach the related entity to the owner
128
     *
129
     * Only non foreign key barrier can add an entity
130
     *
131
     * <code>
132
     * $entity->relation('foo')->($entity, $foo);
133
     * $foo->getOwnerId() === $entity->id(); // Should be true
134
     * </code>
135
     *
136
     * @param R $related
137
     *
138
     * @return int
139
     * @throws PrimeException
140
     */
141
    #[WriteOperation]
142
    public function add($related): int
143
    {
144
        return $this->relation->add($this->owner, $related);
145
    }
146
147
    /**
148
     * Check whether the owner has a distant entity relation
149
     * Note: only works with BelongsToMany relation
150
     *
151
     * @param string|R $related
152
     *
153
     * @return boolean
154
     * @throws PrimeException
155
     */
156
    #[ReadOperation]
157 3
    public function has($related): bool
158
    {
159
        /** @var BelongsToMany $this->relation */
160 3
        return $this->relation->has($this->owner, $related);
0 ignored issues
show
The method has() does not exist on Bdf\Prime\Relations\RelationInterface. It seems like you code against a sub-type of Bdf\Prime\Relations\RelationInterface such as Bdf\Prime\Relations\BelongsToMany. ( Ignorable by Annotation )

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

160
        return $this->relation->/** @scrutinizer ignore-call */ has($this->owner, $related);
Loading history...
161
    }
162
163
    /**
164
     * Attach a distant entity to an entity
165
     * Note: only works with BelongsToMany relation
166
     *
167
     * @param string|R[]|R $related
168
     *
169
     * @return int
170
     * @throws PrimeException
171
     */
172
    #[WriteOperation]
173 3
    public function attach($related): int
174
    {
175
        /** @var BelongsToMany<E, R> $this->relation */
176 3
        return $this->relation->attach($this->owner, $related);
0 ignored issues
show
The method attach() does not exist on Bdf\Prime\Relations\RelationInterface. It seems like you code against a sub-type of Bdf\Prime\Relations\RelationInterface such as Bdf\Prime\Relations\BelongsToMany. ( Ignorable by Annotation )

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

176
        return $this->relation->/** @scrutinizer ignore-call */ attach($this->owner, $related);
Loading history...
177
    }
178
179
    /**
180
     * Detach a distant entity of an entity
181
     * Note: only works with BelongsToMany relation
182
     *
183
     * @param string|R[]|R $related
184
     *
185
     * @return int
186
     */
187 3
    public function detach($related): int
188
    {
189
        /** @var BelongsToMany<E, R> $this->relation */
190 3
        return $this->relation->detach($this->owner, $related);
0 ignored issues
show
The method detach() does not exist on Bdf\Prime\Relations\RelationInterface. It seems like you code against a sub-type of Bdf\Prime\Relations\RelationInterface such as Bdf\Prime\Relations\BelongsToMany. ( Ignorable by Annotation )

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

190
        return $this->relation->/** @scrutinizer ignore-call */ detach($this->owner, $related);
Loading history...
191
    }
192
193
    /**
194
     * Gets the relation query builder
195
     * Note: You can use magic __call method to call query methods directly
196
     *
197
     * <code>
198
     * $entity->relation('foo')->where(['foo' => 'bar'])->get();
199
     * </code>
200
     *
201
     * @return ReadCommandInterface<\Bdf\Prime\Connection\ConnectionInterface, R>
202
     */
203 35
    public function query(): ReadCommandInterface
204
    {
205 35
        return $this->relation->link($this->owner);
206
    }
207
208
    /**
209
     * Save the relation from an entity
210
     *
211
     * Note: This method can only works with attached entities
212
     *
213
     * @param string|array $relations sub-relation names to save
214
     *
215
     * @return int Number of updated / inserted entities
216
     * @throws PrimeException When cannot save entity
217
     */
218
    #[WriteOperation]
219 14
    public function saveAll($relations = []): int
220
    {
221 14
        return $this->relation->saveAll($this->owner, (array)$relations);
222
    }
223
224
    /**
225
     * Remove the relation from an entity
226
     *
227
     * Note: This method can only works
228
     *
229
     * @param array $relations sub-relation names to delete
230
     *
231
     * @return int Number of deleted entities
232
     * @throws PrimeException When cannot delete entity
233
     */
234
    #[WriteOperation]
235 12
    public function deleteAll($relations = []): int
236
    {
237 12
        return $this->relation->deleteAll($this->owner, (array)$relations);
238
    }
239
240
    /**
241
     * Check if the relation is loaded on the current entity
242
     *
243
     * @return bool
244
     */
245 86
    public function isLoaded(): bool
246
    {
247 86
        return $this->relation->isLoaded($this->owner);
248
    }
249
250
    /**
251
     * Redirect every call to the relation query Builder
252
     *
253
     * @param string $name
254
     * @param array $arguments
255
     *
256
     * @return mixed
257
     */
258 31
    public function __call(string $name, array $arguments)
259
    {
260 31
        return $this->query()->$name(...$arguments);
261
    }
262
}
263