Completed
Push — master ( 8e2f23...0bdc89 )
by Maxime
18s queued 11s
created

FormController::renderElementsAction()   A

Complexity

Conditions 6
Paths 9

Size

Total Lines 32
Code Lines 18

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 18
c 1
b 0
f 0
dl 0
loc 32
rs 9.0444
cc 6
nc 9
nop 1
1
<?php
2
3
/*
4
 * This file is part of Monsieur Biz' Rich Editor plugin for Sylius.
5
 *
6
 * (c) Monsieur Biz <[email protected]>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
12
declare(strict_types=1);
13
14
namespace MonsieurBiz\SyliusRichEditorPlugin\Controller;
15
16
use MonsieurBiz\SyliusRichEditorPlugin\Exception\UiElementNotFoundException;
17
use MonsieurBiz\SyliusRichEditorPlugin\Service\FileUploader;
18
use MonsieurBiz\SyliusRichEditorPlugin\UiElement\RegistryInterface;
19
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
20
use Symfony\Component\Form\Extension\Core\Type\FileType;
21
use Symfony\Component\Form\FormInterface;
22
use Symfony\Component\HttpFoundation\File\UploadedFile;
23
use Symfony\Component\HttpFoundation\JsonResponse;
24
use Symfony\Component\HttpFoundation\Request;
25
use Symfony\Component\HttpFoundation\Response;
26
27
class FormController extends AbstractController
28
{
29
    /**
30
     * @var RegistryInterface
31
     */
32
    private $uiElementRegistry;
33
34
    /**
35
     * FormController constructor.
36
     *
37
     * @param RegistryInterface $uiElementRegistry
38
     */
39
    public function __construct(RegistryInterface $uiElementRegistry)
40
    {
41
        $this->uiElementRegistry = $uiElementRegistry;
42
    }
43
44
    /**
45
     * Generate the form for an element.
46
     *
47
     * @param Request $request
48
     * @param string $code
49
     *
50
     * @return Response
51
     */
52
    public function viewAction(Request $request, string $code): Response
53
    {
54
        // Find UI Element from type
55
        try {
56
            $uiElement = $this->uiElementRegistry->getUiElement($code);
57
        } catch (UiElementNotFoundException $exception) {
58
            throw $this->createNotFoundException($exception->getMessage());
59
        }
60
61
        // Check data in post
62
        $data = [];
63
        $isEdition = $request->isMethod('post');
64
        if ($isEdition && ($data = $request->get('data'))) {
65
            $data = json_decode($data, true);
66
            if (!\is_array($data)) {
67
                throw $this->createNotFoundException();
68
            }
69
        }
70
71
        // Create form depending on UI Element with data
72
        $form = $this->createForm($uiElement->getFormClass(), $data);
73
74
        return new JsonResponse([
75
            'code' => $uiElement->getCode(),
76
            'form_html' => $this->renderView($uiElement->getAdminFormTemplate(), [
77
                'form' => $form->createView(),
78
                'uiElement' => $uiElement,
79
                'data' => $data,
80
                'isEdition' => (int) $isEdition,
81
            ]),
82
        ]);
83
    }
84
85
    /**
86
     * Render all UI elements in HTML.
87
     *
88
     * @param Request $request
89
     *
90
     * @return Response
91
     */
92
    public function renderElementsAction(Request $request): Response
93
    {
94
        if ($uiElements = $request->get('ui_elements')) {
95
            $uiElements = json_decode($uiElements, true);
96
            if (!\is_array($uiElements)) {
97
                throw $this->createNotFoundException();
98
            }
99
        }
100
101
        $result = [];
102
        foreach ($uiElements as $uiElementIndex => $uiElementData) {
103
            $result[$uiElementIndex] = '';
104
105
            if (!isset($uiElementData['code'])) {
106
                continue;
107
            }
108
109
            try {
110
                $uiElement = $this->uiElementRegistry->getUiElement($uiElementData['code']);
111
            } catch (UiElementNotFoundException $exception) {
112
                continue;
113
            }
114
115
            $template = $uiElement->getAdminRenderTemplate();
116
117
            $result[$uiElementIndex] = $this->renderView($template, [
118
                'ui_element' => $uiElement,
119
                'element' => $uiElementData['data'],
120
            ]);
121
        }
122
123
        return new JsonResponse($result);
124
    }
125
126
    /**
127
     * Validate submitted data and return an UI Element JSON if everything is OK.
128
     *
129
     * @param Request $request
130
     * @param FileUploader $fileUploader
131
     * @param string $code
132
     * @param bool $isEdition
133
     *
134
     * @return Response
135
     */
