Passed
Pull Request — master (#29)
by Sébastien
08:57
created

BelongsToMany::match()   A

Complexity

Conditions 6
Paths 7

Size

Total Lines 17
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 9
CRAP Score 6.0359

Importance

Changes 0
Metric Value
eloc 10
dl 0
loc 17
ccs 9
cts 10
cp 0.9
rs 9.2222
c 0
b 0
f 0
cc 6
nc 7
nop 2
crap 6.0359
1
<?php
2
3
namespace Bdf\Prime\Relations;
4
5
use Bdf\Prime\Exception\PrimeException;
6
use Bdf\Prime\Query\Contract\Deletable;
7
use Bdf\Prime\Query\Contract\EntityJoinable;
8
use Bdf\Prime\Query\Contract\ReadOperation;
9
use Bdf\Prime\Query\Contract\WriteOperation;
10
use Bdf\Prime\Query\Custom\KeyValue\KeyValueQuery;
11
use Bdf\Prime\Query\QueryInterface;
12
use Bdf\Prime\Query\ReadCommandInterface;
13
use Bdf\Prime\Repository\EntityRepository;
14
use Bdf\Prime\Repository\RepositoryInterface;
15
16
/**
17
 * BelongsToMany
18
 *
19
 * For a relation named 'relation' use the prefix 'relationThrough.' for adding constraints on through table.
20
 * ex:
21
 *
22
 * <code>
23
 * $query->with([
24
 *     'relation' => [
25
 *         'name :like'             => '...',
26
 *         'relationThrough.status' => '...'  // through constraint
27
 *     ]
28
 * ]);
29
 * </code>
30
 *
31
 * @package Bdf\Prime\Relations
32
 *
33
 * @todo Voir pour gérer la table de through dynamiquement. Si cette relation est une HasManyThrough, elle doit etre en readonly
0 ignored issues
show
Coding Style introduced by
Comment refers to a TODO task

This check looks TODO comments that have been left in the code.

``TODO``s show that something is left unfinished and should be attended to.

Loading history...
34
 *
35
 * @template L as object
36
 * @template R as object
37
 *
38
 * @extends Relation<L, R>
39
 */
40
class BelongsToMany extends Relation
41
{
42
    /**
43
     * Through repository
44
     *
45
     * @var EntityRepository
46
     */
47
    protected $through;
48
49
    /**
50
     * Through local key
51
     *
52
     * @var string
53
     */
54
    protected $throughLocal;
55
56
    /**
57
     * Through distant key
58
     *
59
     * @var string
60
     */
61
    protected $throughDistant;
62
63
    /**
64
     * The through global constraints
65
     *
66
     * @var array
67
     */
68
    protected $throughConstraints = [];
69
70
    /**
71
     * Merge of all constraints
72
     *
73
     * @var array
74
     */
75
    protected $allConstraints = [];
76
    
77
    /**
78
     * {@inheritdoc}
79
     */
0 ignored issues
show
Coding Style Documentation introduced by
Missing @var tag in member variable comment
Loading history...
80
    protected $saveStrategy = self::SAVE_STRATEGY_REPLACE;
81
82
    //===============================
0 ignored issues
show
Coding Style introduced by
No space found before comment text; expected "// ===============================" but found "//==============================="
Loading history...
83
    // Save queries for optimisation
84
    //===============================
0 ignored issues
show
Coding Style introduced by
No space found before comment text; expected "// ===============================" but found "//==============================="
Loading history...
85
86
    /**
87
     * @var KeyValueQuery
88
     */
89
    private $throughQuery;
0 ignored issues
show
Coding Style introduced by
Expected 1 blank line(s) before member var; 5 found
Loading history...
90
91
    /**
92
     * @var KeyValueQuery
93
     */
94
    private $relationQuery;
95
96
97
    /**
98
     * {@inheritdoc}
99
     */
100 10
    public function relationRepository(): RepositoryInterface
0 ignored issues
show
Coding Style introduced by
Expected 1 blank line before function; 2 found
Loading history...
101
    {
102 10
        return $this->distant;
103
    }
104
105
    /**
106
     * Set the though infos
107
     *
108
     * @param RepositoryInterface $through
109
     * @param string $throughLocal
110
     * @param string $throughDistant
111
     */
112 4
    public function setThrough(RepositoryInterface $through, string $throughLocal, string $throughDistant)
113
    {
114 4
        $this->through        = $through;
0 ignored issues
show
Documentation Bug introduced by
$through is of type Bdf\Prime\Repository\RepositoryInterface, but the property $through was declared to be of type Bdf\Prime\Repository\EntityRepository. Are you sure that you always receive this specific sub-class here, or does it make sense to add an instanceof check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a given class or a super-class is assigned to a property that is type hinted more strictly.

Either this assignment is in error or an instanceof check should be added for that assignment.

class Alien {}

class Dalek extends Alien {}

class Plot
{
    /** @var  Dalek */
    public $villain;
}

$alien = new Alien();
$plot = new Plot();
if ($alien instanceof Dalek) {
    $plot->villain = $alien;
}
Loading history...
115 4
        $this->throughLocal   = $throughLocal;
116 4
        $this->throughDistant = $throughDistant;
117 4
    }
118
119
    /**
0 ignored issues
show
Coding Style introduced by
Parameter $constraints should have a doc-comment as per coding-style.
Loading history...
120
     * {@inheritdoc}
121
     */
122
    public function setConstraints($constraints)
123
    {
124
        $this->allConstraints = $constraints;
125
126
        list($this->constraints, $this->throughConstraints) = $this->extractConstraints($constraints);
127
128
        return $this;
129
    }
130
131
    /**
132
     * Extract constraints design for through queries
133
     *
134
     * @param array|\Closure $constraints
135
     *
136
     * @return array
137
     */
138 11
    protected function extractConstraints($constraints)
139
    {
140 11
        if (!is_array($constraints)) {
141
            return [$constraints, []];
142
        }
143
144 11
        $through = [];
145 11
        $global = [];
146 11
        $prefix = $this->attributeAim.'Through.';
147 11
        $length = strlen($prefix);
148
149 11
        foreach ($constraints as $column => $value) {
150 2
            if (strpos($column, $prefix) === 0) {
151 1
                $through[substr($column, $length)] = $value;
152
            } else {
153 2
                $global[$column] = $value;
154
            }
155
        }
156
157 11
        return [$global, $through];
158
    }
159
160
    /**
0 ignored issues
show
Coding Style introduced by
Parameter $alias should have a doc-comment as per coding-style.
Loading history...
Coding Style introduced by
Parameter $query should have a doc-comment as per coding-style.
Loading history...
161
     * {@inheritdoc}
162
     */
163 1
    public function join(EntityJoinable $query, string $alias): void
164
    {
165
        // @fixme ??
0 ignored issues
show
Coding Style introduced by
Comment refers to a FIXME task "??"
Loading history...
166
//        if ($alias === null) {
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected at least 8 spaces, found 0
Loading history...
introduced by
Comment indentation error, expected only 1 spaces
Loading history...
Coding Style introduced by
Expected 1 space before comment text but found 8; use block comment if you need indentation
Loading history...
167
//            $alias = $this->attributeAim;
0 ignored issues
show
introduced by
Comment indentation error, expected only 8 spaces
Loading history...
Coding Style introduced by
Expected 1 space before comment text but found 12; use block comment if you need indentation
Loading history...
Coding Style introduced by
Line indented incorrectly; expected at least 8 spaces, found 0
Loading history...
168
//        }
0 ignored issues
show
introduced by
There must be no blank line following an inline comment
Loading history...
Coding Style introduced by
Line indented incorrectly; expected at least 8 spaces, found 0
Loading history...
Coding Style introduced by
Expected 1 space before comment text but found 8; use block comment if you need indentation
Loading history...
169
170
        // TODO rechercher l'alias de through dans les tables alias du query builder
0 ignored issues
show
Coding Style Best Practice introduced by
Comments for TODO tasks are often forgotten in the code; it might be better to use a dedicated issue tracker.
Loading history...
introduced by
There must be no blank line following an inline comment
Loading history...
171
172 1
        $query->joinEntity($this->through->entityName(), $this->throughLocal, $this->getLocalAlias($query).$this->localKey, $this->attributeAim.'Through');
173 1
        $query->joinEntity($this->distant->entityName(), $this->distantKey, $this->attributeAim.'Through>'.$this->throughDistant, $alias);
174
175 1
        $this->applyConstraints($query, [], $alias);
176 1
        $this->applyThroughConstraints($query, [], $this->attributeAim.'Through');
177 1
    }
178
179
    /**
0 ignored issues
show
Coding Style introduced by
Parameter $alias should have a doc-comment as per coding-style.
Loading history...
Coding Style introduced by
Parameter $query should have a doc-comment as per coding-style.
Loading history...
Coding Style introduced by
Parameter $discriminator should have a doc-comment as per coding-style.
Loading history...
180
     * {@inheritdoc}
181
     */
182 1
    public function joinRepositories(EntityJoinable $query, string $alias, $discriminator = null): array
0 ignored issues
show
Coding Style introduced by
The method parameter $query is never used
Loading history...
Coding Style introduced by
The method parameter $discriminator is never used
Loading history...
183
    {
184
        return [
185 1
            $this->attributeAim.'Through' => $this->through,
186 1
            $alias => $this->distant,
187
        ];
188
    }
189
190
    /**
0 ignored issues
show
Coding Style introduced by
Parameter $owner should have a doc-comment as per coding-style.
Loading history...
191
     * {@inheritdoc}
192
     */
193 7
    public function link($owner): ReadCommandInterface
194
    {
195
        /** @var QueryInterface&EntityJoinable $query */
0 ignored issues
show
Coding Style introduced by
The open comment tag must be the only content on the line
Loading history...
Coding Style introduced by
Block comments must be started with /*
Loading history...
Coding Style introduced by
Inline doc block comments are not allowed; use "/* Comment */" or "// Comment" instead
Loading history...
Coding Style introduced by
The close comment tag must be the only content on the line
Loading history...
196 7
        $query = $this->distant->queries()->builder();
197
198
        return $query
199 7
            ->joinEntity($this->through->entityName(), $this->throughDistant, $this->distantKey, $this->attributeAim.'Through')
0 ignored issues
show
Coding Style introduced by
Space found before object operator
Loading history...
introduced by
Object operator not indented correctly; expected 10 spaces but found 12
Loading history...
Bug introduced by
The method joinEntity() does not exist on Bdf\Prime\Query\QueryInterface. Since it exists in all sub-types, consider adding an abstract or default implementation to Bdf\Prime\Query\QueryInterface. ( Ignorable by Annotation )

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

199
            ->/** @scrutinizer ignore-call */ joinEntity($this->through->entityName(), $this->throughDistant, $this->distantKey, $this->attributeAim.'Through')
Loading history...
200 7
            ->where($this->attributeAim.'Through.'.$this->throughLocal, $this->getLocalKeyValue($owner))
0 ignored issues
show
Coding Style introduced by
Space found before object operator
Loading history...
201 7
            ->where($this->allConstraints)
0 ignored issues
show
Coding Style introduced by
Space found before object operator
Loading history...
Coding Style introduced by
Space after closing parenthesis of function call prohibited
Loading history...
202
        ;
0 ignored issues
show
Coding Style introduced by
Space found before semicolon; expected ");" but found ")
;"
Loading history...
203
    }
204
205
    /**
206
     * Get a query from through entity repository
207
     *
208
     * @param string|array  $key
209
     * @param array $constraints
210
     *
211
     * @return ReadCommandInterface&Deletable
0 ignored issues
show
introduced by
Expected "ReadCommandInterfaceDeletable" but found "ReadCommandInterface&Deletable" for function return type
Loading history...
212
     */
213 14
    protected function throughQuery($key, $constraints = []): ReadCommandInterface
0 ignored issues
show
introduced by
Type hint "array" missing for $constraints
Loading history...
214
    {
215 14
        if (is_array($key)) {
216 11
            if (count($key) !== 1 || $constraints || $this->throughConstraints) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->throughConstraints of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
Bug Best Practice introduced by
The expression $constraints of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
217 1
                return $this->applyThroughConstraints(
218 1
                    $this->through->where($this->throughLocal, $key),
219 1
                    $constraints
220
                );
221
            }
222
223 10
            $key = $key[0];
224
        }
225
226 13
        if ($this->throughQuery) {
227 10
            return $this->throughQuery->where($this->throughLocal, $key);
228
        }
229
230 3
        $this->throughQuery = $this->through->queries()->keyValue($this->throughLocal, $key);
231
232 3
        if ($this->throughQuery) {
233 3
            return $this->throughQuery;
234
        }
235
236
        return $this->applyThroughConstraints(
237
            $this->through->where($this->throughLocal, $key),
238
            $constraints
239
        );
240
    }
241
242
    /**
0 ignored issues
show
Coding Style introduced by
Parameter $constraints should have a doc-comment as per coding-style.
Loading history...
Coding Style introduced by
Parameter $keys should have a doc-comment as per coding-style.
Loading history...
243
     * Build the query for find related entities
244
     */
245 11
    protected function relationQuery($keys, $constraints): ReadCommandInterface
246
    {
247
        // Constraints can be on relation attributes : builder must be used
248
        // @todo Handle "bulk select"
0 ignored issues
show
Coding Style Best Practice introduced by
Comments for TODO tasks are often forgotten in the code; it might be better to use a dedicated issue tracker.
Loading history...
249 11
        if (count($keys) !== 1 || $constraints || $this->constraints) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->constraints of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
250 8
            return $this->query($keys, $constraints)->by($this->distantKey);
0 ignored issues
show
Bug introduced by
The method by() does not exist on Bdf\Prime\Query\QueryInterface. It seems like you code against a sub-type of said class. However, the method does not exist in Bdf\Prime\Query\SqlQueryInterface. 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

250
            return $this->query($keys, $constraints)->/** @scrutinizer ignore-call */ by($this->distantKey);
Loading history...
251
        }
252
253 3
        if ($this->relationQuery) {
254 1
            return $this->relationQuery->where($this->distantKey, reset($keys));
255
        }
256
257 2
        $query = $this->distant->queries()->keyValue($this->distantKey, reset($keys));
258
259 2
        if (!$query) {
0 ignored issues
show
introduced by
$query is of type Bdf\Prime\Query\Contract...\KeyValueQueryInterface, thus it always evaluated to true.
Loading history...
260
            return $this->query($keys, $constraints)->by($this->distantKey);
261
        }
262
263 2
        return $this->relationQuery = $query->by($this->distantKey);
0 ignored issues
show
Bug introduced by
The method by() does not exist on Bdf\Prime\Query\Contract...\KeyValueQueryInterface. Since it exists in all sub-types, consider adding an abstract or default implementation to Bdf\Prime\Query\Contract...\KeyValueQueryInterface. ( Ignorable by Annotation )

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

263
        return $this->relationQuery = $query->/** @scrutinizer ignore-call */ by($this->distantKey);
Loading history...
Coding Style introduced by
Assignments must be the first block of code on a line
Loading history...
264
    }
