Completed
Push — unit-test-form-view-helper ( d2fd33...868503 )
by Romain
02:29
created

DataAttributesAssetHandler::getFieldSubmissionDone()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 4
rs 10
c 0
b 0
f 0
cc 1
eloc 2
nc 1
nop 0
1
<?php
2
/*
3
 * 2017 Romain CANON <[email protected]>
4
 *
5
 * This file is part of the TYPO3 Formz project.
6
 * It is free software; you can redistribute it and/or modify it
7
 * under the terms of the GNU General Public License, either
8
 * version 3 of the License, or any later version.
9
 *
10
 * For the full copyright and license information, see:
11
 * http://www.gnu.org/licenses/gpl-3.0.html
12
 */
13
14
namespace Romm\Formz\AssetHandler\Html;
15
16
use Romm\Formz\AssetHandler\AbstractAssetHandler;
17
use Romm\Formz\Error\FormResult;
18
use Romm\Formz\Error\FormzMessageInterface;
19
use Romm\Formz\Form\FormInterface;
20
use Romm\Formz\Service\StringService;
21
use TYPO3\CMS\Extbase\Error\Result;
22
use TYPO3\CMS\Extbase\Reflection\ObjectAccess;
23
24
/**
25
 * This asset handler generates several data attributes which will be added to
26
 * the form element in the Fluid template. Most of these data attributes are
27
 * directly bound to fields and their properties.
28
 *
29
 * Example of data attributes:
30
 *  - Fields values: when a field changes, its new value will be indicated in
31
 *    the form with the attribute: `formz-value-{field name}="value"`.
32
 *  - Fields validation: when a field is considered as valid (it passed all its
33
 *    validation rules), the form gets the attribute: `formz-valid-{field name}`.
34
 *  - Fields errors: when a field validation fails with an error, the form gets
35
 *    the attribute: `formz-error-{field name}-{name of the error}`.
36
 *  - Fields warnings and notices: same as errors.
37
 */
