Completed
Push — master ( 817152...5c67c4 )
by Alex
15s queued 11s
created

MetadataGubbinsHolder::addEntity()   A

Complexity

Conditions 5
Paths 4

Size

Total Lines 12
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 9
c 1
b 0
f 0
dl 0
loc 12
rs 9.6111
cc 5
nc 4
nop 1
1
<?php
2
3
namespace AlgoWeb\PODataLaravel\Models;
4
5
use AlgoWeb\PODataLaravel\Models\ObjectMap\Entities\Associations\AssociationMonomorphic;
6
use AlgoWeb\PODataLaravel\Models\ObjectMap\Entities\Associations\AssociationStubBase;
7
use AlgoWeb\PODataLaravel\Models\ObjectMap\Entities\Associations\AssociationStubMonomorphic;
8
use AlgoWeb\PODataLaravel\Models\ObjectMap\Entities\Associations\AssociationStubPolymorphic;
9
use AlgoWeb\PODataLaravel\Models\ObjectMap\Entities\Associations\AssociationStubRelationType;
10
use AlgoWeb\PODataLaravel\Models\ObjectMap\Entities\EntityGubbins;
11
use Illuminate\Support\Str;
12
use POData\Common\InvalidOperationException;
13
14
class MetadataGubbinsHolder
15
{
16
    protected $relations = [];
17
    protected $knownSides = [];
18
19
    /**
20
     * Add entity to holder
21
     *
22
     * @param EntityGubbins $entity
23
     * @throws InvalidOperationException
24
     */
25
    public function addEntity(EntityGubbins $entity)
26
    {
27
        $className = $entity->getClassName();
28
        if (array_key_exists($className, $this->relations)) {
29
            $msg = $className . ' already added';
30
            throw new \InvalidArgumentException($msg);
31
        }
32
        $this->relations[$className] = $entity;
33
        $this->knownSides[$className] = [];
34
        foreach ($entity->getStubs() as $relName => $stub) {
35
            if ($stub instanceof AssociationStubPolymorphic && $stub->isKnownSide()) {
36
                $this->knownSides[$className][$relName] = $stub;
37
            }
38
        }
39
    }
40
41
    public function getRelationsByRelationName($className, $relName)
42
    {
43
        $this->checkClassExists($className);
44
45
        $rels = $this->relations[$className];
46
47
        if (!array_key_exists($relName, $rels->getStubs())) {
48
            $msg = 'Relation ' . $relName . ' not registered on ' . $className;
49
            throw new \InvalidArgumentException($msg);
50
        }
51
        /** @var AssociationStubBase $stub */
52
        $stub = $rels->getStubs()[$relName];
53
        $targType = $stub->getTargType();
54
        if (!array_key_exists($targType, $this->relations)) {
55
            return [];
56
        }
57
        $targRel = $this->relations[$targType];
58
        // now dig out compatible stubs on target type
59
        $targStubs = $targRel->getStubs();
60
        $relStubs = [];
61
        foreach ($targStubs as $targStub) {
62
            if ($stub->isCompatible($targStub)) {
63
                $relStubs[] = $targStub;
64
            }
65
        }
66
        return $relStubs;
67
    }
68
69
    /**
70
     * @param string $className
71
     * @return array
72
     * @throws InvalidOperationException
73
     */
74
    public function getRelationsByClass($className)
75
    {
76
        $this->checkClassExists($className);
77
78
        $rels = $this->relations[$className];
79
        $stubs = $rels->getStubs();
80
81
        $associations = [];
82
        foreach ($stubs as $relName => $stub) {
83
            $others = $this->getRelationsByRelationName($className, $relName);
84
            if ($stub instanceof AssociationStubMonomorphic) {
85
                $msg = 'Monomorphic relation stub on ' . $className . ' ' . $relName
86
                       . ' should point to at most 1 other stub';
87
                if (!(1 >= count($others))) {
88
                    throw new InvalidOperationException($msg);
89
                }
90
            }
91
            if (1 === count($others)) {
92
                $others = $others[0];
93
                $assoc = new AssociationMonomorphic();
94
                $first = -1 === $stub->compare($others);
95
                $assoc->setFirst($first ? $stub : $others);
96
                $assoc->setLast($first ? $others : $stub);
97
                if (!$assoc->isOk()) {
98
                    throw new InvalidOperationException('');
99
                }
100
                $associations[] = $assoc;
101
            }
102
        }
103
        return $associations;
104
    }
105
106
    /**
107
     * @return array
108
     * @throws InvalidOperationException
109
     */
110
    public function getRelations()
111
    {
112
        $classNames = array_keys($this->relations);
113
114
        $associations = $this->buildRelationAssociations($classNames);
115
116
        $unknowns = $this->buildUnknownSides();
117
118
        $monoAssoc = [];
119
        $polyAssoc = [];
120
        foreach ($associations as $assoc) {
121
            if ($assoc->getFirst() instanceof AssociationStubMonomorphic) {
122
                $monoAssoc[] = $assoc;
123
                continue;
124
            }
125
            // monomorphic associations are dealt with, now for the polymorphic associations - they're a mite trickier
126
            $firstKnown = $assoc->getFirst()->isKnownSide();
127
            /** @var AssociationStubPolymorphic $known */
128
            $known = $firstKnown ? $assoc->getFirst() : $assoc->getLast();
129
            /** @var AssociationStubPolymorphic $unknown */
130
            $unknown = $firstKnown ? $assoc->getLast() : $assoc->getFirst();
131
            $className = $known->getBaseType();
132
            $relName = $known->getRelationName();
133
            $unknowns[$className][$relName][] = $unknown;
134
        }
135
136
        foreach ($this->knownSides as $knownType => $knownDeets) {
137
            foreach (array_keys($knownDeets) as $key) {
138
                /** @var AssociationStubPolymorphic[] $lastCandidates */
139
                $lastCandidates = $unknowns[$knownType][$key];
140
                if (0 == count($lastCandidates)) {
141
                    continue;
142
                }
143
                foreach ($lastCandidates as $lc) {
144
                    /** @var AssociationStubPolymorphic $stub */
145
                    $stub = clone $this->knownSides[$knownType][$key];
146
                    $isMulti = ($stub->getMultiplicity() == AssociationStubRelationType::MANY());
147
                    $relPolyTypeName = substr($lc->getBaseType(), strrpos($lc->getBaseType(), '\\')+1);
148
                    $relPolyTypeName = Str::plural($relPolyTypeName, $isMulti ? 2 : 1);
149
                    $stub->setRelationName($stub->getRelationName() . '_' . $relPolyTypeName);
150
                    $assoc = new AssociationMonomorphic();
151
                    $first = -1 === $stub->compare($lc);
152
                    $assoc->setFirst($first ? $stub : $lc);
153
                    $assoc->setLast($first ? $lc : $stub);
154
                    if (!$assoc->isOk()) {
155
                        throw new InvalidOperationException('');
156
                    }
157
                    $polyAssoc[] = $assoc;
158
                }
159
            }
160
        }
161
        $result = array_merge($monoAssoc, $polyAssoc);
162
        return $result;
163
    }
164
165
    public function hasClass($className)
166
    {
167
        return array_key_exists($className, $this->relations);
168
    }
169
170
    /**
171
     * @param $className
172
     */
173
    protected function checkClassExists($className)
174
    {
175
        if (!$this->hasClass($className)) {
176
            $msg = $className . ' does not exist in holder';
177
            throw new \InvalidArgumentException($msg);
178
        }
179
    }
180
181
    /**
182
     * @param array $classNames
183
     * @return array
184
     * @throws InvalidOperationException
185
     */
186
    protected function buildRelationAssociations(array $classNames)
187
    {
188
        $associations = [];
189
190
        foreach ($classNames as $class) {
191
            $rawAssoc = $this->getRelationsByClass($class);
192
            foreach ($rawAssoc as $raw) {
193
                if (!in_array($raw, $associations)) {
194
                    $associations[] = $raw;
195
                }
196
            }
197
        }
198
        return $associations;
199
    }
200
201
    /**
202
     * @return array
203
     */
204
    protected function buildUnknownSides()
205
    {
206
        $unknowns = [];
207
        foreach ($this->knownSides as $knownType => $knownDeets) {
208
            $unknowns[$knownType] = [];
209
            foreach (array_keys($knownDeets) as $key) {
210
                $unknowns[$knownType][$key] = [];
211
            }
212
        }
213
        return $unknowns;
214
    }
215
}
216