Passed
Branch master (9f13d4)
by Alex
50:02 queued 44:57
created

MetadataRelationshipContainer::getRelations()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 3
c 0
b 0
f 0
dl 0
loc 6
rs 10
cc 2
nc 2
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 associations, keyed by classname.
27
     *
28
     * @var Association[]
29
     */
30
    private $associations = [];
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
    private function buildAssociations(): void
51
    {
52
        array_walk_recursive($this->stubs, [$this, 'buildAssociationFromStub']);
53
    }
54
55
    /**
56
     * @param  string                $baseType
57
     * @param  string                $targetType
58
     * @return AssociationStubBase[]
59
     */
60
    private function getStubs(?string $baseType, ?string $targetType): array
61
    {
62
        if ($baseType === null ||
63
           !array_key_exists($baseType, $this->stubs) ||
64
           !array_key_exists($targetType, $this->stubs[$baseType])) {
65
            return [];
66
        }
67
        return $this->stubs[$baseType][$targetType];
68
    }
69
70
    private function buildAssociationFromStub(AssociationStubBase $item)
71
    {
72
        $baseTypeCheck = ($item instanceof AssociationStubPolymorphic &&
73
            count($item->getThroughFieldChain()) == 3) ? null : $item->getBaseType();
74
75
        $otherCandidates = array_filter($this->getStubs($item->getTargType(), $baseTypeCheck), [$item, 'isCompatible']);
76
        $associations = array_reduce(
77
            $otherCandidates,
78
            function ($carry, $candidate) use ($item) {
79
                $newAssociation = AssociationFactory::getAssocationFromStubs($candidate, $item);
80
                $carry[spl_object_hash($newAssociation)] = $newAssociation;
81
                return $carry;
82
            },
83
            []
84
        );
85
        $this->addAssociations($associations);
86
    }
87
88
    private function addAssociations(array $additionals)
89
    {
90
        $this->associations = array_merge($this->associations, $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->associations)) {
110
            $this->buildAssociations();
111
        }
112
        $entities = $this->entities[$className];
113
        $relation = $entities->getStubs()[$relName];
114
        return array_reduce($relation->getAssociations(), 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->associations)) {
129
            $this->buildAssociations();
130
        }
131
132
        $this->checkClassExists($className);
133
        return array_reduce($this->entities[$className]->getStubs(), function ($carry, AssociationStubBase $item) {
134
            return array_merge($carry, $item->getAssociations());
135
        }, []);
136
    }
137
138
    /**
139
     * @param string $className
140
     */
141
    protected function checkClassExists(string $className)
142
    {
143
        if (!$this->hasClass($className)) {
144
            $msg = '%s does not exist in holder';
145
            throw new \InvalidArgumentException(sprintf($msg, $className));
146
        }
147
    }
148
    /**
149
     * gets all defined Association.
150
     *
151
     * @throws InvalidOperationException
152
     * @return Association[]
153
     */
154
    public function getRelations(): array
155
    {
156
        if (empty($this->associations)) {
157
            $this->buildAssociations();
158
        }
159
        return array_values($this->associations);
160
    }
161
162
    /**
163
     * checks if a class is loaded into the relation container.
164
     *
165
     * @param  string $className
166
     * @return bool
167
     */
168
    public function hasClass(string $className): bool
169
    {
170
        return array_key_exists($className, $this->entities);
171
    }
172
}
173