Completed
Push — feature/directimport-for-initi... ( 2461f0 )
by
unknown
11:07
created

Form   A

Complexity

Total Complexity 25

Size/Duplication

Total Lines 186
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 13

Importance

Changes 1
Bugs 0 Features 0
Metric Value
wmc 25
c 1
b 0
f 0
lcom 1
cbo 13
dl 0
loc 186
rs 10

7 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 9 1
A getForm() 0 5 1
A checkForm() 0 20 2
B getErrorMessages() 0 21 5
D checkJsonRequest() 0 37 9
B checkJsonPatchRequest() 0 11 5
A getLastJsonErrorMessage() 0 10 2
1
<?php
2
/**
3
 * base form validator
4
 */
5
6
namespace Graviton\RestBundle\Validator;
7
8
use Graviton\DocumentBundle\Service\FormDataMapperInterface;
9
use Graviton\ExceptionBundle\Exception\InvalidJsonPatchException;
10
use Graviton\ExceptionBundle\Exception\MalformedInputException;
11
use Graviton\ExceptionBundle\Exception\NoInputException;
12
use Graviton\ExceptionBundle\Exception\ValidationException;
13
use Graviton\RestBundle\Model\DocumentModel;
14
use Symfony\Component\Form\FormFactory;
15
use Symfony\Component\Form\FormInterface;
16
use Graviton\DocumentBundle\Form\Type\DocumentType;
17
use Symfony\Component\HttpFoundation\Request;
18
use Symfony\Component\HttpFoundation\Response;
19
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
20
use Symfony\Component\Validator\Validator\ValidatorInterface;
21
22
/**
23
 * @author   List of contributors <https://github.com/libgraviton/graviton/graphs/contributors>
24
 * @license  http://opensource.org/licenses/gpl-license.php GNU Public License
25
 * @link     http://swisscom.ch
26
 */
