Issues (126)

src/Helpers/NavigationPropertyHelpers.php (2 issues)

Labels
Severity
1
<?php
2
3
declare(strict_types=1);
4
5
namespace AlgoWeb\ODataMetadata\Helpers;
6
7
use AlgoWeb\ODataMetadata\Enums\Multiplicity;
8
use AlgoWeb\ODataMetadata\Interfaces\ICollectionType;
9
use AlgoWeb\ODataMetadata\Interfaces\IEntityReferenceType;
10
use AlgoWeb\ODataMetadata\Interfaces\IEntityType;
11
use AlgoWeb\ODataMetadata\Interfaces\INavigationProperty;
12
use AlgoWeb\ODataMetadata\Interfaces\IStructuralProperty;
13
use AlgoWeb\ODataMetadata\Interfaces\IStructuredType;
14
use AlgoWeb\ODataMetadata\Interfaces\ITypeReference;
15
16
/**
17
 * Trait NavigationPropertyHelpers.
18
 * @package AlgoWeb\ODataMetadata\Helpers
19
 */
20
trait NavigationPropertyHelpers
21
{
22
    /**
23
     * Gets the multiplicity of this end of a bidirectional relationship between this navigation property and
24
     * its partner.
25
     *
26
     * @return Multiplicity the multiplicity of this end of the relationship
27
     */
28
    public function multiplicity(): Multiplicity
29
    {
30
        /** @var INavigationProperty $this */
31
        $partner = $this->getPartner();
32
        if ($partner !== null) {
33
            $partnerType = $partner->getType();
34
            if ($partnerType->isCollection()) {
35
                return Multiplicity::Many();
36
            }
37
            return $partnerType->getNullable() ? Multiplicity::ZeroOrOne() : Multiplicity::One();
38
        }
39
        return Multiplicity::One();
40
    }
41
42
    /**
43
     * Gets the entity type targeted by this navigation property.
44
     *
45
     * @return IEntityType the entity type targeted by this navigation property
46
     */
47
    public function toEntityType(): IEntityType
48
    {
49
        /** @var INavigationProperty $this */
50
        $target = $this->getType()->getDefinition();
51
        if ($target->getTypeKind()->isCollection()) {
52
            assert($target instanceof ICollectionType);
53
            $target = $target->getElementType()->getDefinition();
54
        }
55
56
        if ($target->getTypeKind()->isEntityReference()) {
57
            assert($target instanceof IEntityReferenceType);
58
            $target = $target->getEntityType();
59
        }
60
        assert($target instanceof IEntityType, 'The final target of any navitation property should be a Entity.');
61
        return $target;
62
    }
63
64
    /**
65
     * Gets the entity type declaring this navigation property.
66
     *
67
     * @return IEntityType the entity type that declares this navigation property
68
     */
69
    public function declaringEntityType(): IEntityType
70
    {
71
        /** @var INavigationProperty $this */
72
        $declaringType = $this->getDeclaringType();
73
        assert($declaringType instanceof IEntityType, 'navigation prperties should always be delcared on a Entity');
74
        return $declaringType;
75
    }
76
77
    public function populateCaches(): void
78
    {
79
        /** @var INavigationProperty $property */
80
        $property = $this;
81
        // Force computation that can apply annotations to the navigation property.
82
        $property->getPartner();
83
        $property->isPrincipal();
84
        $property->getDependentProperties();
85
    }
86
87
    /**
88
     * Gets the primary end of a pair of partnered navigation properties, selecting the principal end if there is one
89
     * and making a stable, arbitrary choice otherwise.
90
     *
91
     * @return INavigationProperty the primary end between the navigation property and its partner
92
     */
93
    public function getPrimary(): INavigationProperty
94
    {
95
        /** @var INavigationProperty $property */
96
        $property = $this;
97
        if ($property->isPrincipal()) {
98
            return $property;
99
        }
100
101
        $partner = $property->getPartner();
102
        if ($partner->isPrincipal()) {
103
            return $partner;
104
        }
105
106
        // There is no meaningful basis for determining which of the two partners is principal,
107
        // so break the tie with an arbitrary, stable comparision.
108
        $nameComparison = strcmp($property->getName(), $partner->getName());
0 ignored issues
show
It seems like $partner->getName() can also be of type null; however, parameter $string2 of strcmp() 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

108
        $nameComparison = strcmp($property->getName(), /** @scrutinizer ignore-type */ $partner->getName());
Loading history...
It seems like $property->getName() can also be of type null; however, parameter $string1 of strcmp() 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

108
        $nameComparison = strcmp(/** @scrutinizer ignore-type */ $property->getName(), $partner->getName());
Loading history...
109
        if ($nameComparison == 0) {
110
            $nameComparison = strcmp(
111
                $property->declaringEntityType()->fullName(),
112
                $partner->declaringEntityType()->fullName()
113
            );
114
        }
115
116
        return $nameComparison > 0 ? $property : $partner;
117
    }
118
119
    /**
120
     * @return ITypeReference|null gets the type of this term
121
     */
122
    abstract public function getType(): ?ITypeReference;
123
124
    /**
125
     * @return INavigationProperty gets the partner of this navigation property
126
     */
127
    abstract public function getPartner(): INavigationProperty;
128
129
    /**
130
     * @return IStructuralProperty[]|null gets the dependent properties of this navigation property, returning null if
131
     *                                    this is the principal end or if there is no referential constraint
132
     */
133
    abstract public function getDependentProperties(): ?array;
134
135
    /**
136
     * @return string|null gets the name of this element
137
     */
138
    abstract public function getName(): ?string;
139
140
    /**
141
     * @return bool gets whether this navigation property originates at the principal end of an association
142
     */
143
    abstract public function isPrincipal(): bool;
144
145
    /**
146
     * @return IStructuredType gets the type that this property belongs to
147
     */
148
    abstract public function getDeclaringType(): IStructuredType;
149
}
150