Completed
Push — wip/steps ( e90af4...f019be )
by Romain
02:54
created

FormViewHelper::fluidRenderHiddenReferrerFields()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 25

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 25
rs 9.52
c 0
b 0
f 0
cc 2
nc 2
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\ViewHelpers;
15
16
use Romm\Formz\AssetHandler\AssetHandlerFactory;
17
use Romm\Formz\AssetHandler\Connector\AssetHandlerConnectorManager;
18
use Romm\Formz\AssetHandler\Html\DataAttributesAssetHandler;
19
use Romm\Formz\Core\Core;
20
use Romm\Formz\Exceptions\ClassNotFoundException;
21
use Romm\Formz\Exceptions\InvalidOptionValueException;
22
use Romm\Formz\Form\Definition\Step\Step\Step;
23
use Romm\Formz\Form\FormInterface;
24
use Romm\Formz\Form\FormObject\FormObject;
25
use Romm\Formz\Form\FormObject\FormObjectFactory;
26
use Romm\Formz\Service\ContextService;
27
use Romm\Formz\Service\ControllerService;
28
use Romm\Formz\Service\ExtensionService;
29
use Romm\Formz\Service\FormService;
30
use Romm\Formz\Service\StringService;
31
use Romm\Formz\Service\TimeTrackerService;
32
use Romm\Formz\Service\ViewHelper\Form\FormViewHelperService;
33
use Romm\Formz\ViewHelpers\Step\PreviousLinkViewHelper;
34
use TYPO3\CMS\Core\Page\PageRenderer;
35
use TYPO3\CMS\Core\Utility\GeneralUtility;
36
use TYPO3\CMS\Extbase\Error\Result;
37
use TYPO3\CMS\Extbase\Mvc\Web\Request;
38
use TYPO3\CMS\Fluid\View\StandaloneView;
39
40
/**
41
 * This view helper overrides the default one from Extbase, to include
42
 * everything the extension needs to work properly.
43
 *
44
 * The only difference in Fluid is that the attribute "name" becomes mandatory,
45
 * and must be the exact same name as the form parameter in the controller
46
 * action called when the form is submitted. For instance, if your action looks
47
 * like this: `public function submitAction(ExampleForm $exampleForm) {...}`,
48
 * then the "name" attribute of this view helper must be "exampleForm".
49
 *
50
 * Thanks to the information of the form, the following things are automatically
51
 * handled in this view helper:
52
 *
53
 * - Class
54
 *   A custom class may be added to the form DOM element. If the TypoScript
55
 *   configuration "settings.defaultClass" is set for this form, then the given
56
 *   class will be added to the form element.
57
 *
58
 * - JavaScript
59
 *   A block of JavaScript is built from scratch, which will initialize the
60
 *   form, add validation rules to the fields, and handle activation of the
61
 *   fields validation.
62
 *
63
 * - Data attributes
64
 *   To help integrators customize every aspect they need in CSS, every useful
65
 *   information is put in data attributes in the form DOM element. For example,
66
 *   you can know in real time if the field "email" is valid if the form has the
67
 *   attribute "fz-valid-email"
68
 *
69
 * - CSS
70
 *   A block of CSS is built from scratch, which will handle the fields display,
71
 *   depending on their activation property.
72
 */
