Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
52 / 52
100.00% covered (success)
100.00%
7 / 7
CRAP
100.00% covered (success)
100.00%
1 / 1
EventAttendeesTransformer
100.00% covered (success)
100.00%
51 / 51
100.00% covered (success)
100.00%
7 / 7
33
100.00% covered (success)
100.00%
1 / 1
 supports
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 transformRead
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
2
 transformWrite
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
1 / 1
7
 parseAttendees
100.00% covered (success)
100.00%
14 / 14
100.00% covered (success)
100.00%
1 / 1
2
 decodePayload
100.00% covered (success)
100.00%
6 / 6
100.00% covered (success)
100.00%
1 / 1
7
 parseAttendeesList
100.00% covered (success)
100.00%
14 / 14
100.00% covered (success)
100.00%
1 / 1
11
 parsePermissions
100.00% covered (success)
100.00%
6 / 6
100.00% covered (success)
100.00%
1 / 1
3
1<?php
2
3declare(strict_types=1);
4
5/** @license For full copyright and license information, please see the LICENSE.md file. */
6
7namespace App\Core\Engine\Application\Transformer;
8
9defined('AMMONLY_APP') || exit('Direct script access is forbidden.');
10
11use App\Core\Engine\Domain\Model\FieldMetadata;
12
13/**
14 * Event Attendees UiType Transformer.
15 *
16 * Handles UiType: event_attendees.
17 * Read: converts raw database JSON value to normalized JSON string.
18 * Write: validates and serializes attendees structure into standard JSON.
19 *
20 * @package App\Core\Engine\Application\Transformer
21 */
22final class EventAttendeesTransformer implements UiTypeTransformerInterface
23{
24    /** {@inheritdoc} */
25    public function supports(string $uitypeName): bool
26    {
27        return $uitypeName === 'event_attendees';
28    }
29
30    /** {@inheritdoc} */
31    public function transformRead(mixed $rawValue, FieldMetadata $field): string
32    {
33        $normalized = self::parseAttendees($rawValue);
34        $encoded = json_encode($normalized, JSON_UNESCAPED_UNICODE);
35        return $encoded !== false ? $encoded : '{"attendees":[],"permissions":{}}';
36    }
37
38    /** {@inheritdoc} */
39    public function transformWrite(mixed $inputValue, FieldMetadata $field): ?string
40    {
41        if ($inputValue === null || $inputValue === '' || $inputValue === '{}' || $inputValue === '[]') {
42            return null;
43        }
44
45        $normalized = self::parseAttendees($inputValue);
46        if ($normalized['attendees'] === []) {
47            return null;
48        }
49
50        $encoded = json_encode($normalized, JSON_UNESCAPED_UNICODE);
51        return $encoded !== false ? $encoded : null;
52    }
53
54    /**
55     * Parses and normalizes attendees structure.
56     *
57     * @param mixed $raw Raw input value (JSON string or array).
58     * @return array{attendees: array<int, array<string, mixed>>, permissions: array<string, bool>}
59     */
60    public static function parseAttendees(mixed $raw): array
61    {
62        $data = self::decodePayload($raw);
63        if ($data === null) {
64            return [
65                'attendees'   => [],
66                'permissions' => [
67                    'modify_event'   => false,
68                    'invite_others'  => true,
69                    'see_guest_list' => true,
70                ],
71            ];
72        }
73
74        return [
75            'attendees'   => self::parseAttendeesList($data),
76            'permissions' => self::parsePermissions($data),
77        ];
78    }
79
80    /**
81     * Normalizes raw payload into associative array or null if empty/invalid.
82     *
83     * @return array<string, mixed>|null
84     */
85    private static function decodePayload(mixed $raw): ?array
86    {
87        if (is_array($raw)) {
88            return $raw;
89        }
90        if (is_string($raw) && $raw !== '' && $raw !== '{}' && $raw !== '[]') {
91            $decoded = json_decode($raw, true);
92            return is_array($decoded) ? $decoded : null;
93        }
94
95        return null;
96    }
97
98    /**
99     * Normalizes list of attendee entries.
100     *
101     * @param array<string, mixed> $data
102     * @return list<array{contact_id: int, name: string, email: string}>
103     */
104    private static function parseAttendeesList(array $data): array
105    {
106        $rawAttendees = isset($data['attendees']) && is_array($data['attendees']) ? $data['attendees'] : [];
107        $list = [];
108
109        foreach ($rawAttendees as $att) {
110            if (!is_array($att)) {
111                continue;
112            }
113            $contactId = isset($att['contact_id']) ? (int) $att['contact_id'] : 0;
114            $name = isset($att['name']) ? trim((string) $att['name']) : '';
115            $email = isset($att['email']) ? trim((string) $att['email']) : '';
116
117            if ($contactId > 0 || $email !== '' || $name !== '') {
118                $list[] = [
119                    'contact_id' => $contactId,
120                    'name'       => $name,
121                    'email'      => $email,
122                ];
123            }
124        }
125
126        return $list;
127    }
128
129    /**
130     * @param array<string, mixed> $data
131     * @return array{modify_event: bool, invite_others: bool, see_guest_list: bool}
132     */
133    private static function parsePermissions(array $data): array
134    {
135        $rawPerms = isset($data['permissions']) && is_array($data['permissions']) ? $data['permissions'] : [];
136        return [
137            'modify_event'   => (bool) ($rawPerms['modify_event'] ?? false),
138            'invite_others'  => (bool) ($rawPerms['invite_others'] ?? true),
139            'see_guest_list' => (bool) ($rawPerms['see_guest_list'] ?? true),
140        ];
141    }
142}