1 | <?php |
||||
2 | |||||
3 | declare(strict_types=1); |
||||
4 | |||||
5 | namespace Cycle\ORM\Relation; |
||||
6 | |||||
7 | use Cycle\ORM\Exception\RelationException; |
||||
8 | use Cycle\ORM\Heap\Node; |
||||
9 | use Cycle\ORM\Heap\State; |
||||
10 | use Cycle\ORM\ORMInterface; |
||||
11 | use Cycle\ORM\Reference\ReferenceInterface; |
||||
12 | use Cycle\ORM\Service\MapperProviderInterface; |
||||
13 | use Cycle\ORM\Relation; |
||||
14 | use Cycle\ORM\SchemaInterface; |
||||
15 | |||||
16 | /** |
||||
17 | * @internal |
||||
18 | */ |
||||
19 | abstract class AbstractRelation implements ActiveRelationInterface, \Stringable |
||||
20 | { |
||||
21 | /** |
||||
22 | * Additional target roles: class-name of the primary role, roles and classes of primary role parents if the primary |
||||
23 | * role has parents |
||||
24 | * |
||||
25 | * @var class-string[]|string[] |
||||
0 ignored issues
–
show
Documentation
Bug
introduced
by
![]() |
|||||
26 | */ |
||||
27 | protected array $targets = []; |
||||
28 | |||||
29 | /** @var string[] */ |
||||
30 | protected array $innerKeys; |
||||
31 | |||||
32 | /** @var string[] */ |
||||
33 | protected array $outerKeys; |
||||
34 | |||||
35 | protected ?string $inversion; |
||||
36 | protected MapperProviderInterface $mapperProvider; |
||||
37 | protected SchemaInterface $ormSchema; |
||||
38 | |||||
39 | /** |
||||
40 | * @param string $target Primary target role |
||||
41 | */ |
||||
42 | public function __construct( |
||||
43 | 5100 | ORMInterface $orm, |
|||
44 | private string $role, |
||||
45 | protected string $name, |
||||
46 | protected string $target, |
||||
47 | protected array $schema, |
||||
48 | ) { |
||||
49 | $this->ormSchema = $orm->getSchema(); |
||||
50 | 5100 | $this->mapperProvider = $orm->getService(MapperProviderInterface::class); |
|||
51 | 5100 | $this->innerKeys = (array) $schema[Relation::INNER_KEY]; |
|||
52 | 5100 | $this->outerKeys = (array) $schema[Relation::OUTER_KEY]; |
|||
53 | 5100 | $this->inversion = $schema[Relation::INVERSION] ?? null; |
|||
54 | 5100 | } |
|||
55 | |||||
56 | public function getInnerKeys(): array |
||||
57 | 1718 | { |
|||
58 | return $this->innerKeys; |
||||
59 | 1718 | } |
|||
60 | |||||
61 | public function getName(): string |
||||
62 | 40 | { |
|||
63 | return $this->name; |
||||
64 | } |
||||
65 | 40 | ||||
66 | public function getTarget(): string |
||||
67 | { |
||||
68 | 2772 | return $this->target; |
|||
69 | } |
||||
70 | 2772 | ||||
71 | public function isCascade(): bool |
||||
72 | { |
||||
73 | return $this->schema[Relation::CASCADE] ?? false; |
||||
74 | } |
||||
75 | |||||
76 | public function __toString(): string |
||||
77 | { |
||||
78 | 1968 | // this is incorrect class |
|||
79 | return \sprintf('`%s` (%s)->%s', $this->name, $this::class, $this->target); |
||||
80 | 1968 | } |
|||
81 | |||||
82 | protected function isNullable(): bool |
||||
83 | 912 | { |
|||
84 | // return $this->schema[Relation::NULLABLE] ?? false; |
||||
85 | return !empty($this->schema[Relation::NULLABLE]); |
||||
86 | 912 | } |
|||
87 | |||||
88 | protected function getTargetRelationName(): string |
||||
89 | 1138 | { |
|||
90 | return $this->inversion ?? $this->role . '.' . $this->name . ':' . $this->target; |
||||
91 | 1138 | } |
|||
92 | |||||
93 | /** |
||||
94 | * Assert that given entity is allowed for the relation. |
||||
95 | * |
||||
96 | * @throws RelationException |
||||
97 | */ |
||||
98 | protected function assertValid(Node $related): void |
||||
99 | 1146 | { |
|||
100 | if ($related->getRole() === $this->target || \in_array($related->getRole(), $this->targets, true)) { |
||||
101 | 1146 | return; |
|||
102 | 1146 | } |
|||
103 | $role = $this->ormSchema->resolveAlias($related->getRole()); |
||||
104 | 8 | if ($role === $this->target) { |
|||
105 | 8 | $this->targets[] = $related->getRole(); |
|||
106 | return; |
||||
107 | } |
||||
108 | // Check parents |
||||
109 | do { |
||||
110 | $parent = $this->ormSchema->define($role, SchemaInterface::PARENT); |
||||
0 ignored issues
–
show
It seems like
$role can also be of type null ; however, parameter $role of Cycle\ORM\SchemaInterface::define() does only seem to accept string , maybe add an additional type check?
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||
111 | 8 | if ($parent === $this->target) { |
|||
112 | 8 | $this->targets[] = $related->getRole(); |
|||
113 | 8 | return; |
|||
114 | 8 | } |
|||
115 | $role = $parent; |
||||
116 | } while ($parent !== null); |
||||
117 | throw new RelationException(\sprintf('Unable to link %s, given `%s`.', (string) $this, $related->getRole())); |
||||
118 | } |
||||
119 | |||||
120 | protected function registerWaitingFields(State $state, bool $required = true): void |
||||
121 | 786 | { |
|||
122 | foreach ($this->innerKeys as $key) { |
||||
123 | 786 | $state->waitField($key, $required); |
|||
124 | 786 | } |
|||
125 | } |
||||
126 | |||||
127 | protected function compareReferences(ReferenceInterface $original, mixed $related): bool |
||||
128 | 690 | { |
|||
129 | if ($original === $related) { |
||||
130 | 690 | return true; |
|||
131 | 506 | } |
|||
132 | if ($related instanceof ReferenceInterface) { |
||||
133 | 328 | return $related->getScope() === $original->getScope(); |
|||
134 | 72 | } |
|||
135 | return false; |
||||
136 | 256 | } |
|||
137 | } |
||||
138 |