Passed
Push — master ( 7d597c...1de5f6 )
by Smoren
01:47
created

PreloadedGraphRepository   A

Complexity

Total Complexity 17

Size/Duplication

Total Lines 155
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
wmc 17
eloc 45
dl 0
loc 155
rs 10
c 0
b 0
f 0

8 Methods

Rating   Name   Duplication   Size   Complexity  
A getNextVertexes() 0 8 1
A getEdgeById() 0 9 2
A isSuitableEdge() 0 3 2
A getPrevVertexes() 0 8 1
A __construct() 0 21 3
A isSuitableVertex() 0 3 2
A getVertexById() 0 9 2
A getLinkedVertexesFromMap() 0 16 4
1
<?php
2
3
namespace Smoren\GraphTools\Store;
4
5
use Smoren\GraphTools\Conditions\Interfaces\FilterConditionInterface;
6
use Smoren\GraphTools\Exceptions\RepositoryException;
7
use Smoren\GraphTools\Models\Interfaces\EdgeInterface;
8
use Smoren\GraphTools\Models\Interfaces\VertexInterface;
9
use Smoren\GraphTools\Store\Interfaces\GraphRepositoryInterface;
10
use Smoren\GraphTools\Structs\Interfaces\TraverseStepIteratorInterface;
11
use Smoren\GraphTools\Structs\TraverseStepItem;
12
use Smoren\GraphTools\Structs\TraverseStepIterator;
13
use Smoren\NestedAccessor\Helpers\NestedHelper;
14
15
/**
16
 * Graph repository implementation with data storage in RAM
17
 * @author <[email protected]> Smoren
18
 */
19
class PreloadedGraphRepository implements GraphRepositoryInterface
20
{
21
    /**
22
     * @var array<string, VertexInterface> vertexes map by id
23
     */
24
    protected array $vertexMap = [];
25
    /**
26
     * @var array<string, EdgeInterface> edges map by id
27
     */
28
    protected array $edgesMap = [];
29
    /**
30
     * @var array<string, array<string, string[]>> links map: array<vertexFromId array<edgeId, [edgeType, vertexToId]>>
31
     */
32
    protected array $edgesDirectMap = [];
33
    /**
34
     * @var array<string, array<string, string[]>> links map: array<vertexToId array<edgeId, [edgeType, vertexFromId]>>
35
     */
36
    protected array $edgesReverseMap = [];
37
38
    /**
39
     * SimpleGraphRepository constructor
40
     * @param array<VertexInterface> $vertexes vertexes list
41
     * @param array<EdgeInterface> $edges edges list
42
     */
43
    public function __construct(
44
        array $vertexes,
45
        array $edges
46
    ) {
47
        foreach($vertexes as $vertex) {
48
            $this->vertexMap[$vertex->getId()] = $vertex;
49
        }
50
51
        foreach($edges as $edge) {
52
            $this->edgesMap[$edge->getId()] = $edge;
53
54
            NestedHelper::set(
55
                $this->edgesDirectMap,
56
                [$edge->getFromId(), $edge->getId()],
57
                [$edge->getType(), $edge->getToId()]
58
            );
59
60
            NestedHelper::set(
61
                $this->edgesReverseMap,
62
                [$edge->getToId(), $edge->getId()],
63
                [$edge->getType(), $edge->getFromId()]
64
            );
65
        }
66
    }
67
68
    /**
69
     * @inheritDoc
70
     * @throws RepositoryException
71
     */
72
    public function getVertexById(string $id): VertexInterface
73
    {
74
        if(!isset($this->vertexMap[$id])) {
75
            throw new RepositoryException(
76
                "vertex with id '{$id}' not exist",
77
                RepositoryException::VERTEX_NOT_FOUND
78
            );
79
        }
80
        return $this->vertexMap[$id];
81
    }
82
83
    /**
84
     * @inheritDoc
85
     * @throws RepositoryException
86
     */
87
    public function getEdgeById(string $id): EdgeInterface
88
    {
89
        if(!isset($this->edgesMap[$id])) {
90
            throw new RepositoryException(
91
                "edge with id '{$id}' not exist",
92
                RepositoryException::EDGE_NOT_FOUND
93
            );
94
        }
95
        return $this->edgesMap[$id];
96
    }
97
98
    /**
99
     * @inheritDoc
100
     * @throws RepositoryException
101
     */
102
    public function getNextVertexes(
103
        VertexInterface $vertex,
104
        ?FilterConditionInterface $condition = null
105
    ): TraverseStepIteratorInterface {
106
        return $this->getLinkedVertexesFromMap(
107
            $this->edgesDirectMap,
108
            $vertex,
109
            $condition
110
        );
111
    }
112
113
    /**
114
     * @inheritDoc
115
     * @throws RepositoryException
116
     */
117
    public function getPrevVertexes(
118
        VertexInterface $vertex,
119
        ?FilterConditionInterface $condition = null
120
    ): TraverseStepIteratorInterface {
121
        return $this->getLinkedVertexesFromMap(
122
            $this->edgesReverseMap,
123
            $vertex,
124
            $condition
125
        );
126
    }
127
128
    /**
129
     * Get next vertexes from given map by current vertex
130
     * @param array<string, array<string, string[]>> $source source map
131
     * @param VertexInterface $vertex current vertex
132
     * @param FilterConditionInterface|null $condition filter condition
133
     * @return TraverseStepIteratorInterface next vertexes iterator
134
     * @throws RepositoryException
135
     */
136
    protected function getLinkedVertexesFromMap(
137
        array $source,
138
        VertexInterface $vertex,
139
        ?FilterConditionInterface $condition
140
    ): TraverseStepIteratorInterface {
141
        $result = [];
142
        foreach($source[$vertex->getId()] ?? [] as $edgeId => [$edgeType, $targetId]) {
143
            $edge = $this->getEdgeById($edgeId);
144
            if($this->isSuitableEdge($edge, $condition)) {
145
                $target = $this->getVertexById($targetId);
146
                if($this->isSuitableVertex($target, $condition)) {
147
                    $result[] = new TraverseStepItem($edge, $target);
148
                }
149
            }
150
        }
151
        return new TraverseStepIterator($result);
152
    }
153
154
    /**
155
     * Returns true if given vertex matches filter condition
156
     * @param VertexInterface $vertex vertex to check
157
     * @param FilterConditionInterface|null $condition filter condition
158
     * @return bool
159
     */
160
    protected function isSuitableVertex(VertexInterface $vertex, ?FilterConditionInterface $condition): bool
161
    {
162
        return ($condition === null) || $condition->isSuitableVertex($vertex);
163
    }
164
165
    /**
166
     * Returns true if given edge matches filter condition
167
     * @param EdgeInterface $edge edge to check
168
     * @param FilterConditionInterface|null $condition filter condition
169
     * @return bool
170
     */
171
    protected function isSuitableEdge(EdgeInterface $edge, ?FilterConditionInterface $condition): bool
172
    {
173
        return ($condition === null) || $condition->isSuitableEdge($edge);
174
    }
175
}
176