Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
75.51% covered (warning)
75.51%
148 / 196
68.42% covered (warning)
68.42%
13 / 19
CRAP
0.00% covered (danger)
0.00%
0 / 1
Request
75.51% covered (warning)
75.51%
148 / 196
68.42% covered (warning)
68.42%
13 / 19
132.93
0.00% covered (danger)
0.00%
0 / 1
 readEntity
94.44% covered (success)
94.44%
17 / 18
0.00% covered (danger)
0.00%
0 / 1
6.01
 readCollection
100.00% covered (success)
100.00%
6 / 6
100.00% covered (success)
100.00%
1 / 1
2
 attachRootParentIds
60.00% covered (warning)
60.00%
15 / 25
0.00% covered (danger)
0.00%
0 / 1
16.40
 readEntityWithoutRootParentResolution
0.00% covered (danger)
0.00%
0 / 11
0.00% covered (danger)
0.00%
0 / 1
6
 resolveRootParentId
40.00% covered (danger)
40.00%
6 / 15
0.00% covered (danger)
0.00%
0 / 1
17.58
 getRequestLookupKey
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 readRequestByProcessId
100.00% covered (success)
100.00%
8 / 8
100.00% covered (success)
100.00%
1 / 1
2
 readRequestsByIds
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
1
 readRequestByArchiveId
100.00% covered (success)
100.00%
8 / 8
100.00% covered (success)
100.00%
1 / 1
2
 readListByProvider
100.00% covered (success)
100.00%
12 / 12
100.00% covered (success)
100.00%
1 / 1
2
 readListBySource
84.62% covered (warning)
84.62%
11 / 13
0.00% covered (danger)
0.00%
0 / 1
6.13
 readListByCluster
100.00% covered (success)
100.00%
14 / 14
100.00% covered (success)
100.00%
1 / 1
5
 writeEntity
100.00% covered (success)
100.00%
16 / 16
100.00% covered (success)
100.00%
1 / 1
2
 writeListBySource
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
2
 writeImportEntity
100.00% covered (success)
100.00%
13 / 13
100.00% covered (success)
100.00%
1 / 1
4
 writeDeleteEntity
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
1
 writeDeleteListBySource
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
1
 testSource
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
2
 removeCache
