Passed
Pull Request — master (#226)
by Christopher
07:03 queued 03:16
created

MetadataRelationshipContainer::buildAssocations()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

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