Completed
Push — feature/directimport-for-initi... ( 8f8de3...e79d3c )
by
unknown
06:55
created

Form   A

Complexity

Total Complexity 28

Size/Duplication

Total Lines 200
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 14

Importance

Changes 2
Bugs 0 Features 0
Metric Value
wmc 28
c 2
b 0
f 0
lcom 1
cbo 14
dl 0
loc 200
rs 10

7 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 9 1
A getForm() 0 5 1
A checkForm() 0 20 2
C getErrorMessages() 0 35 8
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
use Symfony\Component\Validator\ConstraintViolation;
22
use Symfony\Component\Form\FormError;
23
24
/**
25
 * @author   List of contributors <https://github.com/libgraviton/graviton/graphs/contributors>
26
 * @license  http://opensource.org/licenses/gpl-license.php GNU Public License
27
 * @link     http://swisscom.ch
28
 */
29
class Form
30
{
31
    /**
32
     * @var FormFactory
33
     */
34
    private $formFactory;
35
36
    /**
37
     * @var DocumentType
38
     */
39
    private $formType;
40
41
    /**
42
     * @var ValidatorInterface
43
     */
44
    private $validator;
45
46
    /**
47
     * @param FormFactory        $formFactory Factory, providing different file document instances.
48
     * @param DocumentType       $formType    Type of form to be set
49
     * @param ValidatorInterface $validator   Validator to verify correctness of the provided data
50
     */
51
    public function __construct(
52
        FormFactory $formFactory,
53
        DocumentType $formType,
54
        ValidatorInterface $validator
55
    ) {
56
        $this->formFactory = $formFactory;
57
        $this->formType = $formType;
58
        $this->validator = $validator;
59
    }
60
61
    /**
62
     * @param Request       $request request
63
     * @param DocumentModel $model   model
64
     *
65
     * @return \Symfony\Component\Form\Form
66
     */
67
    public function getForm(Request $request, DocumentModel $model)
68
    {
69
        $this->formType->initialize($model->getEntityClass());
70
        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...
71
    }
72
73
    /**
74
     * Validates the provided information against a form.
75
     *
76
     * @param FormInterface           $form           form to check
77
     * @param DocumentModel           $model          Model to determine entity to be used
78
     * @param FormDataMapperInterface $formDataMapper Mapps the entity to form fields
79
     * @param string                  $jsonContent    json data
80
     *
81
     * @throws ValidationException
82
     * @return mixed
83
     */
84
    public function checkForm(
85
        FormInterface $form,
86
        DocumentModel $model,
87
        FormDataMapperInterface $formDataMapper,
88
        $jsonContent
89
    ) {
90
        $document = $formDataMapper->convertToFormData(
91
            $jsonContent,
92
            $model->getEntityClass()
93
        );
94
        $form->submit($document, true);
95
96
        if (!$form->isValid()) {
97
            throw new ValidationException($form->getErrors(true));
98
        } else {
99
            $record = $form->getData();
100
        }
101
102
        return $record;
103
    }
104
105
    /**
106
     * Transform Sf error messages to a simple key value array.
107
     *
108
     * @param \Symfony\Component\Form\Form $form
109
     * @return array
110
     */
111
    public function getErrorMessages(\Symfony\Component\Form\Form $form)
112
    {
113
        $errors = array();
114
        /** @var FormError $error */
115
        foreach ($form->getErrors() as $key => $error) {
116
            $template = $error->getMessageTemplate();
0 ignored issues
show
Bug introduced by
The method getMessageTemplate() does not seem to exist on object<Symfony\Component\Form\FormErrorIterator>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
117
            $parameters = $error->getMessageParameters();
0 ignored issues
show
Bug introduced by
The method getMessageParameters() does not seem to exist on object<Symfony\Component\Form\FormErrorIterator>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

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