265
266
    /**
267
     * Apply the through constraints
268
     *
269
     * @param Q $query
0 ignored issues
show
Bug introduced by
The type Bdf\Prime\Relations\Q was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
270
     * @param array $constraints
271
     * @param string|null $context
272
     *
273
     * @return Q
274
     *
275
     * @template Q as ReadCommandInterface<\Bdf\Prime\Connection\ConnectionInterface, object>&\Bdf\Prime\Query\Contract\Whereable
276
     */
277 2
    protected function applyThroughConstraints(ReadCommandInterface $query, $constraints = [], ?string $context = null): ReadCommandInterface
0 ignored issues
show
introduced by
Type hint "array" missing for $constraints
Loading history...
278
    {
279 2
        return $query->where($this->applyContext($context, $constraints + $this->throughConstraints));
0 ignored issues
show
Coding Style introduced by
Operation must be bracketed
Loading history...
Bug introduced by
The method where() does not exist on Bdf\Prime\Query\ReadCommandInterface. Since it exists in all sub-types, consider adding an abstract or default implementation to Bdf\Prime\Query\ReadCommandInterface. ( Ignorable by Annotation )

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

279
        return $query->/** @scrutinizer ignore-call */ where($this->applyContext($context, $constraints + $this->throughConstraints));
Loading history...
280
    }