11.76% covered (danger)
11.76%
2 / 17
0.00% covered (danger)
0.00%
0 / 1
64.64
1<?php
2
3namespace BO\Zmsbackend\Request\Service;
4
5use BO\Zmsbackend\Application as App;
6use BO\Zmsentities\Request as Entity;
7use BO\Zmsentities\Collection\RequestList as Collection;
8
9/**
10 *
11 * @SuppressWarnings(TooManyPublicMethods)
12 */
13class Request extends \BO\Zmsbackend\Base
14{
15    public function readEntity($source, $requestId, $resolveReferences = 0, $disableCache = false)
16    {
17        $cacheKey = "request-$source-$requestId-$resolveReferences";
18
19        if (!$disableCache && App::$cache && App::$cache->has($cacheKey)) {
20            return App::$cache->get($cacheKey);
21        }
22
23        $this->testSource($source);
24        $query = new \BO\Zmsbackend\Request\Repository\Request(\BO\Zmsbackend\Query\Base::SELECT);
25        $query
26            ->setResolveLevel($resolveReferences)
27            ->addEntityMapping()
28            ->addResolvedReferences($resolveReferences)
29            ->addConditionRequestSource($source)
30            ->addConditionRequestId($requestId);
31        $request = $this->fetchOne($query, new Entity());
32        if (! $request->hasId()) {
33            throw new \BO\Zmsbackend\Request\Exception\RequestNotFound("Could not find request with ID $source/$requestId");
34        }
35
36        $this->attachRootParentIds(new Collection([$request]));
37
38        if (App::$cache) {
39            App::$cache->set($cacheKey, $request);
40        }
41
42        return $request;
43    }
44
45    /**
46     * @SuppressWarnings(Param)
47     *
48     */
49    protected function readCollection($query)
50    {
51        $requestList = new Collection();
52        $statement = $this->fetchStatement($query);
53        while ($requestData = $statement->fetch(\PDO::FETCH_ASSOC)) {
54            $request = new Entity($query->postProcessJoins($requestData));
55            $requestList->addEntity($request);
56        }
57
58        return $this->attachRootParentIds($requestList);
59    }
60
61    protected function attachRootParentIds(Collection $requestList): Collection
62    {
63        if (!$requestList->count()) {
64            return $requestList;
65        }
66
67        $lookup = [];
68        foreach ($requestList as $request) {
69            $lookup[$this->getRequestLookupKey($request)] = $request;
70        }
71
72        $changed = true;
73        while ($changed) {
74            $changed = false;
75            foreach ($lookup as $request) {
76                $parentId = $request->getParentId();
77                if ($parentId === null || $parentId === '') {
78                    continue;
79                }
80
81                $parentKey = $request->getSource() . ':' . $parentId;
82                if (isset($lookup[$parentKey])) {
83                    continue;
84                }
85
86                try {
87                    $lookup[$parentKey] = $this->readEntityWithoutRootParentResolution(
88                        $request->getSource(),
89                        $parentId
90                    );
91                    $changed = true;
92                } catch (\Throwable $exception) {
93                    continue;
94                }
95            }
96        }
97
98        foreach ($requestList as $request) {
99            $request['root_parent_id'] = $this->resolveRootParentId($request, $lookup);
100        }
101
102        return $requestList;
103    }
104
105    protected function readEntityWithoutRootParentResolution($source, $requestId): Entity
106    {
107        $this->testSource($source);
108        $query = new Query\Request(Query\Base::SELECT);
109        $query
110            ->setResolveLevel(0)
111            ->addEntityMapping()
112            ->addConditionRequestSource($source)
113            ->addConditionRequestId($requestId);
114
115        $request = $this->fetchOne($query, new Entity());
116        if (!$request->hasId()) {
117            throw new Exception\Request\RequestNotFound("Could not find request with ID $source/$requestId");
118        }
119
120        return $request;
121    }
122
123    protected function resolveRootParentId(Entity $request, array $lookup): string
124    {
125        $current = $request;
126        $visited = [];
127
128        while ($current->getParentId() !== null && $current->getParentId() !== '') {
129            $parentKey = $current->getSource() . ':' . $current->getParentId();
130            if (isset($visited[$parentKey])) {
131                break;
132            }
133            $visited[$parentKey] = true;
134
135            $parent = $lookup[$parentKey] ?? null;
136            if (!$parent instanceof Entity) {
137                break;
138            }
139            $current = $parent;
140        }
141
142        $parentId = $current->getParentId();
143        if ($parentId !== null && $parentId !== '') {
144            return (string) $parentId;
145        }
146
147        return (string) $current->getId();
148    }
149
150    protected function getRequestLookupKey(Entity $request): string
151    {
152        return $request->getSource() . ':' . $request->getId();
153    }
154
155    public function readRequestByProcessId($processId, $resolveReferences = 0)
156    {
157        $collection = new Collection();
158        if ($processId) {
159            $query = new \BO\Zmsbackend\Request\Repository\Request(\BO\Zmsbackend\Query\Base::SELECT);
160            $query->setResolveLevel($resolveReferences);
161            $query->addConditionProcessId($processId);
162            $query->addEntityMapping();
163            $collection = $this->readCollection($query);
164        }
165        return $collection;
166    }
167
168    public function readRequestsByIds($ids, $resolveReferences = 0)
169    {
170        $query = new \BO\Zmsbackend\Request\Repository\Request(\BO\Zmsbackend\Query\Base::SELECT);
171        $query->setResolveLevel($resolveReferences);
172        $query->addConditionIds($ids);
173        $query->addEntityMapping();
174
175        return $this->readCollection($query);
176    }
177
178    public function readRequestByArchiveId($archiveId, $resolveReferences = 0)
179    {
180        $collection = new Collection();
181        if ($archiveId) {
182            $query = new \BO\Zmsbackend\Request\Repository\Request(\BO\Zmsbackend\Query\Base::SELECT);
183            $query->setResolveLevel($resolveReferences);
184            $query->addConditionArchiveId($archiveId);
185            $query->addEntityMapping();
186            $collection = $this->readCollection($query);
187        }
188        return $collection;
189    }
190
191    public function readListByProvider($source, $providerId, $resolveReferences = 0)
192    {
193        $this->testSource($source);
194        $query = new \BO\Zmsbackend\Request\Repository\Request(\BO\Zmsbackend\Query\Base::SELECT);
195        $requestRelationQuery = new \BO\Zmsbackend\RequestRelation\Service\RequestRelation();
196        $query->setResolveLevel($resolveReferences);
197        $query->addConditionProvider($providerId, $source);
198        $query->addConditionRequestSource($source);
199        $query->addEntityMapping();
200        $requestList = $this->readCollection($query);
201        foreach ($requestList as $request) {
202            $requestRelation = $requestRelationQuery->readEntity($request->getId(), $providerId);
203            $request['timeSlotCount'] = $requestRelation->getSlotCount();
204        }
205        return $requestList;
206    }
207
208    public function readListBySource($source, $resolveReferences = 0, $disableCache = false)
209    {
210        $cacheKey = "requestReadListBySource-$source-$resolveReferences";
211
212        if (!$disableCache && App::$cache && App::$cache->has($cacheKey)) {
213            return App::$cache->get($cacheKey);
214        }
215
216        $this->testSource($source);
217        $query = new \BO\Zmsbackend\Request\Repository\Request(\BO\Zmsbackend\Query\Base::SELECT);
218        $query->setResolveLevel($resolveReferences);
219        $query->addConditionRequestSource($source);
220        $query->addEntityMapping();
221        $requestList = $this->readCollection($query);
222        $requestList = ($requestList->count()) ? $requestList->sortByCustomKey('id') : $requestList;
223
224        if (App::$cache) {
225            App::$cache->set($cacheKey, $requestList);
226        }
227
228        return $requestList;
229    }
230
231    public function readListByCluster(\BO\Zmsentities\Cluster $cluster, $resolveReferences = 0)
232    {
233        $intersectList = array();
234        if ($cluster->scopes->count()) {
235            foreach ($cluster->scopes as $scope) {
236                $requestsByProvider = $this
237                    ->readListByProvider(
238                        $scope->provider['source'],
239                        $scope->getProviderId(),
240                        $resolveReferences - 1
241                    )->getArrayCopy();
242                if (count($requestsByProvider)) {
243                    $intersectList = (count($intersectList)) ?
244                        array_values(array_intersect($intersectList, $requestsByProvider)) : $requestsByProvider;
245                }
246            }
247        }
248        $requestList = new Collection($intersectList);
249        return $requestList;
250    }
251
252    public function writeEntity(Entity $entity)
253    {
254        $additionalData = ($entity->getAdditionalData()) ? json_encode($entity->getAdditionalData()) : '{}';
255        $this->writeDeleteEntity($entity->getId(), $entity->getSource());
256        $query = new \BO\Zmsbackend\Request\Repository\Request(\BO\Zmsbackend\Query\Base::INSERT);
257        $query->addValues([
258            'source' => $entity->getSource(),
259            'id' => $entity->getId(),
260            'name' => $entity->getName(),
261            'parent_id' => $entity->getParentId(),
262            'group' => $entity->getGroup(),
263            'link' =>  $entity->getLink(),
264            'data' => $additionalData,
265            'variant_id' => $entity->getVariantId()
266        ]);
267        $this->writeItem($query);
268
269        $this->removeCache($entity);
270
271        return $this->readEntity($entity->getSource(), $entity->getId());
272    }
273
274    public function writeListBySource(\BO\Zmsentities\Source $source)
275    {
276        $this->writeDeleteListBySource($source->getSource());
277        foreach ($source->getRequestList() as $request) {
278            $this->writeEntity($request);
279            $this->removeCache($request);
280        }
281        return $this->readListBySource($source->getSource());
282    }
283
284    public function writeImportEntity($request, $source = 'dldb')
285    {
286        $query = new \BO\Zmsbackend\Request\Repository\Request(\BO\Zmsbackend\Query\Base::REPLACE);
287        $query->addValues([
288            'source' => $source,
289            'id' => $request['id'],
290            'name' => $request['name'],
291            'group' => (isset($request['group'])) ? $request['group'] : 'Sonstiges',
292            'link' => ('dldb' == $source)
293                ? 'https://service.berlin.de/dienstleistung/' . $request['id'] . '/'
294                : ((isset($request['link'])) ? $request['link'] : ''),
295            'data' => json_encode($request)
296        ]);
297        $this->writeItem($query);
298        return $this->readEntity($source, $request['id'], 0, true);
299    }
300
301    public function writeDeleteEntity($requestId, $source)
302    {
303        $query = new \BO\Zmsbackend\Request\Repository\Request(\BO\Zmsbackend\Query\Base::DELETE);
304        $query->addConditionRequestId($requestId);
305        $query->addConditionRequestSource($source);
306
307        return $this->deleteItem($query);
308    }
309
310    public function writeDeleteListBySource($source)
311    {
312        $query = new \BO\Zmsbackend\Request\Repository\Request(\BO\Zmsbackend\Query\Base::DELETE);
313        $query->addConditionRequestSource($source);
314        return $this->deleteItem($query);
315    }
316
317    protected function testSource($source)
318    {
319        if (! (new \BO\Zmsbackend\Source\Service\Source())->readEntity($source)) {
320            throw new \BO\Zmsbackend\Source\Exception\UnknownDataSource();
321        }
322    }
323
324    public function removeCache(Entity $request)
325    {
326        if (!App::$cache) {
327            return;
328        }
329
330        $source = $request->getSource();
331
332        if (isset($request->id)) {
333            $requestId = $request->getId();
334            if (App::$cache->has("request-$source-$requestId-0")) {
335                App::$cache->delete("request-$source-$requestId-0");
336            }
337
338            if (App::$cache->has("request-$source-$requestId-1")) {
339                App::$cache->delete("request-$source-$requestId-1");
340            }
341
342            if (App::$cache->has("request-$source-$requestId-2")) {
343                App::$cache->delete("request-$source-$requestId-2");
344            }
345        }
346
347        if (App::$cache->has("requestReadListBySource-$source-0")) {
348            App::$cache->delete("requestReadListBySource-$source-0");
349        }
350
351        if (App::$cache->has("requestReadListBySource-$source-1")) {
352            App::$cache->delete("requestReadListBySource-$source-1");
353        }
354
355        if (App::$cache->has("requestReadListBySource-$source-2")) {
356            App::$cache->delete("requestReadListBySource-$source-2");
357        }
358    }
359}