Completed
Push — master ( 9f13d4...a886ae )
by Alex
38:38 queued 38:36
created

buildAssociationFromStub()   A

Complexity

Conditions 3
Paths 4

Size

Total Lines 16
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 10
c 1
b 0
f 0
dl 0
loc 16
rs 9.9332
cc 3
nc 4
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
        $baseType                  = $entity->getClassName();
42
        $this->entities[$baseType] = $entity;
43
        if (array_key_exists($baseType, $this->stubs)) {
44
            throw new \InvalidArgumentException(sprintf('%s already added', $baseType));
45
        }
46
        $this->stubs[$baseType] = [];
47
        foreach ($entity->getStubs() as $stub) {
48
            $this->stubs[$baseType][$stub->getTargType()][] = $stub;
49
        }
50
    }
51
52
    private function buildAssociations(): void
53
    {
54
        array_walk_recursive($this->stubs, [$this, 'buildAssociationFromStub']);
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 buildAssociationFromStub(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
        $associations    = array_reduce(
79
            $otherCandidates,
80
            function ($carry, $candidate) use ($item) {
81
                $newAssociation = AssociationFactory::getAssocationFromStubs($candidate, $item);
82
                $carry[spl_object_hash($newAssociation)] = $newAssociation;
83
                return $carry;
84
            },
85
            []
86
        );
87
        $this->addAssociations($associations);
88
    }
89
90
    private function addAssociations(array $additionals)
91
    {
92
        $this->associations = array_merge($this->associations, $additionals);
93
    }
94
95
96
    /**
97
     * returns all Relation Stubs that are permitted at the other end.
98
     *
99
     * @param $className
100
     * @param $relName
101
     * @return AssociationStubBase[]
102
     */
103
    public function getRelationsByRelationName(string $className, string $relName): array
104
    {
105
        $this->checkClassExists($className);
106
        if (!array_key_exists($relName, $this->entities[$className]->getStubs())) {
107
            $msg = 'Relation %s not registered on %s';
108
            throw new \InvalidArgumentException(sprintf($msg, $relName, $className));
109
        }
110
111
        if (empty($this->associations)) {
112
            $this->buildAssociations();
113
        }
114
        $entities = $this->entities[$className];
115
        $relation = $entities->getStubs()[$relName];
116
        return array_reduce($relation->getAssociations(), function ($carry, Association $item) use ($relation) {
117
            $carry[] = ($item->getFirst() === $relation) ? $item->getLast() : $item->getFirst();
118
            return $carry;
119
        }, []);
120
    }
121
122
    /**
123
     * gets All Association On a given class.
124
     *
125
     * @param  string        $className
126
     * @return Association[]
127
     */
128
    public function getRelationsByClass(string $className): array
129
    {
130
        if (empty($this->associations)) {
131
            $this->buildAssociations();
132
        }
133
134
        $this->checkClassExists($className);
135
        return array_reduce($this->entities[$className]->getStubs(), function ($carry, AssociationStubBase $item) {
136
            return array_merge($carry, $item->getAssociations());
137
        }, []);
138
    }
139
140
    /**
141
     * @param string $className
142
     */
143
    protected function checkClassExists(string $className)
144
    {
145
        if (!$this->hasClass($className)) {
146
            $msg = '%s does not exist in holder';
147
            throw new \InvalidArgumentException(sprintf($msg, $className));
148
        }
149
    }
150
    /**
151
     * gets all defined Association.
152
     *
153
     * @throws InvalidOperationException
154
     * @return Association[]
155
     */
156
    public function getRelations(): array
157
    {
158
        if (empty($this->associations)) {
159
            $this->buildAssociations();
160
        }
161
        return array_values($this->associations);
162
    }
163
164
    /**
165
     * checks if a class is loaded into the relation container.
166
     *
167
     * @param  string $className
168
     * @return bool
169
     */
170
    public function hasClass(string $className): bool
171
    {
172
        return array_key_exists($className, $this->entities);
173
    }
174
}
175