281
282
    /**
283
     * {@inheritdoc}
284
     */
285
    #[ReadOperation]
286 11
    protected function relations($keys, $with, $constraints, $without): array
287
    {
288 11
        list($constraints, $throughConstraints) = $this->extractConstraints($constraints);
289
290 11
        $throughEntities = [];
291 11
        $throughDistants = [];
292
293 11
        $collection = $this->throughQuery($keys, $throughConstraints)->execute([
294 11
            $this->throughLocal   => $this->throughLocal,
295 11
            $this->throughDistant => $this->throughDistant,
296
        ]);
297
298 11
        foreach ($collection as $entity) {
299 11
            $throughLocal   = $entity[$this->throughLocal];
300 11
            $throughDistant = $entity[$this->throughDistant];
301
302 11
            $throughDistants[$throughDistant] = $throughDistant;
303 11
            $throughEntities[$throughLocal][$throughDistant] = $throughDistant;
304
        }
305
306 11
        $relations = $this->relationQuery($throughDistants, $constraints)
307 11
            ->with($with)
0 ignored issues
show
Coding Style introduced by
Space found before object operator
Loading history...
Bug introduced by
The method with() does not exist on Bdf\Prime\Query\Custom\KeyValue\KeyValueQuery. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

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

307
            ->/** @scrutinizer ignore-call */ with($with)
Loading history...
introduced by
Object operator not indented correctly; expected 10 spaces but found 12
Loading history...
308 11
            ->without($without)
0 ignored issues
show
Coding Style introduced by
Space found before object operator
Loading history...
309 11
            ->all();
0 ignored issues
show
Coding Style introduced by
Space found before object operator
Loading history...
310
311
        return [
312 11
            'throughEntities' => $throughEntities,
313 11
            'entities'        => $relations,
314
        ];
315
    }
