MetadataRelationshipContainer::hasClass()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 1
c 0
b 0
f 0
dl 0
loc 3
rs 10
cc 1
nc 1
nop 1
1
<?php
2
3
declare(strict_types=1);
4
5
6
namespace AlgoWeb\PODataLaravel\Models;
7
8
use AlgoWeb\PODataLaravel\Models\ObjectMap\Entities\Associations\Association;
9
use AlgoWeb\PODataLaravel\Models\ObjectMap\Entities\Associations\AssociationFactory;
10
use AlgoWeb\PODataLaravel\Models\ObjectMap\Entities\Associations\AssociationStubBase;
11
use AlgoWeb\PODataLaravel\Models\ObjectMap\Entities\Associations\AssociationStubMonomorphic;
12
use AlgoWeb\PODataLaravel\Models\ObjectMap\Entities\Associations\AssociationStubPolymorphic;
13
use AlgoWeb\PODataLaravel\Models\ObjectMap\Entities\EntityGubbins;
14
use POData\Common\InvalidOperationException;
15
16
class MetadataRelationshipContainer implements IMetadataRelationshipContainer
17
{
18
    /**
19
     * @var EntityGubbins[] all entities keyed by class name
20
     */
21
    private $entities = [];
22
    /**
23
     * @var AssociationStubBase[][][] AssociationStubMonomorphic keyed as [BaseClass][targetClass]
24
     */
25
    private $stubs = [
26
    ];
27
    /**
28
     * A complete set of associations, keyed by classname.
29
     *
30
     * @var Association[]
31
     */
32
    private $associations = [];
33
34
    /**
35
     * Add entity to Container.
36
     *
37
     * @param EntityGubbins $entity
38
     */
39
    public function addEntity(EntityGubbins $entity): void
40
    {
41
        /** @var string $baseType */
42
        $baseType                  = $entity->getClassName();
43
        $this->entities[$baseType] = $entity;
44
        if (array_key_exists($baseType, $this->stubs)) {
45
            throw new \InvalidArgumentException(sprintf('%s already added', $baseType));
46
        }
47
        $this->stubs[$baseType] = [];
48
        foreach ($entity->getStubs() as $stub) {
49
            $this->stubs[$baseType][$stub->getTargType()][] = $stub;
50
        }
51
    }
52
53
    private function buildAssociations(): void
54
    {
55
        array_walk_recursive($this->stubs, [$this, 'buildAssociationFromStub']);
56
    }
57
58
    /**
59
     * @param  string                $baseType
60
     * @param  string                $targetType
61
     * @return AssociationStubBase[]
62
     */
63
    private function getStubs(?string $baseType, ?string $targetType): array
64
    {
65
        if ($baseType === null ||
66
           !array_key_exists($baseType, $this->stubs) ||
67
           !array_key_exists($targetType, $this->stubs[$baseType])) {
68
            return [];
69
        }
70
        return $this->stubs[$baseType][$targetType];
71
    }
72
73
    private function buildAssociationFromStub(AssociationStubBase $item): void
74
    {
75
        $baseTypeCheck = ($item instanceof AssociationStubPolymorphic &&
76
            count($item->getThroughFieldChain()) == 3) ? null : $item->getBaseType();
77
78
        $otherCandidates = array_filter($this->getStubs($item->getTargType(), $baseTypeCheck), [$item, 'isCompatible']);
79
        $associations    = array_reduce(
80
            $otherCandidates,
81
            function ($carry, $candidate) use ($item) {
82
                $newAssociation = AssociationFactory::getAssocationFromStubs($candidate, $item);
83
                $carry[spl_object_hash($newAssociation)] = $newAssociation;
84
                return $carry;
85
            },
86
            []
87
        );
88
        $this->addAssociations($associations);
89
    }
90
91
    /**
92
     * @param Association[] $additionals
93
     */
94
    private function addAssociations(array $additionals): void
95
    {
96
        $this->associations = array_merge($this->associations, $additionals);
97
    }
98
99
100
    /**
101
     * returns all Relation Stubs that are permitted at the other end.
102
     *
103
     * @param string                 $className
104
     * @param string                 $relName
105
     * @return AssociationStubBase[]
106
     */
107
    public function getRelationsByRelationName(string $className, string $relName): array
108
    {
109
        $this->checkClassExists($className);
110
        if (!array_key_exists($relName, $this->entities[$className]->getStubs())) {
111
            $msg = 'Relation %s not registered on %s';
112
            throw new \InvalidArgumentException(sprintf($msg, $relName, $className));
113
        }
114
115
        if (empty($this->associations)) {
116
            $this->buildAssociations();
117
        }
118
        $entities = $this->entities[$className];
119
        $relation = $entities->getStubs()[$relName];
120
        return array_reduce($relation->getAssociations(), function ($carry, Association $item) use ($relation) {
121
            $carry[] = ($item->getFirst() === $relation) ? $item->getLast() : $item->getFirst();
122
            return $carry;
123
        }, []);
124
    }
125
126
    /**
127
     * gets All Association On a given class.
128
     *
129
     * @param  string        $className
130
     * @return Association[]
131
     */
132
    public function getRelationsByClass(string $className): array
133
    {
134
        if (empty($this->associations)) {
135
            $this->buildAssociations();
136
        }
137
138
        $this->checkClassExists($className);
139
        return array_reduce($this->entities[$className]->getStubs(), function ($carry, AssociationStubBase $item) {
140
            return array_merge($carry, $item->getAssociations());
141
        }, []);
142
    }
143
144
    /**
145
     * @param string $className
146
     */
147
    protected function checkClassExists(string $className): void
148
    {
149
        if (!$this->hasClass($className)) {
150
            $msg = '%s does not exist in holder';
151
            throw new \InvalidArgumentException(sprintf($msg, $className));
152
        }
153
    }
154
    /**
155
     * gets all defined Association.
156
     *
157
     * @throws InvalidOperationException
158
     * @return Association[]
159
     */
160
    public function getRelations(): array
161
    {
162
        if (empty($this->associations)) {
163
            $this->buildAssociations();
164
        }
165
        return array_values($this->associations);
166
    }
167
168
    /**
169
     * checks if a class is loaded into the relation container.
170
     *
171
     * @param  string $className
172
     * @return bool
173
     */
174
    public function hasClass(string $className): bool
175
    {
176
        return array_key_exists($className, $this->entities);
177
    }
178
}
179