27
class Form
28
{
29
    /**
30
     * @var FormFactory
31
     */
32
    private $formFactory;
33
34
    /**
35
     * @var DocumentType
36
     */
37
    private $formType;
38
39
    /**
40
     * @var ValidatorInterface
41
     */
42
    private $validator;
43
44
    /**
45
     * @param FormFactory        $formFactory Factory, providing different file document instances.
46
     * @param DocumentType       $formType    Type of form to be set
47
     * @param ValidatorInterface $validator   Validator to verify correctness of the provided data
48
     */
49
    public function __construct(
50
        FormFactory $formFactory,
51
        DocumentType $formType,
52
        ValidatorInterface $validator
53
    ) {
54
        $this->formFactory = $formFactory;
55
        $this->formType = $formType;
56
        $this->validator = $validator;
57
    }
58
59
    /**
60
     * @param Request       $request request
61
     * @param DocumentModel $model   model
62
     *
63
     * @return \Symfony\Component\Form\Form
64
     */
65
    public function getForm(Request $request, DocumentModel $model)
66
    {
67
        $this->formType->initialize($model->getEntityClass());
68
        return $this->formFactory->create($this->formType, null, ['method' => $request->getMethod()]);
0 ignored issues
show
Documentation introduced by
$this->formType is of type object<Graviton\Document...Form\Type\DocumentType>, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
69
    }
70
71
    /**
72
     * Validates the provided information against a form.
73
     *
74
     * @param FormInterface           $form           form to check
75
     * @param DocumentModel           $model          Model to determine entity to be used
76
     * @param FormDataMapperInterface $formDataMapper Mapps the entity to form fields
77
     * @param string                  $jsonContent    json data
78
     *
79
     * @throws ValidationException
80
     * @return mixed
81
     */
82
    public function checkForm(
83
        FormInterface $form,
84
        DocumentModel $model,
85
        FormDataMapperInterface $formDataMapper,
86
        $jsonContent
87
    ) {
88
        $document = $formDataMapper->convertToFormData(
89
            $jsonContent,
90
            $model->getEntityClass()
91
        );
92
        $form->submit($document, true);
93
94
        if (!$form->isValid()) {
95
            throw new ValidationException($form->getErrors(true));
96
        } else {
97
            $record = $form->getData();
98
        }
99
100
        return $record;
101
    }
102
103
    /**
104
     * Transform Sf error messages to a simple key value array.
105
     *
106
     * @param \Symfony\Component\Form\Form $form Form Object
107
     * @return array
108
     */
109
    public function getErrorMessages(\Symfony\Component\Form\Form $form)
110
    {
111
        $errors = array();
112
113
        foreach ($form->getErrors() as $key => $error) {
114
            if ($form->isRoot()) {
115
                $errors['#'][] = $error->getMessage();
116
            } else {
117
                $errors[] = $error->getMessage();
118
            }
119
        }
120
121
        /** @var \Symfony\Component\Form\Form $child */
122
        foreach ($form->all() as $child) {
123
            if (!$child->isValid()) {
124
                $errors[$child->getName()] = $this->getErrorMessages($child);
0 ignored issues
show
Compatibility introduced by
$child of type object<Symfony\Component\Form\FormInterface> is not a sub-type of object<Symfony\Component\Form\Form>. It seems like you assume a concrete implementation of the interface Symfony\Component\Form\FormInterface to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
125
            }
126
        }
127
128
        return $errors;
129
    }
130
131
    /**
132
     * validate raw json input
133
     *
134
     * @param Request  $request  request
135
     * @param Response $response response
136
     * @param string   $content  Alternative request content.
137
     *
138
     * @return void
139
     */
140
    public function checkJsonRequest(Request $request, Response $response, $content = '')
141
    {
142
        if (empty($content)) {
143
            $content = $request->getContent();
144
        }
145
146
        if (is_resource($content)) {
147
            throw new BadRequestHttpException('unexpected resource in validation');
148
        }
149
150
        // is request body empty
151
        if ($content === '') {
152
            $e = new NoInputException();
153
            $e->setResponse($response);
154
            throw $e;
155
        }
156
157
        $input = json_decode($content, true);
158
        if (JSON_ERROR_NONE !== json_last_error()) {
159
            $e = new MalformedInputException($this->getLastJsonErrorMessage());
160
            $e->setErrorType(json_last_error());
161
            $e->setResponse($response);
162
            throw $e;
163
        }
164
        if (!is_array($input)) {
165
            $e = new MalformedInputException('JSON request body must be an object');
166
            $e->setResponse($response);
167
            throw $e;
168
        }
169
170
        if ($request->getMethod() == 'PUT' && array_key_exists('id', $input)) {
171
            // we need to check for id mismatches....
172
            if ($request->attributes->get('id') != $input['id']) {
173
                throw new BadRequestHttpException('Record ID in your payload must be the same');
174
            }
175
        }
176
    }
177
178
    /**
179
     * Validate JSON patch for any object
180
     *
181
     * @param array $jsonPatch json patch as array
182
     *
183
     * @throws InvalidJsonPatchException
184
     * @return void
185
     */
186
    public function checkJsonPatchRequest(array $jsonPatch)
187
    {
188
        foreach ($jsonPatch as $operation) {
189
            if (!is_array($operation)) {
190
                throw new InvalidJsonPatchException('Patch request should be an array of operations.');
191
            }
192
            if (array_key_exists('path', $operation) && trim($operation['path']) == '/id') {
193
                throw new InvalidJsonPatchException('Change/remove of ID not allowed');
194
            }
195
        }
196
    }
197
    /**
198
     * Used for backwards compatibility to PHP 5.4
199
     *
200
     * @return string
201
     */
202
    private function getLastJsonErrorMessage()
203
    {
204
        $message = 'Unable to decode JSON string';
205
206
        if (function_exists('json_last_error_msg')) {
207
            $message = json_last_error_msg();
208
        }
209
210
        return $message;
211
    }
212
}
213