Completed
Pull Request — master (#137)
by Alex
01:52
created

MetadataGubbinsHolder::addEntity()   B

Complexity

Conditions 5
Paths 4

Size

Total Lines 15
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

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