Passed
Pull Request — master (#226)
by Christopher
06:17
created

MetadataRelationshipContainer::addAssocations()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 2
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

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