Issues (188)

src/Relation/AbstractRelation.php (2 issues)

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
The doc comment class-string[]|string[] at position 0 could not be parsed: Unknown type name 'class-string' at position 0 in class-string[]|string[].
Loading history...
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 ignore-type  annotation

110
            $parent = $this->ormSchema->define(/** @scrutinizer ignore-type */ $role, SchemaInterface::PARENT);
Loading history...
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