Issues (188)

src/RelationMap.php (3 issues)

1
<?php
2
3
declare(strict_types=1);
4
5
namespace Cycle\ORM;
6
7
use Cycle\ORM\Heap\Node;
8
use Cycle\ORM\Relation\ActiveRelationInterface;
9
use Cycle\ORM\Relation\DependencyInterface;
10
use Cycle\ORM\Relation\RelationInterface;
11
use Cycle\ORM\Relation\SameRowRelationInterface;
12
use Cycle\ORM\Relation\ShadowBelongsTo;
13
use Cycle\ORM\Relation\ShadowHasMany;
14
use Cycle\ORM\Service\EntityFactoryInterface;
15
16
/**
17
 * Manages the position of node in the relation graph and provide access to neighbours.
18
 *
19
 * @internal
20
 */
21
final class RelationMap
22
{
23
    /** @var ActiveRelationInterface[] */
24
    private array $innerRelations;
25
26
    /** @var DependencyInterface[] */
27
    private array $dependencies = [];
28
29
    /** @var RelationInterface[] */
30
    private array $slaves = [];
31
32
    /** @var SameRowRelationInterface[] */
33 6878
    private array $embedded = [];
34
35 6878
    private function __construct(array $innerRelations, array $outerRelations)
0 ignored issues
show
The parameter $outerRelations is not used and could be removed. ( Ignorable by Annotation )

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

35
    private function __construct(array $innerRelations, /** @scrutinizer ignore-unused */ array $outerRelations)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
36
    {
37 6878
        $this->innerRelations = $innerRelations;
38 5364
39 1760
        foreach ($innerRelations as $name => $relation) {
40 4650
            if ($relation instanceof DependencyInterface) {
41 344
                $this->dependencies[$name] = $relation;
42
            } elseif ($relation instanceof SameRowRelationInterface) {
43 4386
                $this->embedded[$name] = $relation;
44
            } else {
45
                $this->slaves[$name] = $relation;
46
            }
47
        }
48 6878
    }
49
50 6878
    public static function build(OrmInterface $orm, string $role): self
51 6878
    {
52
        $factory = $orm->getFactory();
53 6878
        $schema = $orm->getSchema();
54 6878
55
        $outerRelations = $schema->getOuterRelations($role);
0 ignored issues
show
The method getOuterRelations() does not exist on Cycle\ORM\SchemaInterface. Since it exists in all sub-types, consider adding an abstract or default implementation to Cycle\ORM\SchemaInterface. ( Ignorable by Annotation )

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

55
        /** @scrutinizer ignore-call */ 
56
        $outerRelations = $schema->getOuterRelations($role);
Loading history...
56
        $innerRelations = $schema->getInnerRelations($role);
0 ignored issues
show
The method getInnerRelations() does not exist on Cycle\ORM\SchemaInterface. Since it exists in all sub-types, consider adding an abstract or default implementation to Cycle\ORM\SchemaInterface. ( Ignorable by Annotation )

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

56
        /** @scrutinizer ignore-call */ 
57
        $innerRelations = $schema->getInnerRelations($role);
Loading history...
57 6878
58 6878
        // Build relations
59 5196
        $relations = [];
60
        foreach ($innerRelations as $relName => $relSchema) {
61
            $relations[$relName] = $factory->relation($orm, $schema, $role, $relName);
62
        }
63 6878
64 6878
        // add Parent's relations
65 800
        $parent = $schema->define($role, SchemaInterface::PARENT);
66 424
        while ($parent !== null) {
67
            foreach ($schema->getInnerRelations($parent) as $relName => $relSchema) {
68
                if (isset($relations[$relName])) {
69 424
                    continue;
70
                }
71
                $relations[$relName] = $factory->relation($orm, $schema, $parent, $relName);
72 800
            }
73 800
74
            $outerRelations += $schema->getOuterRelations($parent);
75
            $parent = $schema->define($parent, SchemaInterface::PARENT);
76 6878
        }
77
78 6878
        $result = new self($relations, $outerRelations);
79 4536
80 4536
        foreach ($outerRelations as $outerRole => $relations) {
81
            foreach ($relations as $container => $relationSchema) {
82
                $result->registerOuterRelation($outerRole, $container, $relationSchema);
83 6878
            }
84
        }
85
        return $result;
86 4536
    }
87
88
    public function hasDependencies(): bool
89 4536
    {
90
        return $this->dependencies !== [];
91 4536
    }
92 1494
93
    public function hasSlaves(): bool
94 3656
    {
95 886
        return $this->slaves !== [];
96
    }
97 886
98 656
    public function hasEmbedded(): bool
99 656
    {
100 656
        return $this->embedded !== [];
101 656
    }
102 656
103
    /**
104 656
     * Init relation data in entity data and entity state.
105
     */
106 886
    public function init(EntityFactoryInterface $factory, Node $node, array $data): array
107
    {
108 3648
        foreach ($this->innerRelations as $name => $relation) {
109
            if (!\array_key_exists($name, $data)) {
110 530
                if ($node->hasRelation($name)) {
111 530
                    continue;
112 530
                }
113
114
                $data[$name] = $relation->initReference($node);
115 3264
                $node->setRelation($name, $data[$name]);
116 3264
                continue;
117
            }
118
119 2900
            $item = $data[$name];
120
            if (\is_object($item) || $item === null) {
121 2900
                // cyclic initialization
122
                $node->setRelation($name, $item);
123
                continue;
124 2860
            }
125
126 2860
            // init relation for the entity and for state and the same time
127
            $data[$name] = $relation->init($factory, $node, $item);
128
        }
129 2764
130
        return $data;
131 2764
    }
132
133
    /**
134
     * @return RelationInterface[]
135
     */
136
    public function getSlaves(): array
137 4986
    {
138
        return $this->slaves;
139 4986
    }
140 3990
141 2446
    /**
142 122
     * @return array<string, DependencyInterface>
143
     */
144
    public function getMasters(): array
145 2438
    {
146 2438
        return $this->dependencies;
147 2438
    }
148
149
    /**
150 2426
     * @return SameRowRelationInterface[]
151 2426
     */
152
    public function getEmbedded(): array
153 280
    {
154 280
        return $this->embedded;
155
    }
156
157
    /**
158 2394
     * @return ActiveRelationInterface[]
159
     */
160
    public function getRelations(): array
161 4986
    {
162
        return $this->innerRelations;
163
    }
164
165
    private function registerOuterRelation(string $role, string $container, array $relationSchema): void
166
    {
167 1718
        // todo: it better to check instanceOf \Cycle\ORM\Relation\DependencyInterface instead of int
168
        $relationType = $relationSchema[Relation::TYPE];
169 1718
        // skip dependencies
170
        if ($relationType === Relation::BELONGS_TO || $relationType === Relation::REFERS_TO) {
171
            return;
172
        }
173
        if ($relationType === Relation::MANY_TO_MANY) {
174
            $handshaked = \is_string($relationSchema[Relation::SCHEMA][Relation::INVERSION] ?? null);
175 2080
            // Create ShadowHasMany
176
            if (!$handshaked) {
177 2080
                $relation = new ShadowHasMany(
178
                    $role . '.' . $container . ':' . $relationSchema[Relation::TARGET],
179
                    $relationSchema[Relation::SCHEMA][Relation::THROUGH_ENTITY],
180
                    (array) $relationSchema[Relation::SCHEMA][Relation::OUTER_KEY],
181
                    (array) $relationSchema[Relation::SCHEMA][Relation::THROUGH_OUTER_KEY],
182
                );
183 168
                $this->slaves[$relation->getName()] = $relation;
184
            }
185 168
            return;
186
        }
187
        if ($relationType === Relation::MORPHED_HAS_ONE || $relationType === Relation::MORPHED_HAS_MANY) {
188
            // todo: find morphed collisions, decide handshake
189
            $relation = new ShadowBelongsTo('~morphed~' . $container, $role, $relationSchema);
190
            $this->dependencies[$relation->getName()] ??= $relation;
191 6820
            return;
192
        }
193 6820
194
        $relation = new ShadowBelongsTo($container, $role, $relationSchema);
195
        $this->dependencies[$relation->getName()] = $relation;
196
    }
197
}
198