Completed
Push — development ( 7e1249...6494c3 )
by Romain
05:17
created

DataAttributesAssetHandler::addFieldMessageDataAttribute()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 13
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 13
rs 9.4285
c 0
b 0
f 0
cc 2
eloc 7
nc 2
nop 3
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\Reflection\ObjectAccess;
22
23
/**
24
 * This asset handler generates several data attributes which will be added to
25
 * the form element in the Fluid template. Most of these data attributes are
26
 * directly bound to fields and their properties.
27
 *
28
 * Example of data attributes:
29
 *  - Fields values: when a field changes, its new value will be indicated in
30
 *    the form with the attribute: `formz-value-{field name}="value"`.
31
 *  - Fields validation: when a field is considered as valid (it passed all its
32
 *    validation rules), the form gets the attribute: `formz-valid-{field name}`.
33
 *  - Fields errors: when a field validation fails with an error, the form gets
34
 *    the attribute: `formz-error-{field name}-{name of the error}`.
35
 *  - Fields warnings and notices: same as errors.
36
 */
37
class DataAttributesAssetHandler extends AbstractAssetHandler
38
{
39
40
    /**
41
     * Handles the data attributes containing the values of the form fields.
42
     *
43
     * Example: `formz-value-color="blue"`
44
     *
45
     * @param FormInterface|array $formInstance
46
     * @param FormResult          $requestResult
47
     * @return array
48
     */
49
    public function getFieldsValuesDataAttributes($formInstance, FormResult $requestResult)
50
    {
51
        $result = [];
52
        $formConfiguration = $this->getFormObject()->getConfiguration();
53
54
        foreach ($this->getFormObject()->getProperties() as $fieldName) {
55
            if (true === $formConfiguration->hasField($fieldName)
56
                && 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...
57
                && $this->isPropertyGettable($formInstance, $fieldName)
58
            ) {
59
                $value = ObjectAccess::getProperty($formInstance, $fieldName);
60
                $value = (is_array($value))
61
                    ? implode(' ', $value)
62
                    : $value;
63
64
                $result[self::getFieldDataValueKey($fieldName)] = $value;
65
            }
66
        }
67
68
        return $result;
69
    }
70
71
    /**
72
     * Checks if the given field name can be accessed within the form instance,
73
     * whether it is an object or an array.
74
     *
75
     * @param FormInterface|array $formInstance
76
     * @param string              $fieldName
77
     * @return bool
78
     */
79
    protected function isPropertyGettable($formInstance, $fieldName)
80
    {
81
        $objectPropertyIsGettable = (
82
            is_object($formInstance)
83
            && (
84
                in_array($fieldName, get_object_vars($formInstance))
85
                || ObjectAccess::isPropertyGettable($formInstance, $fieldName)
86
            )
87
        );
88
89
        $arrayPropertyGettable = (
90
            is_array($formInstance)
91
            && true === isset($formInstance[$fieldName])
92
        );
93
94
        return $objectPropertyIsGettable || $arrayPropertyGettable;
95
    }
96
97
    /**
98
     * Handles the data attributes for the fields which got errors, warnings and
99
     * notices.
100
     *
101
     * Examples:
102
     * - `formz-error-email="1"`
103
     * - `formz-error-email-rule-default="1"`
104
     *
105
     * @param FormResult $requestResult
106
     * @return array
107
     */
108
    public function getFieldsMessagesDataAttributes(FormResult $requestResult)
109
    {
110
        $result = [];
111
        $formConfiguration = $this->getFormObject()->getConfiguration();
112
113
        foreach ($requestResult->getSubResults() as $fieldName => $fieldResult) {
114
            if (true === $formConfiguration->hasField($fieldName)
115
                && 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...
116
            ) {
117
                if (true === $fieldResult->hasErrors()) {
118
                    $result += $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...
119
                }
120
121
                if (true === $fieldResult->hasWarnings()) {
122
                    $result += $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...
123
                }
124
125
                if (true === $fieldResult->hasNotices()) {
126
                    $result += $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...
127
                }
128
            }
129
        }
130
131
        return $result;
132
    }
133
134
    /**
135
     * @param string                  $fieldName
136
     * @param FormzMessageInterface[] $messages
137
     * @param string                  $type
138
     * @return array
139
     */
140
    protected function addFieldMessageDataAttribute($fieldName, array $messages, $type)
141
    {
142
        $result = [self::getFieldDataMessageKey($fieldName, $type) => '1'];
143
144
        foreach ($messages as $message) {
145
            $validationName = $message->getValidationName();
146
            $messageKey = $message->getMessageKey();
147
148
            $result[self::getFieldDataValidationMessageKey($fieldName, $type, $validationName, $messageKey)] = '1';
149
        }
150
151
        return $result;
152
    }
153
154
    /**
155
     * Handles the data attributes for the fields which are valid.
156
     *
157
     * Example: `formz-valid-email="1"`
158
     *
159
     * @param FormResult $requestResult
160
     * @return array
161
     */
162
    public function getFieldsValidDataAttributes(FormResult $requestResult)
163
    {
164
        $result = [];
165
        $formConfiguration = $this->getFormObject()->getConfiguration();
166
167
        foreach ($formConfiguration->getFields() as $field) {
168
            $fieldName = $field->getFieldName();
169
170
            if (false === $requestResult->fieldIsDeactivated($field)
171
                && false === $requestResult->forProperty($fieldName)->hasErrors()
172
            ) {
173
                $result[self::getFieldDataValidKey($fieldName)] = '1';
174
            }
175
        }
176
177
        return $result;
178
    }
179
180
    /**
181
     * Formats the data value attribute key for a given field name.
182
     *
183
     * @param string $fieldName Name of the field.
184
     * @return string
185
     */
186
    public static function getFieldDataValueKey($fieldName)
187
    {
188
        return 'formz-value-' . StringService::get()->sanitizeString($fieldName);
189
    }
190
191
    /**
192
     * Formats the data valid attribute key for a given field name.
193
     *
194
     * @param string $fieldName Name of the field.
195
     * @return string
196
     */
197
    public static function getFieldDataValidKey($fieldName)
198
    {
199
        return 'formz-valid-' . StringService::get()->sanitizeString($fieldName);
200
    }
201
202
    /**
203
     * Formats the data message attribute key for a given field name.
204
     *
205
     * @param string $fieldName Name of the field.
206
     * @param string $type      Type of the message: `error`, `warning` or `notice`.
207
     * @return string
208
     */
209
    public static function getFieldDataMessageKey($fieldName, $type = 'error')
210
    {
211
        return 'formz-' . $type . '-' . StringService::get()->sanitizeString($fieldName);
212
    }
213
214
    /**
215
     * Formats the data message attribute key for a given failed validation for
216
     * the given field name.
217
     *
218
     * @param string $fieldName
219
     * @param string $type Type of the message: `error`, `warning` or `notice`.
220
     * @param string $validationName
221
     * @param string $messageKey
222
     * @return string
223
     */
224
    public static function getFieldDataValidationMessageKey($fieldName, $type, $validationName, $messageKey)
225
    {
226
        $stringService = StringService::get();
227
228
        return vsprintf(
229
            'formz-%s-%s-%s-%s',
230
            [
231
                $type,
232
                $stringService->sanitizeString($fieldName),
233
                $stringService->sanitizeString($validationName),
234
                $stringService->sanitizeString($messageKey)
235
            ]
236
        );
237
    }
238
}
239