316
317
    /**
0 ignored issues
show
Coding Style introduced by
Parameter $relations should have a doc-comment as per coding-style.
Loading history...
Coding Style introduced by
Parameter $collection should have a doc-comment as per coding-style.
Loading history...
318
     * {@inheritdoc}
319
     */
320 11
    protected function match($collection, $relations): void
321
    {
322 11
        foreach ($relations['throughEntities'] as $key => $throughDistants) {
323 11
            $entities = [];
324
325 11
            foreach ($throughDistants as $throughDistant) {
326 11
                if (isset($relations['entities'][$throughDistant])) {
327 11
                    $entities[] = $relations['entities'][$throughDistant];
328
                }
329
            }
330
331 11
            if (empty($entities)) {
332
                continue;
333
            }
334
335 11
            foreach ($collection[$key] as $local) {
336 11
                $this->setRelation($local, $entities);
337
            }
338
        }
339 11
    }
340
341
    /**
342
     * {@inheritdoc}
343
     *
344
     * @throws PrimeException
345
     */
346
    #[WriteOperation]
347 1
    public function associate($owner, $entity)
348
    {
349 1
        $this->attach($owner, $entity);
350
351 1
        return $owner;
352
    }
353
354
    /**
355
     * {@inheritdoc}
356
     *
357
     * @throws PrimeException
358
     */
