Completed
Push — task/valid-class-checks-empty-... ( ffc713 )
by Romain
02:31
created

ClassViewHelper::getPropertyValidClass()   B

Complexity

Conditions 5
Paths 3

Size

Total Lines 19
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 19
rs 8.8571
cc 5
eloc 11
nc 3
nop 1
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\ViewHelpers;
15
16
use Romm\Formz\Configuration\View\Classes\ViewClass;
17
use Romm\Formz\Exceptions\EntryNotFoundException;
18
use Romm\Formz\Exceptions\InvalidEntryException;
19
use Romm\Formz\Exceptions\UnregisteredConfigurationException;
20
use Romm\Formz\Service\ViewHelper\FieldViewHelperService;
21
use Romm\Formz\Service\ViewHelper\FormViewHelperService;
22
use TYPO3\CMS\Core\Utility\GeneralUtility;
23
use TYPO3\CMS\Extbase\Error\Result;
24
use TYPO3\CMS\Extbase\Reflection\ObjectAccess;
25
26
/**
27
 * This view helper is used to manage the properties set in TypoScript at the
28
 * path `config.tx_formz.view.classes`.
29
 *
30
 * Two groups of classes are handled: `errors` and `valid`. The classes will be
31
 * "activated" only when the field has been validated, and the result matches
32
 * the class group.
33
 *
34
 * In the option `name`, you must indicate which class from which group you want
35
 * to manage, for example `errors.has-error` for the class `has-error` from the
36
 * group `errors`.
37
 *
38
 * If the field currently being rendered with Fluid is not using the view helper
39
 * `FieldViewHelper` (all its skeleton is written manually), you may have to use
40
 * the option `field`, which should then contain the name of the field.
41
 *
42
 * Please be aware that this view helper is useful only when used at the same
43
 * level or under the HTML element containing the field selector (usually the
44
 * one with the data attribute `fz-field-container`). You may encounter
45
 * strange behaviours if you do not respect this requirement.
46
 */