73
class FormViewHelper extends \TYPO3\CMS\Fluid\ViewHelpers\FormViewHelper
74
{
75
    /**
76
     * @var bool
77
     */
78
    protected $escapeOutput = false;
79
80
    /**
81
     * @var PageRenderer
82
     */
83
    protected $pageRenderer;
84
85
    /**
86
     * @var FormObject
87
     */
88
    protected $formObject;
89
90
    /**
91
     * @var FormViewHelperService
92
     */
93
    protected $formService;
94
95
    /**
96
     * @var string
97
     */
98
    protected $formObjectClassName;
99
100
    /**
101
     * @var AssetHandlerFactory
102
     */
103
    protected $assetHandlerFactory;
104
105
    /**
106
     * @var TimeTrackerService
107
     */
108
    protected $timeTracker;
109
110
    /**
111
     * @var bool
112
     */
113
    protected $typoScriptIncluded = false;
114
115
    /**
116
     * @var ControllerService
117
     */
118
    protected $controllerService;
119
120
    /**
121
     * @inheritdoc
122
     */
123
    public function initialize()
124
    {
125
        parent::initialize();
126
127
        $this->typoScriptIncluded = ContextService::get()->isTypoScriptIncluded();
128
129
        if (true === $this->typoScriptIncluded) {
130
            $this->timeTracker = TimeTrackerService::getAndStart();
131
132
            $this->formObjectClassName = $this->getFormClassName();
133
            $this->formObject = $this->getFormObject($this->getFormInstance());
134
            $this->timeTracker->logTime('post-config');
135
136
            $this->assetHandlerFactory = AssetHandlerFactory::get($this->formObject, $this->controllerContext);
137
138
            /** @var Request $request */
139
            $request = $this->controllerContext->getRequest();
140
141
            $this->formService->setFormObject($this->formObject);
142
            $this->formService->setRequest($request);
143
            $this->formService->injectFormRequestData();
144
        }
145
146
        /*
147
         * Important: we need to instantiate the page renderer with this instead
148
         * of Extbase object manager (or with an inject function).
149
         *
150
         * This is due to some TYPO3 low level behaviour which overrides the
151
         * page renderer singleton instance, whenever a new request is used. The
152
         * problem is that the instance is not updated on Extbase side.
153
         *
154
         * Using Extbase injection can lead to old page renderer instance being
155
         * used, resulting in a leak of assets inclusion, and maybe more issues.
156
         */
157
        $this->pageRenderer = GeneralUtility::makeInstance(PageRenderer::class);
158
    }
159
160
    /**
161
     * @inheritdoc
162
     */
163
    public function initializeArguments()
164
    {
165
        parent::initializeArguments();
166
167
        // The name attribute becomes mandatory.
168
        $this->overrideArgument('name', 'string', 'Name of the form.', true);
169
        $this->registerArgument('formClassName', 'string', 'Class name of the form.', false);
170
    }
171
172
    /**
173
     * @return string
174
     */
175
    protected function renderViewHelper()
176
    {
177
        if (false === $this->typoScriptIncluded) {
178
            return (ExtensionService::get()->isInDebugMode())
179
                ? ContextService::get()->translate('form.typoscript_not_included.error_message')
180
                : '';
181
        }
182
183
        $result = ($this->formObject->getDefinitionValidationResult()->hasErrors())
184
            // If the form configuration is not valid, we display the errors list.
185
            ? $this->getErrorText()
186
            // Everything is ok, we render the form.
187
            : $this->renderForm(func_get_args());
188
189
        $this->timeTracker->logTime('final');
190
191
        if (ExtensionService::get()->isInDebugMode()) {
192
            $result = $this->timeTracker->getHTMLCommentLogs() . LF . $result;
193
        }
194
195
        $this->formService->resetState();
196
197
        return $result;
198
    }
199
200
    /**
201
     * Will render the whole form and return the HTML result.
202
     *
203
     * @param array $arguments
204
     * @return string
205
     */
206
    final protected function renderForm(array $arguments)
207
    {
208
        /*
209
         * We begin by setting up the form service: request results and form
210
         * instance are inserted in the service, and are used afterwards.
211
         *
212
         * There are only two ways to be sure the values injected are correct:
213
         * when the form was actually submitted by the user, or when the
214
         * argument `object` of the view helper is filled with a form instance.
215
         */
216
        $this->formService->activateFormContext();
217
218
        /*
219
         * @todo
220
         */
221
        $this->formService->checkStepDefinition();
222
223
        /*
224
         * If the form was submitted, applying custom behaviours on its fields.
225
         */
226
        $this->formService->applyBehavioursOnSubmittedForm();
227
228
        /*
229
         * Adding the default class configured in TypoScript configuration to
230
         * the form HTML tag.
231
         */
232
        $this->addDefaultClass();
233
234
        /*
235
         * Handling data attributes that are added to the form HTML tag,
236
         * depending on several parameters.
237
         */
238
        $this->handleDataAttributes();
239
240
        /*
241
         * @todo
242
         */
243
        $this->handleSubsteps();
244
245
        /*
246
         * Including JavaScript and CSS assets in the page renderer.
247
         */
248
        $this->handleAssets();
249
250
        $this->timeTracker->logTime('pre-render');
251
252
        /*
253
         * Getting the result of the original Fluid `FormViewHelper` rendering.
254
         */
255
        $result = $this->getParentRenderResult($arguments);
256
        $renderingResult = $this->formService->getResult();
257
258
        if ($renderingResult->hasErrors()) {
259
            $result = $this->getErrorText($renderingResult);
260
        }
261
262
        /*
263
         * Language files need to be included at the end, because they depend on
264
         * what was used by previous assets.
265
         */
266
        $this->getAssetHandlerConnectorManager()
267
            ->getJavaScriptAssetHandlerConnector()
268
            ->includeLanguageJavaScriptFiles();
269
270
        return $result;
271
    }
272
273
    /**
274
     * Cette méthode reprend exactement ce qui est fait dans la classe parente
275
     * sauf qu'on ne récupère plus les arguments passés dans la requête.
276
     *
277
     * Ce comportement dans Fluid permet normalement de pouvoir revenir en
278
     * arrière dans un formulaire et de récupérer les arguments qui avaient été
279
     * envoyés. Chose qui n'est pas utilisée dans FormZ, on peut donc supprimer
280
     * la construction de `__referrer[arguments]` qui est peut être "lourd"
281
     *
282
     * @see \TYPO3\CMS\Fluid\ViewHelpers\FormViewHelper::renderHiddenReferrerFields
283
     *
284
     * @return string
285
     */
286
    protected function fluidRenderHiddenReferrerFields()
287
    {
288
        $request = $this->renderingContext->getControllerContext()->getRequest();
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface TYPO3Fluid\Fluid\Core\Re...nderingContextInterface as the method getControllerContext() does only exist in the following implementations of said interface: Nimut\TestingFramework\R...RenderingContextFixture, TYPO3\CMS\Fluid\Core\Rendering\RenderingContext, TYPO3\CMS\Fluid\Tests\Un...RenderingContextFixture.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
289
        $extensionName = $request->getControllerExtensionName();
290
        $vendorName = $request->getControllerVendorName();
291
        $controllerName = $request->getControllerName();
292
        $actionName = $request->getControllerActionName();
293
        $actionRequest = [
294
            '@extension' => $extensionName,
295
            '@controller' => $controllerName,
296
            '@action' => $actionName,
297
        ];
298
299
        $result = LF;
300
        $result .= '<input type="hidden" name="' . $this->prefixFieldName('__referrer[@extension]') . '" value="' . $extensionName . '" />' . LF;
301
        if ($vendorName !== null) {
302
            $result .= '<input type="hidden" name="' . $this->prefixFieldName('__referrer[@vendor]') . '" value="' . $vendorName . '" />' . LF;
303
            $actionRequest['@vendor'] = $vendorName;
304
        }
305
        $result .= '<input type="hidden" name="' . $this->prefixFieldName('__referrer[@controller]') . '" value="' . $controllerName . '" />' . LF;
306
        $result .= '<input type="hidden" name="' . $this->prefixFieldName('__referrer[@action]') . '" value="' . $actionName . '" />' . LF;
307
        $result .= '<input type="hidden" name="' . $this->prefixFieldName('__referrer[@request]') . '" value="' . htmlspecialchars($this->hashService->appendHmac(serialize($actionRequest))) . '" />' . LF;
308
309
        return $result;
310
    }
311
312
    /**
313
     * Adds a hidden field to the form rendering, containing the form request
314
     * data as a hashed string (which can be retrieved and used later).
315
     *
316
     * @return string
317
     */
318
    protected function renderHiddenReferrerFields()
319
    {
320
        $result = $this->fluidRenderHiddenReferrerFields();
321
322
        $requestData = $this->formObject->getRequestData();
323
        $requestData->setFormHash($this->formObject->getFormHash());
324
        $value = htmlspecialchars($this->hashService->appendHmac(base64_encode(serialize($requestData->toArray()))));
325
326
        $result .= '<input type="hidden" name="' . $this->prefixFieldName('fz-hash') . '" value="' . $this->formObject->getFormHash() . '" />' . LF;
327
        $result .= '<input type="hidden" name="' . $this->prefixFieldName('formzData') . '" value="' . $value . '" />' . LF;
328
329
        if ($this->formObject->hasSteps()) {
330
            $currentStep = $this->getCurrentStep();
331
332
            if ($currentStep->hasSubsteps()) {
333
                $substepsLevel = FormObjectFactory::get()
334
                    ->getStepService($this->formObject)
335
                    ->getCurrentSubstepDefinition()
336
                    ->getLevel();
337
338
                $result .= '<input type="hidden" fz-substeps-level="1" name="' . $this->prefixFieldName('substepsLevel') . '" value="' . $substepsLevel . '" />' . LF;
339
            }
340
341
            $result .= '<input type="hidden" fz-substeps-previous="1" name="' . $this->prefixFieldName(PreviousLinkViewHelper::PREVIOUS_LINK_PARAMETER) . '" disabled="1" value="1" />' . LF;
342
            $result .= '<input type="submit" style="position: absolute; left: -9999px" />' . LF;
343
        }
344
345
        return $result;
346
    }
347
348
    /**
349
     * Will add a default class to the form element.
350
     *
351
     * To customize the class, take a look at `settings.defaultClass` in the
352
     * form TypoScript configuration.
353
     */
354
    protected function addDefaultClass()
355
    {
356
        $formDefaultClass = $this->formObject
357
            ->getDefinition()
358
            ->getSettings()
359
            ->getDefaultClass();
360
361
        $class = $this->tag->getAttribute('class');
362
363
        if (false === empty($formDefaultClass)) {
364
            $class = (!empty($class) ? $class . ' ' : '') . $formDefaultClass;
365
            $this->tag->addAttribute('class', $class);
366
        }
367
    }
368
369
    /**
370
     * Adds data attributes to the form element, based on several statements,
371
     * like the submitted form values, the validation result and others.
372
     */
373
    protected function handleDataAttributes()
374
    {
375
        $dataAttributesAssetHandler = $this->getDataAttributesAssetHandler();
376
        $dataAttributes = $this->formService->getDataAttributes($dataAttributesAssetHandler);
377
378
        $this->tag->addAttributes($dataAttributes);
379
    }
380
381
    /**
382
     * @todo
383
     */
384
    protected function handleSubsteps()
385
    {
386
        $currentStep = $this->getCurrentStep();
387
388
        if ($currentStep
389
            && $currentStep->hasSubsteps()
390
        ) {
391
            $identifier = FormObjectFactory::get()
392
                ->getStepService($this->formObject)
393
                ->getCurrentSubstepDefinition()
394
                ->getSubstep()
395
                ->getIdentifier();
396
397
            $this->tag->addAttribute('fz-substep', $identifier);
398
        }
399
    }
400
401
    /**
402
     * Will include all JavaScript and CSS assets needed for this form.
403
     */
404
    protected function handleAssets()
405
    {
406
        $assetHandlerConnectorManager = $this->getAssetHandlerConnectorManager();
407
408
        // Default FormZ assets.
409
        $assetHandlerConnectorManager->includeDefaultAssets();
410
411
        // JavaScript assets.
412
        $assetHandlerConnectorManager->getJavaScriptAssetHandlerConnector()
413
            ->generateAndIncludeFormzConfigurationJavaScript()
414
            ->generateAndIncludeJavaScript()
415
            ->generateAndIncludeInlineJavaScript()
416
            ->includeJavaScriptValidationAndConditionFiles();
417
418
        // CSS assets.
419
        $assetHandlerConnectorManager->getCssAssetHandlerConnector()
420
            ->includeGeneratedCss();
421
    }
422
423
    /**
424
     * Will return an error text from a Fluid view.
425
     *
426
     * @param Result $renderingResult
427
     * @return string
428
     */
429
    protected function getErrorText(Result $renderingResult = null)
430
    {
431
        /** @var $view StandaloneView */
432
        $view = Core::instantiate(StandaloneView::class);
433
        $view->setTemplatePathAndFilename(GeneralUtility::getFileAbsFileName('EXT:' . ExtensionService::get()->getExtensionKey() . '/Resources/Private/Templates/Error/ConfigurationErrorBlock.html'));
434
        $layoutRootPath = StringService::get()->getExtensionRelativePath('Resources/Private/Layouts');
435
        $partialRootPath = StringService::get()->getExtensionRelativePath('Resources/Private/Partials');
436
        $view->setLayoutRootPaths([$layoutRootPath]);
437
        $view->setPartialRootPaths([$partialRootPath]);
438
        $view->assign('formObject', $this->formObject);
439
        $view->assign('renderingResult', $renderingResult);
440
441
        return $view->render();
442
    }
443
444
    /**
445
     * Checks the type of the argument `object`, and returns it if everything is
446
     * ok.
447
     *
448
     * @return FormInterface|null
449
     * @throws InvalidOptionValueException
450
     */
451
    protected function getFormObjectArgument()
452
    {
453
        $objectArgument = $this->arguments['object'];
454
455
        if (null === $objectArgument) {
456
            return null;
457
        }
458
459
        if (false === is_object($objectArgument)) {
460
            throw InvalidOptionValueException::formViewHelperWrongFormValueType($objectArgument);
461
        }
462
463
        if (false === $objectArgument instanceof FormInterface) {
464
            throw InvalidOptionValueException::formViewHelperWrongFormValueObjectType($objectArgument);
465
        }
466
467
        $formClassName = $this->getFormClassName();
468
469
        if (false === $objectArgument instanceof $formClassName) {
470
            throw InvalidOptionValueException::formViewHelperWrongFormValueClassName($formClassName, $objectArgument);
471
        }
472
473
        return $objectArgument;
474
    }
475
476
    /**
477
     * Returns the class name of the form object: it is fetched from the action
478
     * of the controller which will be called when submitting this form. It
479
     * means two things:
480
     * - The action must have a parameter which has the exact same name as the
481
     *   form;
482
     * - The parameter must indicate its type.
483
     *
484
     * @return string
485
     * @throws ClassNotFoundException
486
     * @throws InvalidOptionValueException
487
     */
488
    protected function getFormClassName()
489
    {
490
        $formClassName = ($this->hasArgument('formClassName'))
491
            ? $this->arguments['formClassName']
492
            : $this->getFormClassNameFromControllerAction();
493
494
        if (false === class_exists($formClassName)) {
495
            throw ClassNotFoundException::formViewHelperClassNotFound($formClassName, $this->getFormObjectName(), $this->getControllerName(), $this->getControllerActionName());
496
        }
497
498
        if (false === in_array(FormInterface::class, class_implements($formClassName))) {
499
            throw InvalidOptionValueException::formViewHelperWrongFormType($formClassName);
500
        }
501
502
        return $formClassName;
503
    }
504
505
    /**
506
     * Will fetch the name of the controller action argument bound to this
507
     * request.
508
     *
509
     * @return string
510
     */
511
    protected function getFormClassNameFromControllerAction()
512
    {
513
        return $this->controllerService->getFormClassNameFromControllerAction(
514
            $this->getControllerName(),
515
            $this->getControllerActionName(),
516
            $this->getFormObjectName()
517
        );
518
    }
519
520
    /**
521
     * @todo
522
     */
523
    protected function removeFormFieldNamesFromViewHelperVariableContainer()
524
    {
525
        $this->formService->checkStepFields($this->viewHelperVariableContainer);
0 ignored issues
show
Compatibility introduced by
$this->viewHelperVariableContainer of type object<TYPO3Fluid\Fluid\...elperVariableContainer> is not a sub-type of object<TYPO3\CMS\Fluid\C...elperVariableContainer>. It seems like you assume a child class of the class TYPO3Fluid\Fluid\Core\Vi...HelperVariableContainer 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...
526
527
        parent::removeFormFieldNamesFromViewHelperVariableContainer();
528
    }
529
530
    /**
531
     * The identity of the form will be handled by FormZ, thanks to the form
532
     * hash.
533
     *
534
     * @param object $object
535
     * @param string $name
536
     * @return string
537
     */
538
    protected function renderHiddenIdentityField($object, $name)
539
    {
540
        return '';
541
    }
542
543
    /**
544
     * Renders the whole Fluid template.
545
     *
546
     * @param array $arguments
547
     * @return string
548
     */
549
    protected function getParentRenderResult(array $arguments)
550
    {
551
        return call_user_func_array([get_parent_class(), 'render'], $arguments);
552
    }
553
554
    /**
555
     * @return string
556
     */
557
    protected function getControllerName()
558
    {
559
        return ($this->arguments['controller'])
560
            ?: $this->controllerContext
561
                ->getRequest()
562
                ->getControllerObjectName();
563
    }
564
565
    /**
566
     * @return string
567
     */
568
    protected function getControllerActionName()
569
    {
570
        return ($this->arguments['action'])
571
            ?: $this->controllerContext
572
                ->getRequest()
573
                ->getControllerActionName();
574
    }
575
576
    /**
577
     * @return AssetHandlerConnectorManager
578
     */
579
    protected function getAssetHandlerConnectorManager()
580
    {
581
        return AssetHandlerConnectorManager::get($this->pageRenderer, $this->assetHandlerFactory);
582
    }
583
584
    /**
585
     * @return DataAttributesAssetHandler
586
     */
587
    protected function getDataAttributesAssetHandler()
588
    {
589
        /** @var DataAttributesAssetHandler $assetHandler */
590
        $assetHandler = $this->assetHandlerFactory->getAssetHandler(DataAttributesAssetHandler::class);
591
592
        return $assetHandler;
593
    }
594
595
    /**
596
     * @return FormInterface
597
     */
598
    protected function getFormInstance()
599
    {
600
        /*
601
         * If the argument `object` was filled with an instance of Form, it
602
         * becomes the form instance. Otherwise we try to fetch an instance from
603
         * the form with errors list. If there is still no form, we create an
604
         * empty instance.
605
         */
606
        $objectArgument = $this->getFormObjectArgument();
607
608
        if ($objectArgument) {
609
            $form = $objectArgument;
610
        } else {
611
            $submittedForm = FormService::getFormWithErrors($this->getFormClassName());
0 ignored issues
show
Deprecated Code introduced by
The method Romm\Formz\Service\FormS...ce::getFormWithErrors() has been deprecated with message: This method is deprecated, please try not to use it if you can. It will be removed in FormZ v2, where you will have a whole new way to get a validated form.

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
612
613
            $form = $submittedForm ?: Core::get()->getObjectManager()->getEmptyObject($this->getFormClassName());
614
        }
615
616
        return $form;
617
    }
618
619
    /**
620
     * @return Step|null
621
     */
622
    protected function getCurrentStep()
623
    {
624
        /** @var Request $request */
625
        $request = $this->controllerContext->getRequest();
626
627
        return $this->formService->getFormObject()->fetchCurrentStep($request)->getCurrentStep();
628
    }
629
630
    /**
631
     * @param FormInterface $form
632
     * @return FormObject
633
     */
634
    protected function getFormObject(FormInterface $form)
635
    {
636
        return FormObjectFactory::get()->registerAndGetFormInstance($form, $this->getFormObjectName());
637
    }
638
639
    /**
640
     * @param FormViewHelperService $service
641
     */
642
    public function injectFormService(FormViewHelperService $service)
643
    {
644
        $this->formService = $service;
645
    }
646
647
    /**
648
     * @param ControllerService $controllerService
649
     */
650
    public function injectControllerService(ControllerService $controllerService)
651
    {
652
        $this->controllerService = $controllerService;
653
    }
654
}
655