‪TYPO3CMS  11.5
FormDefinition.php
Go to the documentation of this file.
1 <?php
2 
3 declare(strict_types=1);
4 
5 /*
6  * This file is part of the TYPO3 CMS project.
7  *
8  * It is free software; you can redistribute it and/or modify it under
9  * the terms of the GNU General Public License, either version 2
10  * of the License, or any later version.
11  *
12  * For the full copyright and license information, please read the
13  * LICENSE.txt file that was distributed with this source code.
14  *
15  * The TYPO3 project - inspiring people to share!
16  */
17 
18 /*
19  * Inspired by and partially taken from the Neos.Form package (www.neos.io)
20  */
21 
23 
42 use ‪TYPO3\CMS\Form\Exception as FormException;
44 
226 {
232  protected ‪$renderables = [];
233 
239  protected $finishers = [];
240 
246  protected $processingRules = [];
247 
254  protected $elementsByIdentifier = [];
255 
261  protected $elementDefaultValues = [];
262 
268  protected $rendererClassName = '';
269 
273  protected $typeDefinitions;
274 
278  protected $validatorsDefinition;
279 
283  protected $finishersDefinition;
284 
290  protected $persistenceIdentifier;
291 
301  public function __construct(
302  string ‪$identifier,
303  array $prototypeConfiguration = [],
304  string ‪$type = 'Form',
305  string $persistenceIdentifier = null
306  ) {
307  $this->‪typeDefinitions = $prototypeConfiguration['formElementsDefinition'] ?? [];
308  $this->‪validatorsDefinition = $prototypeConfiguration['validatorsDefinition'] ?? [];
309  $this->finishersDefinition = $prototypeConfiguration['finishersDefinition'] ?? [];
310 
311  if (!is_string(‪$identifier) || strlen(‪$identifier) === 0) {
312  throw new ‪IdentifierNotValidException('The given identifier was not a string or the string was empty.', 1477082503);
313  }
314 
315  $this->identifier = ‪$identifier;
316  $this->type = ‪$type;
317  $this->persistenceIdentifier = (string)$persistenceIdentifier;
318 
319  if ($prototypeConfiguration !== []) {
321  }
322  }
323 
330  protected function ‪initializeFromFormDefaults()
331  {
332  if (!isset($this->‪typeDefinitions[$this->type])) {
333  throw new TypeDefinitionNotFoundException(sprintf('Type "%s" not found. Probably some configuration is missing.', $this->type), 1474905835);
334  }
335  $typeDefinition = $this->‪typeDefinitions[‪$this->type];
336  $this->‪setOptions($typeDefinition);
337  }
338 
348  public function ‪setOptions(array $options, bool $resetFinishers = false)
349  {
350  if (isset($options['rendererClassName'])) {
351  $this->‪setRendererClassName($options['rendererClassName']);
352  }
353  if (isset($options['label'])) {
354  $this->‪setLabel($options['label']);
355  }
356  if (isset($options['renderingOptions'])) {
357  foreach ($options['renderingOptions'] as $key => $value) {
358  $this->‪setRenderingOption($key, $value);
359  }
360  }
361  if (isset($options['finishers'])) {
362  if ($resetFinishers) {
363  $this->finishers = [];
364  }
365  foreach ($options['finishers'] as $finisherConfiguration) {
366  $this->‪createFinisher($finisherConfiguration['identifier'], $finisherConfiguration['options'] ?? []);
367  }
368  }
369 
370  if (isset($options['variants'])) {
371  foreach ($options['variants'] as $variantConfiguration) {
372  $this->‪createVariant($variantConfiguration);
373  }
374  }
375 
377  $options,
378  ['rendererClassName', 'renderingOptions', 'finishers', 'formEditor', 'label', 'variants']
379  );
380  }
381 
395  public function ‪createPage(string ‪$identifier, string $typeName = 'Page'): ‪Page
396  {
397  if (!isset($this->‪typeDefinitions[$typeName])) {
398  throw new ‪TypeDefinitionNotFoundException(sprintf('Type "%s" not found. Probably some configuration is missing.', $typeName), 1474905953);
399  }
400 
401  $typeDefinition = $this->‪typeDefinitions[$typeName];
402 
403  if (!isset($typeDefinition['implementationClassName'])) {
404  throw new TypeDefinitionNotFoundException(sprintf('The "implementationClassName" was not set in type definition "%s".', $typeName), 1477083126);
405  }
406  $implementationClassName = $typeDefinition['implementationClassName'];
407 
408  $classSchema = GeneralUtility::makeInstance(ReflectionService::class)->getClassSchema($implementationClassName);
409  if ($classSchema->hasInjectMethods() || $classSchema->hasInjectProperties() || $classSchema->hasMethod('initializeObject')) {
410  // @deprecated since v11, will be removed in v12 - Fallback for Page implementations that have
411  // inject* or initializeObject methods, since Page prototype needs manual constructor arguments
412  // which can't be mixed with injection in symfony DI. Deprecation is logged by ObjectManager->get().
413  // Drop everything except the makeInstance call below in v12.
414  $objectManager = GeneralUtility::makeInstance(ObjectManager::class);
416  $page = $objectManager->get($implementationClassName, ‪$identifier, $typeName);
417  } else {
419  $page = GeneralUtility::makeInstance($implementationClassName, ‪$identifier, $typeName);
420  }
421 
422  if (isset($typeDefinition['label'])) {
423  $page->setLabel($typeDefinition['label']);
424  }
425 
426  if (isset($typeDefinition['renderingOptions'])) {
427  foreach ($typeDefinition['renderingOptions'] as $key => $value) {
428  $page->setRenderingOption($key, $value);
429  }
430  }
431 
432  if (isset($typeDefinition['variants'])) {
433  foreach ($typeDefinition['variants'] as $variantConfiguration) {
434  $page->createVariant($variantConfiguration);
435  }
436  }
437 
439  $typeDefinition,
440  ['implementationClassName', 'label', 'renderingOptions', 'formEditor', 'variants']
441  );
442 
443  $this->‪addPage($page);
444  return $page;
445  }
446 
456  public function ‪addPage(‪Page $page)
457  {
458  $this->‪addRenderable($page);
459  }
460 
466  public function getPages(): array
467  {
468  return ‪$this->renderables;
469  }
470 
477  public function ‪hasPageWithIndex(int ‪$index): bool
478  {
479  return isset($this->‪renderables[$index]);
480  }
481 
491  public function ‪getPageByIndex(int ‪$index)
492  {
493  if (!$this->‪hasPageWithIndex($index)) {
494  throw new FormException(sprintf('There is no page with an index of %d', ‪$index), 1329233627);
495  }
496  return $this->‪renderables[‪$index];
497  }
498 
504  public function ‪addFinisher(‪FinisherInterface $finisher)
505  {
506  $this->finishers[] = $finisher;
507  }
508 
515  public function ‪createFinisher(string $finisherIdentifier, array $options = []): ‪FinisherInterface
516  {
517  if (isset($this->finishersDefinition[$finisherIdentifier]) && is_array($this->finishersDefinition[$finisherIdentifier]) && isset($this->finishersDefinition[$finisherIdentifier]['implementationClassName'])) {
518  $implementationClassName = $this->finishersDefinition[$finisherIdentifier]['implementationClassName'];
519  $defaultOptions = $this->finishersDefinition[$finisherIdentifier]['options'] ?? [];
520  ‪ArrayUtility::mergeRecursiveWithOverrule($defaultOptions, $options);
521 
522  $classSchema = GeneralUtility::makeInstance(ReflectionService::class)->getClassSchema($implementationClassName);
523  if (!$classSchema->hasMethod('setFinisherIdentifier')
524  || ($classSchema->hasMethod('__construct')
525  && count($classSchema->getMethod('__construct')->getParameters()) >= 1
526  && (string)$classSchema->getMethod('__construct')->getFirstParameter()->getType() === 'string')
527  ) {
528  // @deprecated since v11, will be removed in v12 - Fallback for Finishers that do not
529  // extend AbstractFinisher and have no setFinisherIdentifier() method or still have a
530  // constructor argument to set the finisher name: Mixing manual constructor arguments
531  // with injection is not allowed by symfony DI, so we dropped the constructor argument
532  // for v11 to keep the injection.
533  // The above if detects "old" finisher and falls back to ObjectManager, which will log
534  // a deprecation.
535  // Drop the if with this body in v12, keep else body, clean up
536  // AbstractFinisher->setFinisherIdentifier() and enable the method in FinisherInterface.
537  $objectManager = GeneralUtility::makeInstance(ObjectManager::class);
539  $finisher = $objectManager->get($implementationClassName, $finisherIdentifier);
540  if ($classSchema->hasMethod('setFinisherIdentifier')) {
541  $finisher->setFinisherIdentifier($finisherIdentifier);
542  }
543  } else {
545  $finisher = GeneralUtility::makeInstance($implementationClassName);
546  $finisher->setFinisherIdentifier($finisherIdentifier);
547  }
548 
549  $finisher->setOptions($defaultOptions);
550  $this->‪addFinisher($finisher);
551  return $finisher;
552  }
553  throw new ‪FinisherPresetNotFoundException('The finisher preset identified by "' . $finisherIdentifier . '" could not be found, or the implementationClassName was not specified.', 1328709784);
554  }
555 
561  public function ‪getFinishers(): array
562  {
563  return $this->finishers;
564  }
565 
573  public function ‪registerRenderable(‪RenderableInterface $renderable)
574  {
575  if ($renderable instanceof ‪FormElementInterface) {
576  if (isset($this->‪elementsByIdentifier[$renderable->‪getIdentifier()])) {
577  throw new ‪DuplicateFormElementException(sprintf('A form element with identifier "%s" is already part of the form.', $renderable->‪getIdentifier()), 1325663761);
578  }
579  $this->‪elementsByIdentifier[$renderable->‪getIdentifier()] = $renderable;
580  }
581  }
582 
589  public function ‪unregisterRenderable(RenderableInterface $renderable)
590  {
591  if ($renderable instanceof FormElementInterface) {
592  unset($this->‪elementsByIdentifier[$renderable->getIdentifier()]);
593  }
594  }
595 
601  public function getElements(): array
602  {
603  return $this->elementsByIdentifier;
604  }
605 
614  public function ‪getElementByIdentifier(string $elementIdentifier)
615  {
616  return $this->‪elementsByIdentifier[$elementIdentifier] ?? null;
617  }
618 
626  public function ‪addElementDefaultValue(string $elementIdentifier, $defaultValue)
627  {
628  $this->elementDefaultValues = ‪ArrayUtility::setValueByPath(
629  $this->elementDefaultValues,
630  $elementIdentifier,
631  $defaultValue,
632  '.'
633  );
634  }
635 
644  public function ‪getElementDefaultValueByIdentifier(string $elementIdentifier)
645  {
646  return ‪ObjectAccess::getPropertyPath($this->elementDefaultValues, $elementIdentifier);
647  }
648 
655  public function ‪movePageBefore(‪Page $pageToMove, ‪Page $referencePage)
656  {
657  $this->‪moveRenderableBefore($pageToMove, $referencePage);
658  }
659 
666  public function ‪movePageAfter(‪Page $pageToMove, ‪Page $referencePage)
667  {
668  $this->‪moveRenderableAfter($pageToMove, $referencePage);
669  }
670 
676  public function ‪removePage(‪Page $pageToRemove)
677  {
678  $this->‪removeRenderable($pageToRemove);
679  }
680 
688  public function ‪bind(‪Request $request): ‪FormRuntime
689  {
690  $formRuntime = GeneralUtility::makeInstance(FormRuntime::class);
691  $formRuntime->setFormDefinition($this);
692  $formRuntime->setRequest($request);
693  $formRuntime->initialize();
694  return $formRuntime;
695  }
696 
701  public function ‪getProcessingRule(string $propertyPath): ‪ProcessingRule
702  {
703  if (!isset($this->‪processingRules[$propertyPath])) {
704  $this->‪processingRules[$propertyPath] = GeneralUtility::makeInstance(ProcessingRule::class);
705  }
706  return $this->‪processingRules[$propertyPath];
707  }
708 
715  public function getProcessingRules(): array
716  {
717  return $this->processingRules;
718  }
719 
724  public function getTypeDefinitions(): array
725  {
726  return $this->typeDefinitions;
727  }
728 
733  public function getValidatorsDefinition(): array
734  {
735  return $this->validatorsDefinition;
736  }
737 
744  public function ‪getPersistenceIdentifier(): string
745  {
746  return $this->persistenceIdentifier;
747  }
748 
754  public function ‪setRendererClassName(string $rendererClassName)
755  {
756  $this->rendererClassName = $rendererClassName;
757  }
758 
764  public function ‪getRendererClassName(): string
765  {
766  return $this->rendererClassName;
767  }
768 }
‪TYPO3\CMS\Form\Domain\Model\Renderable\AbstractRenderable\$index
‪int $index
Definition: AbstractRenderable.php:78
‪TYPO3\CMS\Form\Domain\Model\Exception\FinisherPresetNotFoundException
Definition: FinisherPresetNotFoundException.php:26
‪TYPO3\CMS\Form\Domain\Model\FormDefinition\typeDefinitions
‪array< string, function getTypeDefinitions():array { return $this-> typeDefinitions
Definition: FormDefinition.php:716
‪TYPO3\CMS\Form\Domain\Model\Exception\FormDefinitionConsistencyException
Definition: FormDefinitionConsistencyException.php:26
‪TYPO3\CMS\Form\Domain\Model\FormDefinition\createFinisher
‪FinisherInterface createFinisher(string $finisherIdentifier, array $options=[])
Definition: FormDefinition.php:505
‪TYPO3\CMS\Form\Domain\Runtime\FormRuntime
Definition: FormRuntime.php:109
‪TYPO3\CMS\Form\Domain\Exception\TypeDefinitionNotFoundException
Definition: TypeDefinitionNotFoundException.php:30
‪TYPO3\CMS\Form\Domain\Model\FormDefinition\addFinisher
‪addFinisher(FinisherInterface $finisher)
Definition: FormDefinition.php:494
‪TYPO3\CMS\Form\Domain\Model\FormDefinition\removePage
‪removePage(Page $pageToRemove)
Definition: FormDefinition.php:666
‪TYPO3\CMS\Form\Domain\Model\Renderable\AbstractCompositeRenderable
Definition: AbstractCompositeRenderable.php:34
‪TYPO3\CMS\Form\Domain\Model\Renderable\AbstractCompositeRenderable\moveRenderableAfter
‪moveRenderableAfter(RenderableInterface $renderableToMove, RenderableInterface $referenceRenderable)
Definition: AbstractCompositeRenderable.php:108
‪TYPO3\CMS\Form\Domain\Model\Renderable\AbstractRenderable\setRenderingOption
‪mixed setRenderingOption(string $key, $value)
Definition: AbstractRenderable.php:280
‪TYPO3\CMS\Form\Domain\Finishers\FinisherInterface
Definition: FinisherInterface.php:31
‪TYPO3\CMS\Form\Domain\Model\FormDefinition\setOptions
‪setOptions(array $options, bool $resetFinishers=false)
Definition: FormDefinition.php:338
‪TYPO3\CMS\Form\Domain\Model\FormDefinition\movePageAfter
‪movePageAfter(Page $pageToMove, Page $referencePage)
Definition: FormDefinition.php:656
‪TYPO3\CMS\Form\Domain\Model\Renderable\AbstractCompositeRenderable\removeRenderable
‪removeRenderable(RenderableInterface $renderableToRemove)
Definition: AbstractCompositeRenderable.php:162
‪TYPO3\CMS\Form\Domain\Model\Renderable\AbstractRenderable\setLabel
‪setLabel(string $label)
Definition: AbstractRenderable.php:407
‪TYPO3\CMS\Form\Domain\Exception\IdentifierNotValidException
Definition: IdentifierNotValidException.php:30
‪TYPO3\CMS\Form\Domain\Model\FormDefinition\renderables
‪array< int, function getPages():array { return $this-> renderables
Definition: FormDefinition.php:458
‪TYPO3\CMS\Form\Domain\Model\Renderable\AbstractCompositeRenderable\moveRenderableBefore
‪moveRenderableBefore(RenderableInterface $renderableToMove, RenderableInterface $referenceRenderable)
Definition: AbstractCompositeRenderable.php:72
‪TYPO3\CMS\Form\Domain\Model\FormDefinition\bind
‪FormRuntime bind(Request $request)
Definition: FormDefinition.php:678
‪TYPO3\CMS\Form\Domain\Model\FormDefinition\setRendererClassName
‪setRendererClassName(string $rendererClassName)
Definition: FormDefinition.php:744
‪TYPO3\CMS\Form\Domain\Model\FormDefinition\createPage
‪Page createPage(string $identifier, string $typeName='Page')
Definition: FormDefinition.php:385
‪TYPO3\CMS\Form\Domain\Model\FormDefinition\getPersistenceIdentifier
‪string getPersistenceIdentifier()
Definition: FormDefinition.php:734
‪TYPO3\CMS\Core\Utility\ArrayUtility\mergeRecursiveWithOverrule
‪static mergeRecursiveWithOverrule(array &$original, array $overrule, $addKeys=true, $includeEmptyValues=true, $enableUnsetFeature=true)
Definition: ArrayUtility.php:654
‪TYPO3\CMS\Form\Domain\Model\Renderable\AbstractRenderable\$type
‪string $type
Definition: AbstractRenderable.php:48
‪TYPO3\CMS\Form\Domain\Model\FormDefinition\hasPageWithIndex
‪bool hasPageWithIndex(int $index)
Definition: FormDefinition.php:467
‪TYPO3\CMS\Form\Domain\Model\Renderable\RootRenderableInterface\getIdentifier
‪string getIdentifier()
‪TYPO3\CMS\Extbase\Reflection\ObjectAccess
Definition: ObjectAccess.php:39
‪TYPO3\CMS\Form\Domain\Model\FormDefinition\getElementDefaultValueByIdentifier
‪mixed getElementDefaultValueByIdentifier(string $elementIdentifier)
Definition: FormDefinition.php:634
‪TYPO3\CMS\Form\Domain\Model\FormElements\Page
Definition: Page.php:44
‪TYPO3\CMS\Form\Domain\Model\Renderable\RenderableInterface
Definition: RenderableInterface.php:32
‪TYPO3\CMS\Extbase\Reflection\ReflectionService
Definition: ReflectionService.php:28
‪TYPO3\CMS\Form\Domain\Model
‪TYPO3\CMS\Form\Exception
Definition: Exception.php:25
‪TYPO3\CMS\Form\Domain\Model\FormDefinition\unregisterRenderable
‪unregisterRenderable(RenderableInterface $renderable)
Definition: FormDefinition.php:579
‪TYPO3\CMS\Form\Domain\Model\FormDefinition\getElementByIdentifier
‪FormElementInterface null getElementByIdentifier(string $elementIdentifier)
Definition: FormDefinition.php:604
‪TYPO3\CMS\Form\Domain\Model\FormElements\FormElementInterface
Definition: FormElementInterface.php:40
‪TYPO3\CMS\Form\Domain\Model\FormDefinition\getProcessingRule
‪ProcessingRule getProcessingRule(string $propertyPath)
Definition: FormDefinition.php:691
‪TYPO3\CMS\Form\Domain\Model\FormDefinition\validatorsDefinition
‪array< string, function getValidatorsDefinition():array { return $this-> validatorsDefinition
Definition: FormDefinition.php:725
‪TYPO3\CMS\Form\Domain\Model\FormDefinition\addElementDefaultValue
‪addElementDefaultValue(string $elementIdentifier, $defaultValue)
Definition: FormDefinition.php:616
‪TYPO3\CMS\Form\Domain\Model\Renderable\AbstractCompositeRenderable\$renderables
‪TYPO3 CMS Form Domain Model Renderable RenderableInterface[] $renderables
Definition: AbstractCompositeRenderable.php:39
‪TYPO3\CMS\Form\Domain\Model\FormDefinition\getFinishers
‪list< FinisherInterface > getFinishers()
Definition: FormDefinition.php:551
‪TYPO3\CMS\Core\Utility\ArrayUtility\setValueByPath
‪static array setValueByPath(array $array, $path, $value, $delimiter='/')
Definition: ArrayUtility.php:272
‪TYPO3\CMS\Form\Domain\Model\FormDefinition
Definition: FormDefinition.php:226
‪TYPO3\CMS\Form\Domain\Model\Renderable\VariableRenderableInterface
Definition: VariableRenderableInterface.php:29
‪TYPO3\CMS\Form\Domain\Runtime\FormRuntime
Definition: FormSession.php:18
‪TYPO3\CMS\Form\Domain\Model\FormDefinition\getRendererClassName
‪string getRendererClassName()
Definition: FormDefinition.php:754
‪TYPO3\CMS\Core\Utility\ArrayUtility
Definition: ArrayUtility.php:24
‪TYPO3\CMS\Form\Domain\Model\FormDefinition\addPage
‪addPage(Page $page)
Definition: FormDefinition.php:446
‪TYPO3\CMS\Form\Domain\Model\FormDefinition\processingRules
‪array< string, function getProcessingRules():array { return $this-> processingRules
Definition: FormDefinition.php:707
‪TYPO3\CMS\Core\Utility\ArrayUtility\assertAllArrayKeysAreValid
‪static assertAllArrayKeysAreValid(array $arrayToTest, array $allowedArrayKeys)
Definition: ArrayUtility.php:33
‪TYPO3\CMS\Form\Domain\Model\Renderable\AbstractRenderable\createVariant
‪RenderableVariantInterface createVariant(array $options)
Definition: AbstractRenderable.php:458
‪TYPO3\CMS\Extbase\Reflection\ObjectAccess\getPropertyPath
‪static mixed getPropertyPath($subject, string $propertyPath)
Definition: ObjectAccess.php:140
‪TYPO3\CMS\Form\Domain\Model\Renderable\AbstractRenderable\$identifier
‪string $identifier
Definition: AbstractRenderable.php:54
‪TYPO3\CMS\Form\Domain\Model\FormDefinition\initializeFromFormDefaults
‪array< int, $renderables=array();protected list< FinisherInterface > $finishers=array();protected array< string, $processingRules=array();protected array< string, $elementsByIdentifier=array();protected array< string, $elementDefaultValues=array();protected string $rendererClassName='';protected array< string, $typeDefinitions;protected array< string, $validatorsDefinition;protected array< string, $finishersDefinition;protected string $persistenceIdentifier;public __construct(string $identifier, array $prototypeConfiguration=[], string $type='Form', string $persistenceIdentifier=null) { $this->typeDefinitions=$prototypeConfiguration[ 'formElementsDefinition'] ??[];$this->validatorsDefinition=$prototypeConfiguration[ 'validatorsDefinition'] ??[];$this->finishersDefinition=$prototypeConfiguration[ 'finishersDefinition'] ??[];if(!is_string( $identifier)||strlen( $identifier)===0) { throw new IdentifierNotValidException( 'The given identifier was not a string or the string was empty.', 1477082503);} $this->identifier=$identifier;$this->type=$type;$this->persistenceIdentifier=(string) $persistenceIdentifier;if( $prototypeConfiguration !==[]) { $this-> initializeFromFormDefaults()
‪TYPO3\CMS\Form\Domain\Model\Renderable\AbstractCompositeRenderable\addRenderable
‪addRenderable(RenderableInterface $renderable)
Definition: AbstractCompositeRenderable.php:51
‪TYPO3\CMS\Form\Domain\Model\Exception\DuplicateFormElementException
Definition: DuplicateFormElementException.php:26
‪TYPO3\CMS\Core\Utility\GeneralUtility
Definition: GeneralUtility.php:50
‪TYPO3\CMS\Form\Domain\Model\FormDefinition\elementsByIdentifier
‪array< string, function getElements():array { return $this-> elementsByIdentifier
Definition: FormDefinition.php:593
‪TYPO3\CMS\Extbase\Object\ObjectManager
Definition: ObjectManager.php:31
‪TYPO3\CMS\Form\Domain\Model\FormDefinition\movePageBefore
‪movePageBefore(Page $pageToMove, Page $referencePage)
Definition: FormDefinition.php:645
‪TYPO3\CMS\Form\Domain\Model\FormDefinition\getPageByIndex
‪Page getPageByIndex(int $index)
Definition: FormDefinition.php:481
‪TYPO3\CMS\Extbase\Mvc\Request
Definition: Request.php:39
‪TYPO3\CMS\Form\Domain\Model\FormDefinition\registerRenderable
‪registerRenderable(RenderableInterface $renderable)
Definition: FormDefinition.php:563
‪TYPO3\CMS\Form\Mvc\ProcessingRule
Definition: ProcessingRule.php:35
‪TYPO3\CMS\Form\Domain\Model\FormDefinition\initializeFromFormDefaults
‪initializeFromFormDefaults()
Definition: FormDefinition.php:320