Parse and validate AT Protocol Lexicons with DTO generation for Laravel
at main 5.1 kB view raw
1<?php 2 3namespace SocialDept\AtpSchema\Validation; 4 5class ValidationErrorFormatter 6{ 7 /** 8 * Format errors for Laravel ValidationException. 9 * 10 * @param array<ValidationError> $errors 11 * @return array<string, array<string>> 12 */ 13 public function formatForLaravel(array $errors): array 14 { 15 $formatted = []; 16 17 foreach ($errors as $error) { 18 $field = $this->convertFieldPath($error->getField()); 19 20 if (! isset($formatted[$field])) { 21 $formatted[$field] = []; 22 } 23 24 $formatted[$field][] = $error->getMessage(); 25 } 26 27 return $formatted; 28 } 29 30 /** 31 * Format errors as flat array of messages. 32 * 33 * @param array<ValidationError> $errors 34 * @return array<string> 35 */ 36 public function formatAsMessages(array $errors): array 37 { 38 $messages = []; 39 40 foreach ($errors as $error) { 41 $messages[] = $error->getMessage(); 42 } 43 44 return $messages; 45 } 46 47 /** 48 * Format errors with field context. 49 * 50 * @param array<ValidationError> $errors 51 * @return array<string> 52 */ 53 public function formatWithFields(array $errors): array 54 { 55 $messages = []; 56 57 foreach ($errors as $error) { 58 $messages[] = $error->getField().': '.$error->getMessage(); 59 } 60 61 return $messages; 62 } 63 64 /** 65 * Format errors as detailed array. 66 * 67 * @param array<ValidationError> $errors 68 * @return array<array<string, mixed>> 69 */ 70 public function formatDetailed(array $errors): array 71 { 72 $formatted = []; 73 74 foreach ($errors as $error) { 75 $formatted[] = $error->toArray(); 76 } 77 78 return $formatted; 79 } 80 81 /** 82 * Group errors by field. 83 * 84 * @param array<ValidationError> $errors 85 * @return array<string, array<ValidationError>> 86 */ 87 public function groupByField(array $errors): array 88 { 89 $grouped = []; 90 91 foreach ($errors as $error) { 92 $field = $error->getField(); 93 94 if (! isset($grouped[$field])) { 95 $grouped[$field] = []; 96 } 97 98 $grouped[$field][] = $error; 99 } 100 101 return $grouped; 102 } 103 104 /** 105 * Convert field path from dot notation to Laravel format. 106 */ 107 protected function convertFieldPath(string $path): string 108 { 109 // Remove leading $. if present 110 if (str_starts_with($path, '$.')) { 111 $path = substr($path, 2); 112 } elseif ($path === '$') { 113 return '_root'; 114 } 115 116 // Convert array notation from [0] to .0 117 $path = preg_replace('/\[(\d+)\]/', '.$1', $path); 118 119 return $path; 120 } 121 122 /** 123 * Format a single error. 124 */ 125 public function formatError(ValidationError $error): string 126 { 127 $message = $error->getMessage(); 128 129 if ($error->hasRule()) { 130 $message .= " (Rule: {$error->getRule()})"; 131 } 132 133 if ($error->hasExpected() && $error->hasActual()) { 134 $expected = $this->formatValue($error->getExpected()); 135 $actual = $this->formatValue($error->getActual()); 136 $message .= " [Expected: {$expected}, Got: {$actual}]"; 137 } 138 139 return $message; 140 } 141 142 /** 143 * Format a value for display. 144 */ 145 protected function formatValue(mixed $value): string 146 { 147 if (is_null($value)) { 148 return 'null'; 149 } 150 151 if (is_bool($value)) { 152 return $value ? 'true' : 'false'; 153 } 154 155 if (is_array($value)) { 156 return 'array('.count($value).')'; 157 } 158 159 if (is_object($value)) { 160 return 'object('.get_class($value).')'; 161 } 162 163 if (is_string($value) && strlen($value) > 50) { 164 return substr($value, 0, 50).'...'; 165 } 166 167 return (string) $value; 168 } 169 170 /** 171 * Create human-readable summary. 172 * 173 * @param array<ValidationError> $errors 174 */ 175 public function createSummary(array $errors): string 176 { 177 $count = count($errors); 178 179 if ($count === 0) { 180 return 'No validation errors'; 181 } 182 183 if ($count === 1) { 184 return 'Validation failed: '.$errors[0]->getMessage(); 185 } 186 187 $fields = array_unique(array_map(fn ($error) => $error->getField(), $errors)); 188 $fieldCount = count($fields); 189 190 return "Validation failed with {$count} errors across {$fieldCount} fields"; 191 } 192 193 /** 194 * Format errors as JSON string. 195 * 196 * @param array<ValidationError> $errors 197 */ 198 public function toJson(array $errors, int $options = 0): string 199 { 200 return json_encode($this->formatDetailed($errors), $options); 201 } 202 203 /** 204 * Format errors as pretty JSON string. 205 * 206 * @param array<ValidationError> $errors 207 */ 208 public function toPrettyJson(array $errors): string 209 { 210 return $this->toJson($errors, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES); 211 } 212}