1<?php
2
3declare(strict_types=1);
4
5namespace JMS\Serializer\Handler;
6
7use JMS\Serializer\GraphNavigatorInterface;
8use JMS\Serializer\JsonSerializationVisitor;
9use JMS\Serializer\Visitor\SerializationVisitorInterface;
10use JMS\Serializer\XmlSerializationVisitor;
11use Symfony\Component\Form\Form;
12use Symfony\Component\Form\FormError;
13use Symfony\Component\Translation\TranslatorInterface;
14
15final class FormErrorHandler implements SubscribingHandlerInterface
16{
17    /**
18     * @var TranslatorInterface|null
19     */
20    private $translator;
21
22    /**
23     * @var string
24     */
25    private $translationDomain;
26
27
28    /**
29     * {@inheritdoc}
30     */
31    public static function getSubscribingMethods()
32    {
33        $methods = [];
34        foreach (['xml', 'json'] as $format) {
35            $methods[] = [
36                'direction' => GraphNavigatorInterface::DIRECTION_SERIALIZATION,
37                'type' => 'Symfony\Component\Form\Form',
38                'format' => $format,
39            ];
40            $methods[] = [
41                'direction' => GraphNavigatorInterface::DIRECTION_SERIALIZATION,
42                'type' => 'Symfony\Component\Form\FormError',
43                'format' => $format,
44            ];
45        }
46
47        return $methods;
48    }
49
50    public function __construct(?TranslatorInterface $translator = null, string $translationDomain = 'validators')
51    {
52        $this->translator = $translator;
53        $this->translationDomain = $translationDomain;
54    }
55
56    /**
57     * @param array $type
58     */
59    public function serializeFormToXml(XmlSerializationVisitor $visitor, Form $form, array $type): \DOMElement
60    {
61        $formNode = $visitor->getDocument()->createElement('form');
62
63        $formNode->setAttribute('name', $form->getName());
64
65        $formNode->appendChild($errorsNode = $visitor->getDocument()->createElement('errors'));
66        foreach ($form->getErrors() as $error) {
67            $errorNode = $visitor->getDocument()->createElement('entry');
68            $errorNode->appendChild($this->serializeFormErrorToXml($visitor, $error, []));
69            $errorsNode->appendChild($errorNode);
70        }
71
72        foreach ($form->all() as $child) {
73            if ($child instanceof Form) {
74                if (null !== $node = $this->serializeFormToXml($visitor, $child, [])) {
75                    $formNode->appendChild($node);
76                }
77            }
78        }
79
80        return $formNode;
81    }
82
83    /**
84     * @param array $type
85     */
86    public function serializeFormToJson(JsonSerializationVisitor $visitor, Form $form, array $type): \ArrayObject
87    {
88        return $this->convertFormToArray($visitor, $form);
89    }
90
91    /**
92     * @param array $type
93     */
94    public function serializeFormErrorToXml(XmlSerializationVisitor $visitor, FormError $formError, array $type): \DOMCdataSection
95    {
96        return $visitor->getDocument()->createCDATASection($this->getErrorMessage($formError));
97    }
98
99    /**
100     * @param array $type
101     */
102    public function serializeFormErrorToJson(JsonSerializationVisitor $visitor, FormError $formError, array $type): string
103    {
104        return $this->getErrorMessage($formError);
105    }
106
107    private function getErrorMessage(FormError $error): ?string
108    {
109        if (null === $this->translator) {
110            return $error->getMessage();
111        }
112
113        if (null !== $error->getMessagePluralization()) {
114            return $this->translator->transChoice($error->getMessageTemplate(), $error->getMessagePluralization(), $error->getMessageParameters(), $this->translationDomain);
115        }
116
117        return $this->translator->trans($error->getMessageTemplate(), $error->getMessageParameters(), $this->translationDomain);
118    }
119
120    private function convertFormToArray(SerializationVisitorInterface $visitor, Form $data): \ArrayObject
121    {
122        $form = new \ArrayObject();
123        $errors = [];
124        foreach ($data->getErrors() as $error) {
125            $errors[] = $this->getErrorMessage($error);
126        }
127
128        if ($errors) {
129            $form['errors'] = $errors;
130        }
131
132        $children = [];
133        foreach ($data->all() as $child) {
134            if ($child instanceof Form) {
135                $children[$child->getName()] = $this->convertFormToArray($visitor, $child);
136            }
137        }
138
139        if ($children) {
140            $form['children'] = $children;
141        }
142
143        return $form;
144    }
145}
146