136
    public function submitAction(Request $request, FileUploader $fileUploader, string $code, bool $isEdition): Response
137
    {
138
        // Find UI Element from type
139
        try {
140
            $uiElement = $this->uiElementRegistry->getUiElement($code);
141
        } catch (UiElementNotFoundException $exception) {
142
            throw $this->createNotFoundException($exception->getMessage());
143
        }
144
145
        // Create and validate form
146
        $form = $this->createForm($uiElement->getFormClass());
147
        $form->handleRequest($request);
148
        if (!$form->isSubmitted()) {
149
            throw $this->createNotFoundException();
150
        }
151
152
        // Convert uploaded files to string in form data if necessary, or retrieve current image path if edition
153
        $formData = $this->processFormData($form, $fileUploader, $request->request->get($form->getName()));
154
155
        // Generate form render with error display
156
        if (!$form->isValid()) {
157
            // Manage current uplodaded files to be sure the user will not loose it
158
            $request->request->add(['rich_editor_uploaded_files' => $this->convertFormDataForRequest(
159
                [$form->getName() => $formData]
160
            )]);
161
162
            return new JsonResponse([
163
                'error' => true,
164
                'code' => $uiElement->getCode(),
165
                'form_html' => $this->renderView($uiElement->getAdminFormTemplate(), [
166
                    'form' => $form->createView(),
167
                    'uiElement' => $uiElement,
168
                    'data' => $formData,
169
                    'isEdition' => (int) $isEdition,
170
                ]),
171
            ]);
172
        }
173
174
        $template = $uiElement->getAdminRenderTemplate();
175
176
        $previewHtml = $this->renderView($template, [
177
            'ui_element' => $uiElement,
178
            'element' => $formData,
179
        ]);
180
181
        return new JsonResponse([
182
            'code' => $uiElement->getCode(),
183
            'data' => $formData,
184
            'previewHtml' => $previewHtml,
185
        ]);
186
    }
187
188
    /**
189
     * Build a new form data array with the uploaded file path instead of files, or current filenames on edition.
190
     *
191
     * @param FormInterface $form
192
     * @param FileUploader $fileUploader
193
     * @param array|string $requestData
194
     *
195
     * @return array|mixed|string
196
     */
197
    private function processFormData(FormInterface $form, FileUploader $fileUploader, $requestData)
198
    {
199
        // No child, end of recursivity, return form value or uploaded file path
200
        if (!\count($form->all())) {
201
            return $this->processFormDataWithoutChild($form, $fileUploader, $requestData);
202
        }
203
204
        $processedData = [];
205
        foreach ($form as $child) {
206
            $formData = $this->processFormData($child, $fileUploader, $requestData[$child->getName()] ?? []);
207
            $processedData[$child->getName()] = $formData;
208
        }
209
210
        return $processedData;
211
    }
212
213
    /**
214
     * @param FormInterface $form
215
     * @param FileUploader $fileUploader
216
     * @param array|string $requestData
217
     *
218
     * @return array|mixed|string
219
     */
220
    private function processFormDataWithoutChild(FormInterface $form, FileUploader $fileUploader, $requestData)
221
    {
222
        if ($form->isValid() && $form->getData() instanceof UploadedFile) {
223
            // Upload image selected by user
224
            return $fileUploader->upload($form->getData());
225
        }
226
        if ($form->getConfig()->getType()->getInnerType() instanceof FileType && !empty($requestData)) {
227
            // Check if we have a string value for this fields which is the file path (During edition for example)
228
            return $requestData; // Will return the current filename string
229
        }
230
231
        return $form->getData();
232
    }
233
234
    /**
235
     * Recursively convert multidimensional array to one dimension
236
     * The key is the full input name (ex : `image_collection[images][0][image]`)
237
     * It is used in form with file inputs when the form is not valid to avoid to loose uploaded files.
238
     *
239
     * @param array $formData
240
     * @param string $prefix
241
     *
242
     * @return array
243
     */
244
    private function convertFormDataForRequest(array $formData, string $prefix = ''): array
245
    {
246
        $items = [];
247
248
        foreach ($formData as $key => $value) {
249
            if (\is_array($value)) {
250
                if (empty($prefix)) {
251
                    $items = array_merge($items, $this->convertFormDataForRequest($value, sprintf('%s', $key)));
252
                } else {
253
                    $items = array_merge($items, $this->convertFormDataForRequest($value, sprintf('%s[%s]', $prefix, $key)));
254
                }
255
            } else {
256
                $items[sprintf('%s[%s]', $prefix, $key)] = $value;
257
            }
258
        }
259
260
        return $items;
261
    }
262
}
263