Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
89.10% covered (warning)
89.10%
286 / 321
42.86% covered (danger)
42.86%
6 / 14
CRAP
0.00% covered (danger)
0.00%
0 / 1
MapperService
89.10% covered (warning)
89.10%
286 / 321
42.86% covered (danger)
42.86%
6 / 14
210.35
0.00% covered (danger)
0.00%
0 / 1
 mapScopeForProvider
100.00% covered (success)
100.00%
8 / 8
100.00% covered (success)
100.00%
1 / 1
5
 extractReservationDuration
85.71% covered (warning)
85.71%
6 / 7
0.00% covered (danger)
0.00%
0 / 1
5.07
 extractActivationDuration
81.82% covered (warning)
81.82%
9 / 11
0.00% covered (danger)
0.00%
0 / 1
7.29
 mapOfficesWithScope
92.00% covered (success)
92.00%
46 / 50
0.00% covered (danger)
0.00%
0 / 1
44.99
 mapCombinable
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
2
 mapServicesWithCombinations
96.15% covered (success)
96.15%
25 / 26
0.00% covered (danger)
0.00%
0 / 1
11
 mapRelations
91.67% covered (success)
91.67%
11 / 12
0.00% covered (danger)
0.00%
0 / 1
4.01
 scopeToThinnedScope
76.47% covered (warning)
76.47%
39 / 51
0.00% covered (danger)
0.00%
0 / 1
39.96
 processToThinnedProcess
81.40% covered (warning)
81.40%
35 / 43
0.00% covered (danger)
0.00%
0 / 1
45.82
 thinnedProcessToProcess
100.00% covered (success)
100.00%
27 / 27
100.00% covered (success)
100.00%
1 / 1
4
 createScope
100.00% covered (success)
100.00%
39 / 39
100.00% covered (success)
100.00%
1 / 1
6
 createRequests
66.67% covered (warning)
66.67%
12 / 18
0.00% covered (danger)
0.00%
0 / 1
4.59
 contactToThinnedContact
100.00% covered (success)
100.00%
19 / 19
100.00% covered (success)
100.00%
1 / 1
6
 providerToThinnedProvider