47
class ClassViewHelper extends AbstractViewHelper
48
{
49
    const CLASS_ERRORS = 'errors';
50
    const CLASS_VALID = 'valid';
51
52
    /**
53
     * @var array
54
     */
55
    protected static $acceptedClassesNameSpace = [self::CLASS_ERRORS, self::CLASS_VALID];
56
57
    /**
58
     * @var FormViewHelperService
59
     */
60
    protected $formService;
61
62
    /**
63
     * @var FieldViewHelperService
64
     */
65
    protected $fieldService;
66
67
    /**
68
     * @var string
69
     */
70
    protected $fieldName;
71
72
    /**
73
     * @var string
74
     */
75
    protected $classValue;
76
77
    /**
78
     * @var string
79
     */
80
    protected $classNameSpace;
81
82
    /**
83
     * @var string
84
     */
85
    protected $className;
86
87
    /**
88
     * @inheritdoc
89
     */
90
    public function initializeArguments()
91
    {
92
        $this->registerArgument('name', 'string', 'Name of the class which will be managed.', true);
93
        $this->registerArgument('field', 'string', 'Name of the field which will be managed. By default, it is the field from the current `FieldViewHelper`.');
94
    }
95
96
    /**
97
     * @inheritdoc
98
     */
99
    public function render()
100
    {
101
        $this->initializeClassNames();
102
        $this->initializeClassValue();
103
        $this->initializeFieldName();
104
105
        $result = vsprintf(
106
            'fz-%s-%s',
107
            [
108
                $this->classNameSpace,
109
                str_replace(' ', '-', $this->classValue)
110
            ]
111
        );
112
113
        $result .= $this->getFormResultClass();
114
115
        return $result;
116
    }
117
118
    /**
119
     * Will initialize the namespace and name of the class which is given as
120
     * argument to this ViewHelper.
121
     *
122
     * @throws InvalidEntryException
123
     */
124
    protected function initializeClassNames()
125
    {
126
        list($this->classNameSpace, $this->className) = GeneralUtility::trimExplode('.', $this->arguments['name']);
127
128
        if (false === in_array($this->classNameSpace, self::$acceptedClassesNameSpace)) {
129
            throw InvalidEntryException::invalidCssClassNamespace($this->arguments['name'], self::$acceptedClassesNameSpace);
130
        }
131
    }
132
133
    /**
134
     * Fetches the name of the field which should refer to this class. It can
135
     * either be a given value, or be empty if the ViewHelper is used inside a
136
     * `FieldViewHelper`.
137
     *
138
     * @throws EntryNotFoundException
139
     */
140
    protected function initializeFieldName()
141
    {
142
        $this->fieldName = $this->arguments['field'];
143
144
        if (empty($this->fieldName)
145
            && $this->fieldService->fieldContextExists()
146
        ) {
147
            $this->fieldName = $this->fieldService
148
                ->getCurrentField()
149
                ->getName();
150
        }
151
152
        if (null === $this->fieldName) {
153
            throw EntryNotFoundException::classViewHelperFieldNotFound($this->arguments['name']);
154
        }
155
    }
156
157
    /**
158
     * Fetches the corresponding value of this class, which was defined in
159
     * TypoScript.
160
     *
161
     * @throws UnregisteredConfigurationException
162
     */
163
    protected function initializeClassValue()
164
    {
165
        $classesConfiguration = $this->formService
166
            ->getFormObject()
167
            ->getConfiguration()
168
            ->getRootConfiguration()
169
            ->getView()
170
            ->getClasses();
171
172
        /** @var ViewClass $class */
173
        $class = ObjectAccess::getProperty($classesConfiguration, $this->classNameSpace);
174
175
        if (false === $class->hasItem($this->className)) {
176
            throw UnregisteredConfigurationException::cssClassNameNotFound($this->arguments['name'], $this->classNameSpace, $this->className);
177
        }
178
179
        $this->classValue = $class->getItem($this->className);
0 ignored issues
show
Documentation Bug introduced by
It seems like $class->getItem($this->className) of type array is incompatible with the declared type string of property $classValue.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
180
    }
181
182
    /**
183
     * Checks if the form was submitted, then parses its result to handle
184
     * classes depending on TypoScript configuration.
185
     *
186
     * @return string
187
     */
188
    protected function getFormResultClass()
189
    {
190
        $result = '';
191
        $formObject = $this->formService->getFormObject();
192
193
        if ($formObject->formWasSubmitted()
194
            && $formObject->hasFormResult()
195
        ) {
196
            $fieldResult = $formObject->getFormResult()->forProperty($this->fieldName);
197
            $result = $this->getPropertyResultClass($fieldResult);
198
        }
199
200
        return $result;
201
    }
202
203
    /**
204
     * @param Result $propertyResult
205
     * @return string
206
     */
207
    protected function getPropertyResultClass(Result $propertyResult)
208
    {
209
        $result = '';
210
211
        switch ($this->classNameSpace) {
212
            case self::CLASS_ERRORS:
213
                $result = $this->getPropertyErrorClass($propertyResult);
214
                break;
215
            case self::CLASS_VALID:
216
                $result = $this->getPropertyValidClass($propertyResult);
217
                break;
218
        }
219
220
        return $result;
221
    }
222
223
    /**
224
     * @param Result $propertyResult
225
     * @return string
226
     */
227
    protected function getPropertyErrorClass(Result $propertyResult)
228
    {
229
        return (true === $propertyResult->hasErrors())
230
            ? ' ' . $this->classValue
231
            : '';
232
    }
233
234
    /**
235
     * @param Result $propertyResult
236
     * @return string
237
     */
238
    protected function getPropertyValidClass(Result $propertyResult)
239
    {
240
        $result = '';
241
        $formObject = $this->formService->getFormObject();
242
        $field = $formObject->getConfiguration()->getField($this->fieldName);
243
244
        if ($formObject->hasForm()
245
            && false === $propertyResult->hasErrors()
246
            && false === $formObject->getFormResult()->fieldIsDeactivated($field)
247
        ) {
248
            $fieldValue = ObjectAccess::getProperty($formObject->getForm(), $this->fieldName);
249
250
            if (false === empty($fieldValue)) {
251
                $result .= ' ' . $this->classValue;
252
            }
253
        }
254
255
        return $result;
256
    }
257
258
    /**
259
     * @param FormViewHelperService $service
260
     */
261
    public function injectFormService(FormViewHelperService $service)
262
    {
263
        $this->formService = $service;
264
    }
265
266
    /**
267
     * @param FieldViewHelperService $service
268
     */
269
    public function injectFieldService(FieldViewHelperService $service)
270
    {
271
        $this->fieldService = $service;
272
    }
273
}
274