1616use Symfony \Component \HttpKernel \Exception \AccessDeniedHttpException ;
1717use Symfony \Component \HttpKernel \Exception \HttpExceptionInterface ;
1818use Symfony \Component \Security \Core \Exception \AccessDeniedException ;
19+ use Symfony \Component \Validator \ConstraintViolationInterface ;
20+ use Symfony \Component \Validator \Exception \ValidationFailedException ;
1921use Symfony \Component \Validator \Exception \ValidatorException ;
2022
2123class ExceptionListener
@@ -36,33 +38,107 @@ public function onKernelException(ExceptionEvent $event): void
3638 {
3739 $ exception = $ event ->getThrowable ();
3840
41+ if ($ exception instanceof ValidationFailedException) {
42+ $ event ->setResponse (
43+ new JsonResponse ([
44+ 'message ' => 'Validation failed ' ,
45+ 'errors ' => $ this ->parseFlatValidationMessage ($ exception ->getMessage ()),
46+ ], 422 )
47+ );
48+
49+ return ;
50+ }
51+
3952 foreach (self ::EXCEPTION_STATUS_MAP as $ class => $ statusCode ) {
4053 if ($ exception instanceof $ class ) {
41- $ status = $ statusCode ?? $ exception ->getStatusCode ();
54+ $ status = $ statusCode ?? (
55+ method_exists ($ exception , 'getStatusCode ' )
56+ ? $ exception ->getStatusCode ()
57+ : 400
58+ );
59+
4260 $ event ->setResponse (
4361 new JsonResponse ([
44- 'message ' => $ exception ->getMessage ()
62+ 'message ' => $ exception ->getMessage (),
63+ 'errors ' => $ this ->parseFlatValidationMessage ($ exception ->getMessage ()),
4564 ], $ status )
4665 );
66+
4767 return ;
4868 }
4969 }
5070
5171 if ($ exception instanceof HttpExceptionInterface) {
5272 $ event ->setResponse (
5373 new JsonResponse ([
54- 'message ' => $ exception ->getMessage ()
74+ 'message ' => $ exception ->getMessage (),
75+ 'errors ' => $ this ->parseFlatValidationMessage ($ exception ->getMessage ()),
5576 ], $ exception ->getStatusCode ())
5677 );
78+
5779 return ;
5880 }
5981
6082 if ($ exception instanceof Exception) {
6183 $ event ->setResponse (
6284 new JsonResponse ([
63- 'message ' => $ exception ->getMessage ()
85+ 'message ' => $ exception ->getMessage (),
86+ 'errors ' => $ this ->parseFlatValidationMessage ($ exception ->getMessage ()),
6487 ], 500 )
6588 );
6689 }
6790 }
91+
92+ /**
93+ * @return array<string, array<int, string>>
94+ */
95+ private function formatViolations (ValidationFailedException $ exception ): array
96+ {
97+ $ errors = [];
98+
99+ foreach ($ exception ->getViolations () as $ violation ) {
100+ /** @var ConstraintViolationInterface $violation */
101+ $ field = $ violation ->getPropertyPath ();
102+ $ message = $ violation ->getMessage ();
103+
104+ if ($ field === '' ) {
105+ $ field = '_global ' ;
106+ }
107+
108+ $ errors [$ field ][] = $ message ;
109+ }
110+
111+ return $ errors ;
112+ }
113+
114+ /**
115+ * @return array<string, array<int, string>>
116+ */
117+ private function parseFlatValidationMessage (string $ message ): array
118+ {
119+ $ errors = [];
120+ $ lines = preg_split ('/\r\n|\r|\n/ ' , $ message ) ?: [];
121+
122+ foreach ($ lines as $ line ) {
123+ $ line = trim ($ line );
124+
125+ if ($ line === '' ) {
126+ continue ;
127+ }
128+
129+ $ parts = explode (': ' , $ line , 2 );
130+
131+ if (count ($ parts ) !== 2 ) {
132+ $ errors ['_global ' ][] = $ line ;
133+ continue ;
134+ }
135+
136+ $ field = trim ($ parts [0 ]);
137+ $ errorMessage = trim ($ parts [1 ]);
138+
139+ $ errors [$ field ][] = $ errorMessage ;
140+ }
141+
142+ return $ errors ;
143+ }
68144}
0 commit comments