359
    #[WriteOperation]
360 2
    public function dissociate($owner)
361
    {
362 2
        $this->detach($owner, $this->getRelation($owner));
363
364 2
        return $owner;
365
    }
366
367
    /**
0 ignored issues
show
Coding Style introduced by
Parameter $data should have a doc-comment as per coding-style.
Loading history...
Coding Style introduced by
Parameter $owner should have a doc-comment as per coding-style.
Loading history...
368
     * {@inheritdoc}
369
     *
370
     * @throws PrimeException
371
     */
372 1
    public function create($owner, array $data = [])
373
    {
374 1
        $entity = $this->distant->entity($data);
375
376 1
        $this->distant->save($entity);
377
378 1
        $this->add($owner, $entity);
379
380 1
        return $entity;
381
    }
382
383
    /**
384
     * {@inheritdoc}
385
     */
386
    #[WriteOperation]
387 1
    public function add($owner, $related)
388
    {
389 1
        return $this->attach($owner, $related);
390
    }
391
392
    /**
393
     * {@inheritdoc}
394
     */
395
    #[WriteOperation]
396 3
    public function saveAll($owner, array $relations = []): int
0 ignored issues
show
Coding Style introduced by
The method parameter $relations is never used
Loading history...
397
    {
398
        //Detach all relations
0 ignored issues
show
Coding Style introduced by
No space found before comment text; expected "// Detach all relations" but found "//Detach all relations"
Loading history...
399 3
        if ($this->saveStrategy === self::SAVE_STRATEGY_REPLACE) {
400 3
            $this->throughQuery($this->getLocalKeyValue($owner))->delete();
401
        }
402
        
403
        // Attach new relations
404 3
        return $this->attach($owner, $this->getRelation($owner));
405
    }