38
class DataAttributesAssetHandler extends AbstractAssetHandler
39
{
40
41
    /**
42
     * Handles the data attributes containing the values of the form fields.
43
     *
44
     * Example: `formz-value-color="blue"`
45
     *
46
     * @param FormInterface|array $formInstance
47
     * @param FormResult          $requestResult
48
     * @return array
49
     */
50
    public function getFieldsValuesDataAttributes($formInstance, FormResult $requestResult)
51
    {
52
        $result = [];
53
        $formConfiguration = $this->getFormObject()->getConfiguration();
54
55
        foreach ($this->getFormObject()->getProperties() as $fieldName) {
56
            if (true === $formConfiguration->hasField($fieldName)
57
                && false === $requestResult->fieldIsDeactivated($formConfiguration->getField($fieldName))
0 ignored issues
show
Bug introduced by
It seems like $formConfiguration->getField($fieldName) can be null; however, fieldIsDeactivated() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
58
                && $this->isPropertyGettable($formInstance, $fieldName)
59
            ) {
60
                $value = ObjectAccess::getProperty($formInstance, $fieldName);
61
                $value = (is_array($value))
62
                    ? implode(' ', $value)
63
                    : $value;
64
65
                if (false === empty($value)) {
66
                    $result[self::getFieldDataValueKey($fieldName)] = $value;
67
                }
68
            }
69
        }
70
71
        return $result;
72
    }
73
74
    /**
75
     * Checks if the given field name can be accessed within the form instance,
76
     * whether it is an object or an array.
77
     *
78
     * @param FormInterface|array $formInstance
79
     * @param string              $fieldName
80
     * @return bool
81
     */
82
    protected function isPropertyGettable($formInstance, $fieldName)
83
    {
84
        $objectPropertyIsGettable = (
85
            is_object($formInstance)
86
            && (
87
                in_array($fieldName, get_object_vars($formInstance))
88
                || ObjectAccess::isPropertyGettable($formInstance, $fieldName)
89
            )
90
        );
91
92
        $arrayPropertyGettable = (
93
            is_array($formInstance)
94
            && true === isset($formInstance[$fieldName])
95
        );
96
97
        return $objectPropertyIsGettable || $arrayPropertyGettable;
98
    }
99
100
    /**
101
     * Handles the data attributes for the fields which got errors, warnings and
102
     * notices.
103
     *
104
     * Examples:
105
     * - `formz-error-email="1"`
106
     * - `formz-error-email-rule-default="1"`
107
     *
108
     * @param FormResult $requestResult
109
     * @return array
110
     */
111
    public function getFieldsMessagesDataAttributes(FormResult $requestResult)
112
    {
113
        $result = [];
114
        $formConfiguration = $this->getFormObject()->getConfiguration();
115
116
        foreach ($requestResult->getSubResults() as $fieldName => $fieldResult) {
117
            if (true === $formConfiguration->hasField($fieldName)
118
                && false === $requestResult->fieldIsDeactivated($formConfiguration->getField($fieldName))
0 ignored issues
show
Bug introduced by
It seems like $formConfiguration->getField($fieldName) can be null; however, fieldIsDeactivated() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
119
            ) {
120
                $result += $this->getFieldErrorMessages($fieldName, $fieldResult);
121
                $result += $this->getFieldWarningMessages($fieldName, $fieldResult);
122
                $result += $this->getFieldNoticeMessages($fieldName, $fieldResult);
123
            }
124
        }
125
126
        return $result;
127
    }
128
129
    /**
130
     * @param string $fieldName
131
     * @param Result $fieldResult
132
     * @return array
133
     */
134
    protected function getFieldErrorMessages($fieldName, Result $fieldResult)
135
    {
136
        return (true === $fieldResult->hasErrors())
137
            ? $this->addFieldMessageDataAttribute($fieldName, $fieldResult->getErrors(), 'error')
0 ignored issues
show
Documentation introduced by
$fieldResult->getErrors() is of type array<integer,object<TYP...S\Extbase\Error\Error>>, but the function expects a array<integer,object<Rom...FormzMessageInterface>>.

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...
138
            : [];
139
    }
140
141
    /**
142
     * @param string $fieldName
143
     * @param Result $fieldResult
144
     * @return array
145
     */
146
    protected function getFieldWarningMessages($fieldName, Result $fieldResult)
147
    {
148
        return (true === $fieldResult->hasWarnings())
149
            ? $this->addFieldMessageDataAttribute($fieldName, $fieldResult->getWarnings(), 'warning')
0 ignored issues
show
Documentation introduced by
$fieldResult->getWarnings() is of type array<integer,object<TYP...Extbase\Error\Warning>>, but the function expects a array<integer,object<Rom...FormzMessageInterface>>.

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...
150
            : [];
151
    }
152
153
    /**
154
     * @param string $fieldName
155
     * @param Result $fieldResult
156
     * @return array
157
     */
158
    protected function getFieldNoticeMessages($fieldName, Result $fieldResult)
159
    {
160
        return (true === $fieldResult->hasNotices())
161
            ? $this->addFieldMessageDataAttribute($fieldName, $fieldResult->getNotices(), 'notice')
0 ignored issues
show
Documentation introduced by
$fieldResult->getNotices() is of type array<integer,object<TYP...\Extbase\Error\Notice>>, but the function expects a array<integer,object<Rom...FormzMessageInterface>>.

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...
162
            : [];
163
    }
164
165
    /**
166
     * @param string                  $fieldName
167
     * @param FormzMessageInterface[] $messages
168
     * @param string                  $type
169
     * @return array
170
     */
171
    protected function addFieldMessageDataAttribute($fieldName, array $messages, $type)
172
    {
173
        $result = [self::getFieldDataMessageKey($fieldName, $type) => '1'];
174
175
        foreach ($messages as $message) {
176
            $validationName = $message->getValidationName();
177
            $messageKey = $message->getMessageKey();
178
179
            $result[self::getFieldDataValidationMessageKey($fieldName, $type, $validationName, $messageKey)] = '1';
180
        }
181
182
        return $result;
183
    }
184
185
    /**
186
     * Handles the data attributes for the fields which are valid.
187
     *
188
     * Example: `formz-valid-email="1"`
189
     *
190
     * @param FormResult $requestResult
191
     * @return array
192
     */
193
    public function getFieldsValidDataAttributes(FormResult $requestResult)
194
    {
195
        $result = [];
196
        $formConfiguration = $this->getFormObject()->getConfiguration();
197
198
        foreach ($formConfiguration->getFields() as $field) {
199
            $fieldName = $field->getFieldName();
200
201
            if (false === $requestResult->fieldIsDeactivated($field)
202
                && false === $requestResult->forProperty($fieldName)->hasErrors()
203
            ) {
204
                $result[self::getFieldDataValidKey($fieldName)] = '1';
205
            }
206
        }
207
208
        return $result;
209
    }
210
211
    /**
212
     * Formats the data value attribute key for a given field name.
213
     *
214
     * @param string $fieldName Name of the field.
215
     * @return string
216
     */
217
    public static function getFieldDataValueKey($fieldName)
218
    {
219
        return 'formz-value-' . StringService::get()->sanitizeString($fieldName);
220
    }
221
222
    /**
223
     * Formats the data valid attribute key for a given field name.
224
     *
225
     * @param string $fieldName Name of the field.
226
     * @return string
227
     */
228
    public static function getFieldDataValidKey($fieldName)
229
    {
230
        return 'formz-valid-' . StringService::get()->sanitizeString($fieldName);
231
    }
232
233
    /**
234
     * Formats the data message attribute key for a given field name.
235
     *
236
     * @param string $fieldName Name of the field.
237
     * @param string $type      Type of the message: `error`, `warning` or `notice`.
238
     * @return string
239
     */
240
    public static function getFieldDataMessageKey($fieldName, $type = 'error')
241
    {
242
        return 'formz-' . $type . '-' . StringService::get()->sanitizeString($fieldName);
243
    }
244
245
    /**
246
     * @return string
247
     */
248
    public static function getFieldSubmissionDone()
249
    {
250
        return 'formz-submission-done';
251
    }
252
253
    /**
254
     * Formats the data message attribute key for a given failed validation for
255
     * the given field name.
256
     *
257
     * @param string $fieldName
258
     * @param string $type Type of the message: `error`, `warning` or `notice`.
259
     * @param string $validationName
260
     * @param string $messageKey
261
     * @return string
262
     */
263
    public static function getFieldDataValidationMessageKey($fieldName, $type, $validationName, $messageKey)
264
    {
265
        $stringService = StringService::get();
266
267
        return vsprintf(
268
            'formz-%s-%s-%s-%s',
269
            [
270
                $type,
271
                $stringService->sanitizeString($fieldName),
272
                $stringService->sanitizeString($validationName),
273
                $stringService->sanitizeString($messageKey)
274
            ]
275
        );
276
    }
277
}
278