100.00% covered (success)
100.00%
9 / 9
100.00% covered (success)
100.00%
1 / 1
8
1<?php
2
3declare(strict_types=1);
4
5namespace BO\Zmscitizenapi\Services\Core;
6
7use BO\Zmscitizenapi\Helper\ClientIpHelper;
8use BO\Zmscitizenapi\Models\Office;
9use BO\Zmscitizenapi\Models\Combinable;
10use BO\Zmscitizenapi\Models\OfficeServiceRelation;
11use BO\Zmscitizenapi\Models\Service;
12use BO\Zmscitizenapi\Models\ThinnedContact;
13use BO\Zmscitizenapi\Models\ThinnedProcess;
14use BO\Zmscitizenapi\Models\ThinnedProvider;
15use BO\Zmscitizenapi\Models\ThinnedScope;
16use BO\Zmscitizenapi\Models\Collections\OfficeList;
17use BO\Zmscitizenapi\Models\Collections\OfficeServiceRelationList;
18use BO\Zmscitizenapi\Models\Collections\ServiceList;
19use BO\Zmscitizenapi\Models\Collections\ThinnedScopeList;
20use BO\Zmsentities\Appointment;
21use BO\Zmsentities\Client;
22use BO\Zmsentities\Contact;
23use BO\Zmsentities\Process;
24use BO\Zmsentities\Provider;
25use BO\Zmsentities\Request;
26use BO\Zmsentities\Scope;
27use BO\Zmsentities\Collection\ProviderList;
28use BO\Zmsentities\Collection\RequestList;
29use BO\Zmsentities\Collection\RequestRelationList;
30
31/**
32 * @SuppressWarnings(PHPMD.ExcessiveClassComplexity)
33 * @TODO: Extract class has ExcessiveClassComplexity 101 vs 100
34 */
35class MapperService
36{
37    public static function mapScopeForProvider(int $providerId, ?ThinnedScopeList $scopes): ThinnedScope
38    {
39        if (!$scopes) {
40            return new ThinnedScope();
41        }
42
43        $matchingScope = new ThinnedScope();
44        foreach ($scopes->getScopes() as $scope) {
45            if ($scope->provider && $scope->provider->id === $providerId) {
46                $matchingScope = $scope;
47                break;
48            }
49        }
50
51        return $matchingScope;
52    }
53
54    public static function extractReservationDuration(Scope|ThinnedScope|null $scope): ?int
55    {
56        if ($scope === null) {
57            return null;
58        }
59        if ($scope instanceof ThinnedScope) {
60            $reservationDuration = $scope->getReservationDuration();
61            return $reservationDuration !== null ? (int) $reservationDuration : null;
62        }
63        $reservationDuration = $scope?->toProperty()?->preferences?->appointment?->reservationDuration?->get();
64        return $reservationDuration !== null ? (int) $reservationDuration : null;
65    }
66
67    public static function extractActivationDuration(Scope|ThinnedScope|null $scope): ?int
68    {
69        if ($scope === null) {
70            return null;
71        }
72
73        if ($scope instanceof ThinnedScope) {
74            $activationDuration = $scope->getActivationDuration();
75            if ($activationDuration === null || $activationDuration === '') {
76                return null;
77            }
78            return (int) $activationDuration;
79        }
80
81        $activationDuration = $scope?->toProperty()?->preferences?->appointment?->activationDuration?->get();
82        if ($activationDuration === null || $activationDuration === '') {
83            return null;
84        }
85        return (int) $activationDuration;
86    }
87
88    /**
89     * @SuppressWarnings(PHPMD.CyclomaticComplexity)
90     * @SuppressWarnings(PHPMD.NPathComplexity)
91     * @TODO: Extract mapping logic into specialized mapper classes for each entity type
92     */
93    public static function mapOfficesWithScope(ProviderList $providerList, bool $showUnpublished = false): OfficeList
94    {
95        $offices = [];
96        $scopes = ZmsApiFacadeService::getScopes();
97        if (!$scopes instanceof ThinnedScopeList) {
98            return new OfficeList();
99        }
100
101        foreach ($providerList as $provider) {
102            $providerScope = self::mapScopeForProvider((int) $provider->id, $scopes);
103
104            if (!$showUnpublished && isset($provider->data['public']) && !(bool) $provider->data['public']) {
105                continue;
106            }
107
108            $offices[] = new Office(
109                id: isset($provider->id) ? (int) $provider->id : 0,
110                name: isset($provider->displayName) ? $provider->displayName : (isset($provider->name) ? $provider->name : null),
111                address: isset($provider->data['address']) ? $provider->data['address'] : null,
112                showAlternativeLocations: isset($provider->data['showAlternativeLocations']) ? $provider->data['showAlternativeLocations'] : null,
113                displayNameAlternatives: $provider->data['displayNameAlternatives'] ?? [],
114                organization: $provider->data['organization'] ?? null,
115                organizationUnit: $provider->data['organizationUnit'] ?? null,
116                slotTimeInMinutes: $provider->data['slotTimeInMinutes'] ?? null,
117                geo: isset($provider->data['geo']) ? $provider->data['geo'] : null,
118                disabledByServices: isset($provider->data['dontShowByServices']) ? $provider->data['dontShowByServices'] : [],
119                priority: isset($provider->data['prio']) ? $provider->data['prio'] : 1,
120                scope: isset($providerScope) && !isset($providerScope['errors']) ? new ThinnedScope(
121                    id: isset($providerScope->id) ? (int) $providerScope->id : 0,
122                    provider: isset($providerScope->provider) ? $providerScope->provider : null,
123                    shortName: isset($providerScope->shortName) ? (string) $providerScope->shortName : null,
124                    emailFrom: isset($providerScope->emailFrom) ? (string) $providerScope->emailFrom : null,
125                    emailRequired: isset($providerScope->emailRequired) ? (bool) $providerScope->emailRequired : null,
126                    telephoneActivated: isset($providerScope->telephoneActivated) ? (bool) $providerScope->telephoneActivated : null,
127                    telephoneRequired: isset($providerScope->telephoneRequired) ? (bool) $providerScope->telephoneRequired : null,
128                    customTextfieldActivated: isset($providerScope->customTextfieldActivated) ? (bool) $providerScope->customTextfieldActivated : null,
129                    customTextfieldRequired: isset($providerScope->customTextfieldRequired) ? (bool) $providerScope->customTextfieldRequired : null,
130                    customTextfieldLabel: isset($providerScope->customTextfieldLabel) ? (string) $providerScope->customTextfieldLabel : null,
131                    customTextfield2Activated: isset($providerScope->customTextfield2Activated) ? (bool) $providerScope->customTextfield2Activated : null,
132                    customTextfield2Required: isset($providerScope->customTextfield2Required) ? (bool) $providerScope->customTextfield2Required : null,
133                    customTextfield2Label: isset($providerScope->customTextfield2Label) ? (string) $providerScope->customTextfield2Label : null,
134                    captchaActivatedRequired: isset($providerScope->captchaActivatedRequired) ? (bool) $providerScope->captchaActivatedRequired : null,
135                    infoForAppointment: isset($providerScope->infoForAppointment)
136                        ? ((string) $providerScope->infoForAppointment === '' ? null : (string) $providerScope->infoForAppointment)
137                        : null,
138                    infoForAllAppointments: isset($providerScope->infoForAllAppointments)
139                        ? ((string) $providerScope->infoForAllAppointments === '' ? null : (string) $providerScope->infoForAllAppointments)
140                        : null,
141                    appointmentsPerMail: isset($providerScope->appointmentsPerMail) ? ((string) $providerScope->appointmentsPerMail === '' ? null : (string) $providerScope->appointmentsPerMail) : null,
142                    whitelistedMails: isset($providerScope->whitelistedMails) ? ((string) $providerScope->whitelistedMails === '' ? null : (string) $providerScope->whitelistedMails) : null,
143                    reservationDuration: (int) self::extractReservationDuration($providerScope),
144                    activationDuration: self::extractActivationDuration($providerScope),
145                    hint: isset($providerScope->hint) ? (trim((string) $providerScope->hint) === '' ? null : (string) $providerScope->hint) : null
146                ) : null,
147                maxSlotsPerAppointment: isset($providerScope) && !isset($providerScope['errors']) && isset($providerScope->slotsPerAppointment) ? ((string) $providerScope->slotsPerAppointment === '' ? null : (string) $providerScope->slotsPerAppointment) : null
148            );
149        }
150
151        return new OfficeList($offices);
152    }
153
154    public static function mapCombinable(array $serviceCombinations): ?Combinable
155    {
156        return !empty($serviceCombinations) ? new Combinable($serviceCombinations) : null;
157    }
158
159    /**
160     * Map services with combinations based on request and relation lists.
161     *
162     * @param RequestList $requestList
163     * @param RequestRelationList $relationList
164     * @return ServiceList
165     */
166    public static function mapServicesWithCombinations(
167        RequestList $requestList,
168        RequestRelationList $relationList,
169        bool $showUnpublished = false
170    ): ServiceList {
171        /** @var array<string, array<int>> $servicesProviderIds */
172        $servicesProviderIds = [];
173        foreach ($relationList as $relation) {
174            if (!$showUnpublished && !$relation->isPublic()) {
175                continue;
176            }
177
178            $serviceId = $relation->request->id;
179            $servicesProviderIds[$serviceId] ??= [];
180            $servicesProviderIds[$serviceId][] = $relation->provider->id;
181        }
182
183        /** @var Service[] $services */
184        $services = [];
185        $requestArray = iterator_to_array($requestList);
186        usort($requestArray, function ($a, $b) {
187
188            return $a->getId() <=> $b->getId();
189            // Sorting by service ID (ascending order)
190        });
191        foreach ($requestArray as $service) {
192            if (
193                !$showUnpublished
194                && isset($service->getAdditionalData()['public'])
195                && !$service->getAdditionalData()['public']
196            ) {
197                continue;
198            }
199
200            /** @var array<string, array<int>> $serviceCombinations */
201            $serviceCombinations = [];
202            $combinableData = $service->getAdditionalData()['combinable'] ?? [];
203            foreach ($combinableData as $combinationServiceId) {
204                $commonProviders = array_intersect($servicesProviderIds[$service->getId()] ?? [], $servicesProviderIds[$combinationServiceId] ?? []);
205                $serviceCombinations[$combinationServiceId] = !empty($commonProviders) ? array_values($commonProviders) : [];
206            }
207
208            $combinable = self::mapCombinable($serviceCombinations);
209
210            if (!empty($servicesProviderIds[$service->getId()])) {
211                $services[] = new Service(id: (int) $service->getId(), name: $service->getName(), maxQuantity: $service->getAdditionalData()['maxQuantity'] ?? 1, combinable: $combinable ?? new Combinable());
212            }
213        }
214
215        return new ServiceList($services);
216    }
217
218
219    public static function mapRelations(
220        RequestRelationList $relationList,
221        bool $showUnpublished = false
222    ): OfficeServiceRelationList {
223        $relations = [];
224        foreach ($relationList as $relation) {
225            if (!$showUnpublished && !$relation->isPublic()) {
226                continue;
227            }
228
229            $relations[] = new OfficeServiceRelation(
230                officeId: (int) $relation->provider->id,
231                serviceId: (int) $relation->request->id,
232                slots: intval($relation->slots),
233                public: (bool) $relation->isPublic(),
234                maxQuantity: (int) $relation->maxQuantity
235            );
236        }
237
238        return new OfficeServiceRelationList($relations);
239    }
240
241    /**
242     * @SuppressWarnings(PHPMD.CyclomaticComplexity)
243     * @SuppressWarnings(PHPMD.NPathComplexity)
244     */
245    public static function scopeToThinnedScope(Scope $scope): ThinnedScope
246    {
247        if (!$scope || !isset($scope->id)) {
248            return new ThinnedScope();
249        }
250
251        $thinnedProvider = null;
252        try {
253            if (isset($scope->provider)) {
254                $provider = $scope->provider;
255                $contact = $provider->contact ?? null;
256                $thinnedProvider = new ThinnedProvider(
257                    id: isset($provider->id) ? (int)$provider->id : null,
258                    name: $provider->name ?? null,
259                    displayName: $provider->displayName ?? null,
260                    source: $provider->source ?? null,
261                    contact: $contact ? self::contactToThinnedContact($contact) : null
262                );
263            }
264        } catch (\BO\Zmsentities\Exception\ScopeMissingProvider $e) {
265            $thinnedProvider = null;
266        }
267
268        return new ThinnedScope(
269            id: (int) ($scope->id ?? 0),
270            provider: $thinnedProvider,
271            shortName: isset($scope->shortName) ? (string) $scope->shortName : null,
272            emailFrom: (string) $scope->getEmailFrom() ?? null,
273            emailRequired: isset($scope->data['emailRequired']) ? (bool) $scope->data['emailRequired'] : null,
274            telephoneActivated: isset($scope->data['telephoneActivated']) ? (bool) $scope->data['telephoneActivated'] : null,
275            telephoneRequired: isset($scope->data['telephoneRequired']) ? (bool) $scope->data['telephoneRequired'] : null,
276            customTextfieldActivated: isset($scope->data['customTextfieldActivated']) ? (bool) $scope->data['customTextfieldActivated'] : null,
277            customTextfieldRequired: isset($scope->data['customTextfieldRequired']) ? (bool) $scope->data['customTextfieldRequired'] : null,
278            customTextfieldLabel: isset($scope->data['customTextfieldLabel']) ? (string) $scope->data['customTextfieldLabel'] : null,
279            customTextfield2Activated: isset($scope->data['customTextfield2Activated']) ? (bool) $scope->data['customTextfield2Activated'] : null,
280            customTextfield2Required: isset($scope->data['customTextfield2Required']) ? (bool) $scope->data['customTextfield2Required'] : null,
281            customTextfield2Label: isset($scope->data['customTextfield2Label']) ? (string) $scope->data['customTextfield2Label'] : null,
282            captchaActivatedRequired: isset($scope->data['captchaActivatedRequired']) ? (bool) $scope->data['captchaActivatedRequired'] : null,
283            infoForAppointment: isset($scope->data['infoForAppointment'])
284                ? ((string) $scope->data['infoForAppointment'] === ''
285                    ? null
286                    : (string) $scope->data['infoForAppointment'])
287                : null,
288            infoForAllAppointments: isset($scope->data['infoForAllAppointments'])
289                ? ((string) $scope->data['infoForAllAppointments'] === ''
290                    ? null
291                    : (string) $scope->data['infoForAllAppointments'])
292                : null,
293            slotsPerAppointment: isset($scope->data['slotsPerAppointment'])
294                ? ((string) $scope->data['slotsPerAppointment'] === ''
295                    ? null
296                    : (string) $scope->data['slotsPerAppointment'])
297                : null,
298            appointmentsPerMail: isset($scope->data['appointmentsPerMail']) ? ((string) $scope->data['appointmentsPerMail'] === '' ? null : (string) $scope->data['appointmentsPerMail']) : null,
299            whitelistedMails: isset($scope->data['whitelistedMails']) ? ((string) $scope->data['whitelistedMails'] === '' ? null : (string) $scope->data['whitelistedMails']) : null,
300            reservationDuration: MapperService::extractReservationDuration($scope),
301            activationDuration: MapperService::extractActivationDuration($scope),
302            hint: (trim((string) ($scope->getScopeHint() ?? '')) === '') ? null : (string) $scope->getScopeHint()
303        );
304    }
305
306    /**
307     * @SuppressWarnings(PHPMD.CyclomaticComplexity)
308     * @SuppressWarnings(PHPMD.NPathComplexity)
309     * @TODO: Break down process mapping into smaller, focused methods
310     */
311    public static function processToThinnedProcess(Process $myProcess): ThinnedProcess
312    {
313        if (!$myProcess || !isset($myProcess->id)) {
314            return new ThinnedProcess();
315        }
316
317        $subRequestCounts = [];
318        $mainServiceId = null;
319        $mainServiceName = null;
320        $mainServiceCount = 0;
321        $requests = $myProcess->getRequests() ?? [];
322        if ($requests) {
323            $requests = is_array($requests) ? $requests : iterator_to_array($requests);
324            if (count($requests) > 0) {
325                $mainServiceId = $requests[0]->id;
326                foreach ($requests as $request) {
327                    if ($request->id === $mainServiceId) {
328                        $mainServiceCount++;
329                        if (!$mainServiceName && isset($request->name)) {
330                            $mainServiceName = $request->name;
331                        }
332                    } else {
333                        if (!isset($subRequestCounts[$request->id])) {
334                            $subRequestCounts[$request->id] = [
335                                'id' => (int) $request->id,
336                                'name'  => $request->name,
337                                'count' => 0,
338                            ];
339                        }
340                        $subRequestCounts[$request->id]['count']++;
341                    }
342                }
343            }
344        }
345
346        return new ThinnedProcess(
347            processId: isset($myProcess->id) ? (int) $myProcess->id : 0,
348            timestamp: (isset($myProcess->appointments[0]) && isset($myProcess->appointments[0]->date)) ? strval($myProcess->appointments[0]->date) : null,
349            authKey: isset($myProcess->authKey) ? $myProcess->authKey : null,
350            captchaToken: isset($myProcess->captchaToken) ? $myProcess->captchaToken : null,
351            familyName: (isset($myProcess->clients[0]) && isset($myProcess->clients[0]->familyName)) ? $myProcess->clients[0]->familyName : null,
352            customTextfield: isset($myProcess->customTextfield) ? $myProcess->customTextfield : null,
353            customTextfield2: isset($myProcess->customTextfield2) ? $myProcess->customTextfield2 : null,
354            email: (isset($myProcess->clients[0]) && isset($myProcess->clients[0]->email)) ? $myProcess->clients[0]->email : null,
355            telephone: (isset($myProcess->clients[0]) && isset($myProcess->clients[0]->telephone)) ? $myProcess->clients[0]->telephone : null,
356            officeName: (isset($myProcess->scope->contact) && isset($myProcess->scope->contact->name)) ? $myProcess->scope->contact->name : null,
357            officeId: (isset($myProcess->scope->provider) && isset($myProcess->scope->provider->id)) ? (int) $myProcess->scope->provider->id : 0,
358            scope: isset($myProcess->scope) ? self::scopeToThinnedScope($myProcess->scope) : null,
359            subRequestCounts: isset($subRequestCounts) ? array_values($subRequestCounts) : [],
360            serviceId: isset($mainServiceId) ? (int) $mainServiceId : 0,
361            serviceName: isset($mainServiceName) ? $mainServiceName : null,
362            serviceCount: isset($mainServiceCount) ? $mainServiceCount : 0,
363            status: (isset($myProcess->queue) && isset($myProcess->queue->status)) ? $myProcess->queue->status : null,
364            slotCount: (isset($myProcess->appointments[0]) && isset($myProcess->appointments[0]->slotCount)) ? (int) $myProcess->appointments[0]->slotCount : null
365        );
366    }
367
368    public static function thinnedProcessToProcess(ThinnedProcess $thinnedProcess): Process
369    {
370        if (!$thinnedProcess || !isset($thinnedProcess->processId)) {
371            return new Process();
372        }
373
374        $processEntity = new Process();
375        $processEntity->id = $thinnedProcess->processId;
376        $processEntity->authKey = $thinnedProcess->authKey ?? null;
377        $processEntity->customTextfield = $thinnedProcess->customTextfield ?? null;
378        $processEntity->customTextfield2 = $thinnedProcess->customTextfield2 ?? null;
379        $processEntity->captchaToken = $thinnedProcess->captchaToken ?? null;
380
381        $client = new Client();
382        $client->familyName = $thinnedProcess->familyName ?? null;
383        $client->email = $thinnedProcess->email ?? null;
384        $client->telephone = $thinnedProcess->telephone ?? null;
385        $processEntity->clients = [$client];
386
387        $appointment = new Appointment();
388        $appointment->slotCount = $thinnedProcess->slotCount ?? null;
389        $appointment->date = $thinnedProcess->timestamp ?? null;
390        $processEntity->appointments = [$appointment];
391        $processEntity->scope = self::createScope($thinnedProcess);
392        $processEntity->requests = self::createRequests($thinnedProcess);
393
394        if (isset($thinnedProcess->status)) {
395            $processEntity->queue = new \stdClass();
396            $processEntity->queue->status = $thinnedProcess->status;
397            $processEntity->status = $thinnedProcess->status;
398        }
399
400        $processEntity->lastChange = time();
401        $processEntity->createIP = ClientIpHelper::getClientIp();
402        $processEntity->createTimestamp = time();
403        return $processEntity;
404    }
405
406    private static function createScope(ThinnedProcess $thinnedProcess): Scope
407    {
408        $scope = new Scope();
409        if ($thinnedProcess->scope) {
410            $scope->id = $thinnedProcess->scope->id;
411            $scope->source = \App::$source_name;
412
413            // Set preferences as array structure
414            $scope->preferences = [
415                'client' => [
416                    'appointmentsPerMail' => $thinnedProcess->scope->getAppointmentsPerMail() ?? null,
417                    "whitelistedMails" => $thinnedProcess->scope->getWhitelistedMails() ?? null,
418                    'emailFrom' => $thinnedProcess->scope->getEmailFrom() ?? null,
419                    'emailRequired' => $thinnedProcess->scope->getEmailRequired() ?? false,
420                    'telephoneActivated' => $thinnedProcess->scope->getTelephoneActivated() ?? false,
421                    'telephoneRequired' => $thinnedProcess->scope->getTelephoneRequired() ?? false,
422                    'customTextfieldActivated' => $thinnedProcess->scope->getCustomTextfieldActivated() ?? false,
423                    'customTextfieldRequired' => $thinnedProcess->scope->getCustomTextfieldRequired() ?? false,
424                    'customTextfieldLabel' => $thinnedProcess->scope->getCustomTextfieldLabel() ?? null,
425                    'customTextfield2Activated' => $thinnedProcess->scope->getCustomTextfield2Activated() ?? false,
426                    'customTextfield2Required' => $thinnedProcess->scope->getCustomTextfield2Required() ?? false,
427                    'customTextfield2Label' => $thinnedProcess->scope->getCustomTextfield2Label() ?? null
428                ],
429                'notifications' => [
430                    'enabled' => true
431                ]
432            ];
433        }
434        if (isset($thinnedProcess->officeName)) {
435            $scope->contact = new Contact();
436            $scope->contact->name = $thinnedProcess->officeName;
437        }
438        if (isset($thinnedProcess->officeId)) {
439            $scope->provider = new Provider();
440            $scope->provider->id = $thinnedProcess->officeId;
441            if (isset($thinnedProcess->scope->provider)) {
442                $provider = $thinnedProcess->scope->provider;
443                $scope->provider->name  = $provider->name ?? null;
444                $scope->provider->displayName = $provider->displayName ?? null;
445
446                if (isset($provider->contact)) {
447                    $scope->provider->contact = new Contact();
448                    $scope->provider->contact->street = $provider->contact->street ?? null;
449                    $scope->provider->contact->streetNumber = $provider->contact->streetNumber ?? null;
450                }
451            }
452            $scope->provider->source = \App::$source_name;
453        }
454
455        return $scope;
456    }
457
458    private static function createRequests(ThinnedProcess $thinnedProcess): array
459    {
460        $requests = [];
461        $mainServiceId = $thinnedProcess->serviceId ?? null;
462        $mainServiceName = $thinnedProcess->serviceName ?? null;
463        $mainServiceCount = $thinnedProcess->serviceCount ?? 0;
464
465        for ($i = 0; $i < $mainServiceCount; $i++) {
466            $request = new Request();
467            $request->id = $mainServiceId;
468            $request->name = $mainServiceName;
469            $request->source = \App::$source_name;
470            $requests[] = $request;
471        }
472
473        foreach ($thinnedProcess->subRequestCounts ?? [] as $subRequest) {
474            for ($i = 0; $i < ($subRequest['count'] ?? 0); $i++) {
475                $request = new Request();
476                $request->id = $subRequest['id'];
477                $request->name = $subRequest['name'];
478                $request->source = \App::$source_name;
479                $requests[] = $request;
480            }
481        }
482
483        return $requests;
484    }
485
486    /**
487     * Converts a raw or existing contact object/array into a ThinnedContact model.
488     *
489     * @param object|array $contact
490     * @return ThinnedContact
491     */
492    public static function contactToThinnedContact($contact): ThinnedContact
493    {
494        if (is_array($contact)) {
495            return new ThinnedContact(
496                city: $contact['city'] ?? null,
497                country: $contact['country'] ?? null,
498                name: $contact['name'] ?? null,
499                postalCode: isset($contact['postalCode']) ? (is_null($contact['postalCode']) ? null : (string)$contact['postalCode']) : null,
500                region: $contact['region'] ?? null,
501                street: $contact['street'] ?? null,
502                streetNumber: $contact['streetNumber'] ?? null
503            );
504        }
505
506        return new ThinnedContact(
507            city: $contact->city ?? null,
508            country: $contact->country ?? null,
509            name: $contact->name ?? null,
510            postalCode: isset($contact->postalCode) ? (is_null($contact->postalCode) ? null : (string)$contact->postalCode) : null,
511            region: $contact->region ?? null,
512            street: $contact->street ?? null,
513            streetNumber: $contact->streetNumber ?? null
514        );
515    }
516
517    /**
518     * Convert a Provider object to a ThinnedProvider.
519     *
520     * @param Provider $provider
521     * @return ThinnedProvider
522     */
523    public static function providerToThinnedProvider(Provider $provider): ThinnedProvider
524    {
525        return new ThinnedProvider(
526            id: isset($provider->id) ? (int) $provider->id : null,
527            name: isset($provider->name) ? $provider->name : null,
528            displayName: isset($provider->displayName) ? $provider->displayName : null,
529            source: isset($provider->source) ? $provider->source : null,
530            lon: isset($provider->data['geo']['lon']) ? (float) $provider->data['geo']['lon'] : null,
531            lat: isset($provider->data['geo']['lat']) ? (float) $provider->data['geo']['lat'] : null,
532            contact: isset($provider->contact) ? self::contactToThinnedContact($provider->contact) : null
533        );
534    }
535}