* * The factory class must implement :php:`TYPO3\CMS\Form\Domain\Factory\FormFactoryInterface`. * * Scope: frontend * * @see https://docs.typo3.org/permalink/t3viewhelper:typo3-form-render */ final class RenderViewHelper extends AbstractViewHelper { /** * @var bool */ protected $escapeOutput = false; public function __construct( private readonly FormPersistenceManagerInterface $formPersistenceManager, private readonly ExtbaseConfigurationManagerInterface $extbaseConfigurationManager, ) {} public function initializeArguments(): void { $this->registerArgument('persistenceIdentifier', 'string', 'The persistence identifier for the form.'); $this->registerArgument('factoryClass', 'string', 'The fully qualified class name of the factory', false, ArrayFormFactory::class); $this->registerArgument('prototypeName', 'string', 'Name of the prototype to use'); $this->registerArgument('overrideConfiguration', 'array', 'factory specific configuration', false, []); } public function render(): ?string { $persistenceIdentifier = $this->arguments['persistenceIdentifier']; $prototypeName = $this->arguments['prototypeName']; $overrideConfiguration = $this->arguments['overrideConfiguration']; /** @var RequestInterface $request */ $request = $this->renderingContext->getAttribute(ServerRequestInterface::class); // @todo: formvh:render() does not make sense without a persistenceIdentifier, does it? if (!empty($persistenceIdentifier)) { // The ConfigurationManager of ext:form needs ext:extbase ConfigurationManager to retrieve basic TS // settings. ConfigurationManager of extbase should *usually* only be called in extbase context and // needs a Request, which is usually set by extbase bootstrap. // We are however (most likely) not in extbase context here. // To prevent a fallback of extbase ConfigurationManager to $GLOBALS['TYPO3_REQUEST'], we set // the request explicitly here, to then fetch $formSettings from ext:form ConfigurationManager. // $typoScriptSettings is hand over to load() to apply TS overrides for single forms, see #92408. $this->extbaseConfigurationManager->setRequest($request); $typoScriptSettings = $this->extbaseConfigurationManager->getConfiguration(ExtbaseConfigurationManagerInterface::CONFIGURATION_TYPE_SETTINGS, 'form'); $formConfiguration = $this->formPersistenceManager->load($persistenceIdentifier, $typoScriptSettings, $request); ArrayUtility::mergeRecursiveWithOverrule($formConfiguration, $overrideConfiguration); $overrideConfiguration = $formConfiguration; $overrideConfiguration['persistenceIdentifier'] = $persistenceIdentifier; } if (empty($prototypeName)) { $prototypeName = $overrideConfiguration['prototypeName'] ?? 'standard'; } // Even though getContainer() is internal, we can't get container injected here due to static scope /** @var FormFactoryInterface $factory */ $factory = GeneralUtility::getContainer()->get($this->arguments['factoryClass']); $formDefinition = $factory->build($overrideConfiguration, $prototypeName, $request); $form = $formDefinition->bind($request); return $form->render(); } }