406
407
    /**
408
     * {@inheritdoc}
409
     */
410
    #[WriteOperation]
411 2
    public function deleteAll($owner, array $relations = []): int
0 ignored issues
show
Coding Style introduced by
The method parameter $relations is never used
Loading history...
412
    {
413 2
        return $this->detach($owner, $this->getRelation($owner));
414
    }
415
416
    /**
417
     * Check whether the owner has a distant entity relation
418
     *
419
     * @param L $owner
0 ignored issues
show
Bug introduced by
The type Bdf\Prime\Relations\L was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
420
     * @param string|R $entity
421
     *
422
     * @return boolean
423
     * @throws PrimeException
424
     */
425
    #[ReadOperation]
426 1
    public function has($owner, $entity): bool
427
    {
428 1
        $data = [$this->throughLocal => $this->getLocalKeyValue($owner)];
429
430 1
        if (!is_object($entity)) {
431
            $data[$this->throughDistant] = $entity;
432
        } else {
433 1
            $data[$this->throughDistant] = $this->getDistantKeyValue($entity);
434
        }
435
436 1
        return $this->through->exists($this->through->entity($data));
437
    }
438
439
    /**
440
     * Attach a distant entity to an entity
441
     *
442
     * @param L $owner
443
     * @param string|R[]|R $entities
444
     *
445
     * @return int
446
     * @throws PrimeException
447
     */
448
    #[WriteOperation]
449 6
    public function attach($owner, $entities): int
450
    {
451 6
        if (empty($entities)) {
452
            return 0;
453
        }
454
455 6
        $ownerId = $this->getLocalKeyValue($owner);
456
457 6
        if (!is_array($entities)) {
458 3
            $entities = [$entities];
459
        }
460
461 6
        $nb = 0;
462
463 6
        foreach ($entities as $entity) {
464
            // distant could be a object or the distant id
0 ignored issues
show
Coding Style Documentation introduced by
Inline comments must start with a capital letter
Loading history...
465 6
            $data = [$this->throughLocal => $ownerId];
466
467 6
            if (!is_object($entity)) {
468
                $data[$this->throughDistant] = $entity;
469
            } else {
470 6
                $data[$this->throughDistant] = $this->getDistantKeyValue($entity);
471
            }
472
473 6
            $nb += $this->through->save($this->through->entity($data));
474
        }
475
476 6
        return $nb;
477
    }
478
479
    /**
480
     * Detach a distant entity of an entity
481
     *
482
     * @param L $owner
483
     * @param string|R[]|R $entities
484
     *
485
     * @return int
486
     * @throws PrimeException
487
     */
488
    #[WriteOperation]
489 5
    public function detach($owner, $entities): int
490
    {
491 5
        if (empty($entities)) {
492
            return 0;
493
        }
494
495 5
        $ownerId = $this->getLocalKeyValue($owner);
496
497 5
        if (!is_array($entities)) {
498 1
            $entities = [$entities];
499
        }
500
501 5
        $nb = 0;
502
503 5
        foreach ($entities as $entity) {
504
            // distant could be a object or the distant id
0 ignored issues
show
Coding Style Documentation introduced by
Inline comments must start with a capital letter
Loading history...
505 5
            $data = [$this->throughLocal => $ownerId];
506
507 5
            if (!is_object($entity)) {
508
                $data[$this->throughDistant] = $entity;
509
            } else {
510 5
                $data[$this->throughDistant] = $this->getDistantKeyValue($entity);
511
            }
512
513 5
            $nb += $this->through->delete($this->through->entity($data));
514
        }
515
516 5
        return $nb;
517
    }
518
}
519