Parse and validate AT Protocol Lexicons with DTO generation for Laravel
1<?php
2
3namespace SocialDept\AtpSchema\Services;
4
5use Illuminate\Support\Traits\Macroable;
6use SocialDept\AtpSchema\Contracts\Transformer;
7use SocialDept\AtpSchema\Exceptions\SchemaException;
8
9class ModelMapper
10{
11 use Macroable;
12
13 /**
14 * Registered transformers.
15 *
16 * @var array<string, Transformer>
17 */
18 protected array $transformers = [];
19
20 /**
21 * Register a transformer for a specific type.
22 */
23 public function register(string $type, Transformer $transformer): self
24 {
25 $this->transformers[$type] = $transformer;
26
27 return $this;
28 }
29
30 /**
31 * Register multiple transformers at once.
32 *
33 * @param array<string, Transformer> $transformers
34 */
35 public function registerMany(array $transformers): self
36 {
37 foreach ($transformers as $type => $transformer) {
38 $this->register($type, $transformer);
39 }
40
41 return $this;
42 }
43
44 /**
45 * Transform raw data to model.
46 */
47 public function fromArray(string $type, array $data): mixed
48 {
49 $transformer = $this->getTransformer($type);
50
51 if ($transformer === null) {
52 throw SchemaException::withContext(
53 "No transformer registered for type '{$type}'",
54 ['type' => $type]
55 );
56 }
57
58 return $transformer->fromArray($data);
59 }
60
61 /**
62 * Transform model to raw data.
63 */
64 public function toArray(string $type, mixed $model): array
65 {
66 $transformer = $this->getTransformer($type);
67
68 if ($transformer === null) {
69 throw SchemaException::withContext(
70 "No transformer registered for type '{$type}'",
71 ['type' => $type]
72 );
73 }
74
75 return $transformer->toArray($model);
76 }
77
78 /**
79 * Transform multiple items from arrays.
80 *
81 * @param array<array> $items
82 * @return array<mixed>
83 */
84 public function fromArrayMany(string $type, array $items): array
85 {
86 return array_map(
87 fn (array $item) => $this->fromArray($type, $item),
88 $items
89 );
90 }
91
92 /**
93 * Transform multiple items to arrays.
94 *
95 * @param array<mixed> $items
96 * @return array<array>
97 */
98 public function toArrayMany(string $type, array $items): array
99 {
100 return array_map(
101 fn (mixed $item) => $this->toArray($type, $item),
102 $items
103 );
104 }
105
106 /**
107 * Get transformer for a specific type.
108 */
109 public function getTransformer(string $type): ?Transformer
110 {
111 // Check exact match first
112 if (isset($this->transformers[$type])) {
113 return $this->transformers[$type];
114 }
115
116 // Check if any transformer supports this type
117 foreach ($this->transformers as $transformer) {
118 if ($transformer->supports($type)) {
119 return $transformer;
120 }
121 }
122
123 return null;
124 }
125
126 /**
127 * Check if a transformer is registered for a type.
128 */
129 public function has(string $type): bool
130 {
131 return $this->getTransformer($type) !== null;
132 }
133
134 /**
135 * Unregister a transformer.
136 */
137 public function unregister(string $type): self
138 {
139 unset($this->transformers[$type]);
140
141 return $this;
142 }
143
144 /**
145 * Get all registered transformers.
146 *
147 * @return array<string, Transformer>
148 */
149 public function all(): array
150 {
151 return $this->transformers;
152 }
153
154 /**
155 * Clear all registered transformers.
156 */
157 public function clear(): self
158 {
159 $this->transformers = [];
160
161 return $this;
162 }
163
164 /**
165 * Try to transform from array, return null if transformer not found.
166 */
167 public function tryFromArray(string $type, array $data): mixed
168 {
169 if (! $this->has($type)) {
170 return null;
171 }
172
173 return $this->fromArray($type, $data);
174 }
175
176 /**
177 * Try to transform to array, return null if transformer not found.
178 */
179 public function tryToArray(string $type, mixed $model): ?array
180 {
181 if (! $this->has($type)) {
182 return null;
183 }
184
185 return $this->toArray($type, $model);
186 }
187
188 /**
189 * Get count of registered transformers.
190 */
191 public function count(): int
192 {
193 return count($this->transformers);
194 }
195}