Completed
Push — master ( a5f989...67ea50 )
by Daniel
07:31
created

FormPatchAction::isAssocArray()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 3
c 1
b 0
f 0
dl 0
loc 6
ccs 0
cts 1
cp 0
rs 10
cc 2
nc 2
nop 1
crap 6
1
<?php
2
3
namespace Silverback\ApiComponentBundle\Controller;
4
5
use Silverback\ApiComponentBundle\Dto\Form\FormView;
6
use Silverback\ApiComponentBundle\Entity\Component\Form\Form;
7
use Symfony\Component\Form\FormInterface;
8
use Symfony\Component\HttpFoundation\Request;
9
use Symfony\Component\HttpFoundation\Response;
10
11
class FormPatchAction extends AbstractFormAction
12
{
13
    private function isAssocArray(array $arr): bool
14
    {
15
        if (array() === $arr) {
16
            return false;
17
        }
18
        return array_keys($arr) !== range(0, count($arr) - 1);
19
    }
20
21
    private function arrayIsStrings(array $arr): bool
22
    {
23
        foreach ($arr as $item) {
24
            if (!is_string($item)) {
25
                return false;
26
            }
27
        }
28
        return true;
29
    }
30
31
    private function getNestedKey(FormInterface $form, array $formData): FormInterface
32
    {
33
        $child = $form->get($key = key($formData));
34
        while(is_array($formData = $formData[$key]) && $count = \count($formData)) {
35
            if (!$this->isAssocArray($formData) && $this->arrayIsStrings($formData)) {
36
                break;
37
            }
38
            if ($count === 1) {
39
                $child = $child->get($key = key($formData));
40
                continue;
41
            }
42
            // front-end should submit empty objects for each item in a collection up to the one we are trying to validate
43
            // so let us just get the last item to validate
44
            $key = ($count - 1);
45
            $child = $child->get($key);
46
        }
47
        return $child;
48
    }
49
50
    /**
51
     * @param Request $request
52
     * @param Form $data
53
     * @return Response
54
     */
55
    public function __invoke(Request $request, Form $data)
56
    {
57
        $contentType = $request->headers->get('CONTENT_TYPE');
58
        $_format = $request->attributes->get('_format') ?: $request->getFormat($contentType);
59
60
        $builder = $this->formFactory->create($data);
61
        $form = $builder->getForm();
62
        $formData = $this->deserializeFormData($form, $request->getContent());
63
        $form->submit($formData, false);
64
65
        $dataCount = \count($formData);
66
        if ($dataCount === 1) {
67
            $formItem = $this->getNestedKey($form, $formData);
68
            $data->setForm($formView = new FormView($formItem->createView()));
69
            return $this->getResponse($data, $_format, $this->getFormValid($formView));
70
        }
71
72
        $datum = [];
73
        $valid = true;
74
        foreach ($formData as $key => $value) {
75
            $dataItem = clone $data;
76
            $formItem = $this->getNestedKey($form, $formData[$key]);
77
            $dataItem->setForm($formView = new FormView($formItem->createView()));
78
            $datum[] = $dataItem;
79
            if ($valid && !$this->getFormValid($formView)) {
80
                $valid = false;
81
            }
82
        }
83
84
        return $this->getResponse($datum, $_format, $valid);
85
    }
86
}
87