mirror of
https://github.com/Combodo/iTop.git
synced 2026-04-20 09:08:42 +02:00
N°8772 - Form dependencies manager implementation - WIP
This commit is contained in:
@@ -480,13 +480,14 @@ return array(
|
||||
'Combodo\\iTop\\Forms\\Block\\DataModel\\AttributeValueChoiceFormBlock' => $baseDir . '/sources/Forms/Block/DataModel/AttributeValueChoiceFormBlock.php',
|
||||
'Combodo\\iTop\\Forms\\Block\\DataModel\\OqlFormBlock' => $baseDir . '/sources/Forms/Block/DataModel/OqlFormBlock.php',
|
||||
'Combodo\\iTop\\Forms\\Block\\FormBlock' => $baseDir . '/sources/Forms/Block/FormBlock.php',
|
||||
'Combodo\\iTop\\Forms\\Block\\FormBlockException' => $baseDir . '/sources/Forms/Block/FormBlockException.php',
|
||||
'Combodo\\iTop\\Forms\\Block\\FormInput' => $baseDir . '/sources/Forms/Block/FormInput.php',
|
||||
'Combodo\\iTop\\Forms\\Block\\FormOutput' => $baseDir . '/sources/Forms/Block/FormOutput.php',
|
||||
'Combodo\\iTop\\Forms\\Converter\\AbstractConverter' => $baseDir . '/sources/Forms/Converter/AbstractConverter.php',
|
||||
'Combodo\\iTop\\Forms\\Converter\\OqlToClassName' => $baseDir . '/sources/Forms/Converter/OqlToClassName.php',
|
||||
'Combodo\\iTop\\Forms\\Dependency\\DependencyDescription' => $baseDir . '/sources/Forms/Dependency/DependencyDescription.php',
|
||||
'Combodo\\iTop\\Forms\\Dependency\\DependencyHandler' => $baseDir . '/sources/Forms/Dependency/DependencyHandler.php',
|
||||
'Combodo\\iTop\\Forms\\FormBuilder\\DependencyHandler' => $baseDir . '/sources/Forms/FormBuilder/DependencyHandler.php',
|
||||
'Combodo\\iTop\\Forms\\FormBuilder\\FormBuilder' => $baseDir . '/sources/Forms/FormBuilder/FormBuilder.php',
|
||||
'Combodo\\iTop\\Forms\\FormBuilder\\FormBuilderException' => $baseDir . '/sources/Forms/FormBuilder/FormBuilderException.php',
|
||||
'Combodo\\iTop\\Forms\\FormBuilder\\FormTypeExtension' => $baseDir . '/sources/Forms/FormBuilder/FormTypeExtension.php',
|
||||
'Combodo\\iTop\\Forms\\FormBuilder\\ResolvedFormType' => $baseDir . '/sources/Forms/FormBuilder/ResolvedFormType.php',
|
||||
'Combodo\\iTop\\Forms\\FormBuilder\\ResolvedFormTypeFactory' => $baseDir . '/sources/Forms/FormBuilder/ResolvedFormTypeFactory.php',
|
||||
@@ -494,6 +495,7 @@ return array(
|
||||
'Combodo\\iTop\\Forms\\FormType\\AttributeValueChoiceType' => $baseDir . '/sources/Forms/FormType/AttributeValueChoiceType.php',
|
||||
'Combodo\\iTop\\Forms\\FormType\\OqlType' => $baseDir . '/sources/Forms/FormType/OqlType.php',
|
||||
'Combodo\\iTop\\Forms\\Forms' => $baseDir . '/sources/Forms/Forms.php',
|
||||
'Combodo\\iTop\\Forms\\FormsException' => $baseDir . '/sources/Forms/FormsException.php',
|
||||
'Combodo\\iTop\\Forms\\Twig\\Extension\\FormCompatibilityExtension' => $baseDir . '/sources/Forms/Twig/Extension/FormCompatibilityExtension.php',
|
||||
'Combodo\\iTop\\PhpParser\\Evaluation\\PhpExpressionEvaluator' => $baseDir . '/sources/PhpParser/Evaluation/PhpExpressionEvaluator.php',
|
||||
'Combodo\\iTop\\Renderer\\BlockRenderer' => $baseDir . '/sources/Renderer/BlockRenderer.php',
|
||||
|
||||
@@ -861,13 +861,14 @@ class ComposerStaticInit7f81b4a2a468a061c306af5e447a9a9f
|
||||
'Combodo\\iTop\\Forms\\Block\\DataModel\\AttributeValueChoiceFormBlock' => __DIR__ . '/../..' . '/sources/Forms/Block/DataModel/AttributeValueChoiceFormBlock.php',
|
||||
'Combodo\\iTop\\Forms\\Block\\DataModel\\OqlFormBlock' => __DIR__ . '/../..' . '/sources/Forms/Block/DataModel/OqlFormBlock.php',
|
||||
'Combodo\\iTop\\Forms\\Block\\FormBlock' => __DIR__ . '/../..' . '/sources/Forms/Block/FormBlock.php',
|
||||
'Combodo\\iTop\\Forms\\Block\\FormBlockException' => __DIR__ . '/../..' . '/sources/Forms/Block/FormBlockException.php',
|
||||
'Combodo\\iTop\\Forms\\Block\\FormInput' => __DIR__ . '/../..' . '/sources/Forms/Block/FormInput.php',
|
||||
'Combodo\\iTop\\Forms\\Block\\FormOutput' => __DIR__ . '/../..' . '/sources/Forms/Block/FormOutput.php',
|
||||
'Combodo\\iTop\\Forms\\Converter\\AbstractConverter' => __DIR__ . '/../..' . '/sources/Forms/Converter/AbstractConverter.php',
|
||||
'Combodo\\iTop\\Forms\\Converter\\OqlToClassName' => __DIR__ . '/../..' . '/sources/Forms/Converter/OqlToClassName.php',
|
||||
'Combodo\\iTop\\Forms\\Dependency\\DependencyDescription' => __DIR__ . '/../..' . '/sources/Forms/Dependency/DependencyDescription.php',
|
||||
'Combodo\\iTop\\Forms\\Dependency\\DependencyHandler' => __DIR__ . '/../..' . '/sources/Forms/Dependency/DependencyHandler.php',
|
||||
'Combodo\\iTop\\Forms\\FormBuilder\\DependencyHandler' => __DIR__ . '/../..' . '/sources/Forms/FormBuilder/DependencyHandler.php',
|
||||
'Combodo\\iTop\\Forms\\FormBuilder\\FormBuilder' => __DIR__ . '/../..' . '/sources/Forms/FormBuilder/FormBuilder.php',
|
||||
'Combodo\\iTop\\Forms\\FormBuilder\\FormBuilderException' => __DIR__ . '/../..' . '/sources/Forms/FormBuilder/FormBuilderException.php',
|
||||
'Combodo\\iTop\\Forms\\FormBuilder\\FormTypeExtension' => __DIR__ . '/../..' . '/sources/Forms/FormBuilder/FormTypeExtension.php',
|
||||
'Combodo\\iTop\\Forms\\FormBuilder\\ResolvedFormType' => __DIR__ . '/../..' . '/sources/Forms/FormBuilder/ResolvedFormType.php',
|
||||
'Combodo\\iTop\\Forms\\FormBuilder\\ResolvedFormTypeFactory' => __DIR__ . '/../..' . '/sources/Forms/FormBuilder/ResolvedFormTypeFactory.php',
|
||||
@@ -875,6 +876,7 @@ class ComposerStaticInit7f81b4a2a468a061c306af5e447a9a9f
|
||||
'Combodo\\iTop\\Forms\\FormType\\AttributeValueChoiceType' => __DIR__ . '/../..' . '/sources/Forms/FormType/AttributeValueChoiceType.php',
|
||||
'Combodo\\iTop\\Forms\\FormType\\OqlType' => __DIR__ . '/../..' . '/sources/Forms/FormType/OqlType.php',
|
||||
'Combodo\\iTop\\Forms\\Forms' => __DIR__ . '/../..' . '/sources/Forms/Forms.php',
|
||||
'Combodo\\iTop\\Forms\\FormsException' => __DIR__ . '/../..' . '/sources/Forms/FormsException.php',
|
||||
'Combodo\\iTop\\Forms\\Twig\\Extension\\FormCompatibilityExtension' => __DIR__ . '/../..' . '/sources/Forms/Twig/Extension/FormCompatibilityExtension.php',
|
||||
'Combodo\\iTop\\PhpParser\\Evaluation\\PhpExpressionEvaluator' => __DIR__ . '/../..' . '/sources/PhpParser/Evaluation/PhpExpressionEvaluator.php',
|
||||
'Combodo\\iTop\\Renderer\\BlockRenderer' => __DIR__ . '/../..' . '/sources/Renderer/BlockRenderer.php',
|
||||
|
||||
@@ -69,13 +69,13 @@ abstract class AbstractFormBlock
|
||||
* @param string $sOutputName
|
||||
*
|
||||
* @return $this
|
||||
* @throws \Combodo\iTop\Forms\Block\FormsBlockException
|
||||
* @throws \Combodo\iTop\Forms\Block\FormBlockException
|
||||
*/
|
||||
public function DependsOn(string $sInputName, string $sOutputBlockName, string $sOutputName): AbstractFormBlock
|
||||
{
|
||||
$oFormInput = $this->GetInput($sInputName);
|
||||
if (is_null($oFormInput)) {
|
||||
throw new FormsBlockException('Missing input ' . $sInputName . ' for ' . $this->sName);
|
||||
throw new FormBlockException('Missing input ' . $sInputName . ' for ' . $this->sName);
|
||||
}
|
||||
$oFormInput->Connect($sOutputBlockName, $sOutputName);
|
||||
|
||||
@@ -92,6 +92,18 @@ abstract class AbstractFormBlock
|
||||
return false;
|
||||
}
|
||||
|
||||
public function GetConnections(): array
|
||||
{
|
||||
$aConnections = [];
|
||||
/** @var \Combodo\iTop\Forms\Block\FormInput $oFormInput */
|
||||
foreach ($this->aFormInputs as $oFormInput) {
|
||||
if ($oFormInput->HasConnections()) {
|
||||
$aConnections[$oFormInput->GetName()] = $oFormInput->GetConnections();
|
||||
}
|
||||
}
|
||||
return $aConnections;
|
||||
}
|
||||
|
||||
abstract public function GetFormType(): string;
|
||||
|
||||
abstract public function InitInputs(): void;
|
||||
|
||||
@@ -4,6 +4,7 @@ namespace Combodo\iTop\Forms\Block\DataModel;
|
||||
|
||||
use Combodo\iTop\Forms\Block\Base\StringFormBlock;
|
||||
use Combodo\iTop\Forms\Block\FormOutput;
|
||||
use Combodo\iTop\Forms\Converter\OqlToClassName;
|
||||
|
||||
class OqlFormBlock extends StringFormBlock
|
||||
{
|
||||
@@ -13,7 +14,7 @@ class OqlFormBlock extends StringFormBlock
|
||||
public function InitOutputs(): void
|
||||
{
|
||||
parent::InitOutputs();
|
||||
$this->AddOutput(new FormOutput(self::OUTPUT_SELECTED_CLASS, 'string'));
|
||||
$this->AddOutput(new FormOutput(self::OUTPUT_SELECTED_CLASS, 'string', new OqlToClassName()));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -8,7 +8,7 @@ namespace Combodo\iTop\Forms\Block;
|
||||
|
||||
use Combodo\iTop\Forms\FormsException;
|
||||
|
||||
class FormsBlockException extends FormsException
|
||||
class FormBlockException extends FormsException
|
||||
{
|
||||
|
||||
}
|
||||
@@ -38,7 +38,7 @@ class FormInput
|
||||
|
||||
public function Connect(string $sOutputBlockName, string $sOutputName)
|
||||
{
|
||||
$this->aConnections[] = ['block' => $sOutputBlockName, 'output' => $sOutputName];
|
||||
$this->aConnections[] = ['output_block' => $sOutputBlockName, 'output' => $sOutputName];
|
||||
}
|
||||
|
||||
public function GetConnections(): array
|
||||
|
||||
@@ -11,6 +11,7 @@ class FormOutput
|
||||
private string $sType;
|
||||
|
||||
private null|AbstractConverter $oConverter;
|
||||
private array $aValues;
|
||||
|
||||
public function __construct(string $sName, string $sType, AbstractConverter $oConverter = null)
|
||||
{
|
||||
@@ -39,10 +40,23 @@ class FormOutput
|
||||
$this->sType = $sType;
|
||||
}
|
||||
|
||||
public function GetOutputValue(mixed $oData): mixed
|
||||
public function ConvertValue(mixed $oData): mixed
|
||||
{
|
||||
if (is_null($this->oConverter)) {
|
||||
return $oData;
|
||||
}
|
||||
return $this->oConverter->Convert($oData);
|
||||
}
|
||||
|
||||
public function UpdateOutputValue(mixed $oData, string $sEventType): void
|
||||
{
|
||||
$this->aValues[$sEventType] = $this->ConvertValue($oData);
|
||||
}
|
||||
|
||||
public function GetValue(string $sEventType): mixed
|
||||
{
|
||||
return $this->aValues[$sEventType] ?? null;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -1,107 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Combodo\iTop\Forms\Dependency;
|
||||
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Symfony\Component\Form\FormEvents;
|
||||
|
||||
class DependencyDescription
|
||||
{
|
||||
private array $aPostSetData = [];
|
||||
|
||||
private array $aPostSubmitData = [];
|
||||
|
||||
private bool $isAdded = false;
|
||||
|
||||
public function __construct(public readonly array $aDependencies, public readonly string|FormBuilderInterface $child, public readonly ?string $type = null, public readonly array $options = [])
|
||||
{
|
||||
}
|
||||
|
||||
public function IsAdded(): bool
|
||||
{
|
||||
return $this->isAdded;
|
||||
}
|
||||
|
||||
public function SetAdded(bool $bAdded): void
|
||||
{
|
||||
$this->isAdded = $bAdded;
|
||||
}
|
||||
|
||||
public function IsDataReady(string $sEventType): bool
|
||||
{
|
||||
$aData = ($sEventType === FormEvents::POST_SET_DATA) ? $this->aPostSetData : $this->aPostSubmitData;
|
||||
|
||||
foreach (array_keys($this->aDependencies) as $sInput){
|
||||
if(!array_key_exists($sInput, $aData) || $aData[$sInput] == null){
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public function IsPostSetDataReady(): bool
|
||||
{
|
||||
foreach ($this->aDependencies as $sData => $sValue) {
|
||||
if (!array_key_exists($sData, $this->aPostSetData)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public function IsPostSubmitDataReady(): bool
|
||||
{
|
||||
foreach ($this->aDependencies as $sData => $sValue) {
|
||||
if (!array_key_exists($sData, $this->aPostSubmitData)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public function SetData(string $sEventType, string $sData, mixed $oValue): void
|
||||
{
|
||||
if($oValue === null) return;
|
||||
|
||||
if($sEventType === FormEvents::POST_SET_DATA){
|
||||
$this->aPostSetData[$sData] = $oValue;
|
||||
}
|
||||
else{
|
||||
$this->aPostSubmitData[$sData] = $oValue;
|
||||
}
|
||||
}
|
||||
|
||||
public function GetData(string $sEventType): array
|
||||
{
|
||||
if($sEventType === FormEvents::POST_SET_DATA){
|
||||
return $this->aPostSetData;
|
||||
}
|
||||
else{
|
||||
return $this->aPostSubmitData;
|
||||
}
|
||||
}
|
||||
|
||||
public function SetPostSetData(string $sInput, mixed $oData): void
|
||||
{
|
||||
$this->aPostSetData[$sInput] = $oData;
|
||||
}
|
||||
|
||||
public function SetPostSubmitData(string $sInput, mixed $oData): void
|
||||
{
|
||||
$this->aPostSubmitData[$sInput] = $oData;
|
||||
}
|
||||
|
||||
public function IsReady(string $sEventType): bool
|
||||
{
|
||||
$aData = ($sEventType === FormEvents::POST_SET_DATA) ? $this->aPostSetData : $this->aPostSubmitData;
|
||||
|
||||
foreach (array_keys($this->aDependencies) as $sInput){
|
||||
if(!array_key_exists($sInput, $aData)){
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -1,172 +1,172 @@
|
||||
<?php
|
||||
|
||||
namespace Combodo\iTop\Forms\Dependency;
|
||||
|
||||
use Exception;
|
||||
use Symfony\Component\Form\Event\PostSetDataEvent;
|
||||
use Symfony\Component\Form\Event\PostSubmitEvent;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Symfony\Component\Form\FormEvent;
|
||||
use Symfony\Component\Form\FormEvents;
|
||||
use Symfony\Component\Form\FormInterface;
|
||||
|
||||
class DependencyHandler
|
||||
{
|
||||
/** @var array dépendencies descriptions stored on builder add */
|
||||
private array $aDependenciesDescription = [];
|
||||
|
||||
/** @var array dependencies map computed on form pre set data */
|
||||
private array $aDependenciesMap = [];
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param FormBuilderInterface $builder
|
||||
*/
|
||||
public function __construct(public FormBuilderInterface $builder)
|
||||
{
|
||||
// Initialize the dependencies listeners once the form is built
|
||||
$builder->addEventListener(FormEvents::PRE_SET_DATA, function (FormEvent $event) {
|
||||
$oForm = $event->getForm();
|
||||
$this->InitializeDependenciesMap($oForm);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
*Initialize the dependencies map and register listeners on the dependencies inputs.
|
||||
*
|
||||
* @param FormInterface $oForm
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function InitializeDependenciesMap(FormInterface $oForm): void
|
||||
{
|
||||
|
||||
/** iterate throw dependencies descriptions... @var DependencyDescription $oDependencyDescription */
|
||||
foreach ($this->aDependenciesDescription as $oDependencyDescription) {
|
||||
|
||||
/** iterate throw dependencies items... */
|
||||
foreach ($oDependencyDescription->aDependencies as $sInput => $aData) {
|
||||
|
||||
// get the dependency field name
|
||||
$sDependency = $aData['source'];
|
||||
|
||||
// get the field input name
|
||||
$sOutput = array_key_exists('output', $aData) ? $aData['output'] : null;
|
||||
|
||||
// add the dependency to the map
|
||||
if(!array_key_exists($sDependency, $this->aDependenciesMap)){
|
||||
$this->aDependenciesMap[$sDependency] = [];
|
||||
}
|
||||
$this->aDependenciesMap[$sDependency][] = ['description' => $oDependencyDescription, 'input' => $sInput, 'output' => $sOutput];
|
||||
}
|
||||
}
|
||||
|
||||
/** iterate throw dependencies... */
|
||||
foreach (array_keys($this->aDependenciesMap) as $sDependency){
|
||||
|
||||
// Listen the dependency
|
||||
$this->builder->get($sDependency)->addEventListener(FormEvents::POST_SET_DATA, $this->GetEventListeningCallback());
|
||||
$this->builder->get($sDependency)->addEventListener(FormEvents::POST_SUBMIT, $this->GetEventListeningCallback());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a dependency description.
|
||||
*
|
||||
* @param DependencyDescription $oDependencyDescription
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function AddDependencyDescription(DependencyDescription $oDependencyDescription): void
|
||||
{
|
||||
$this->aDependenciesDescription[] = $oDependencyDescription;
|
||||
}
|
||||
|
||||
/**
|
||||
* The event handling callback.
|
||||
*
|
||||
* @return callable
|
||||
*/
|
||||
private function GetEventListeningCallback(): callable
|
||||
{
|
||||
return function (FormEvent $event){
|
||||
|
||||
// Get the event type
|
||||
$sEventType = $this->GetEventType($event);
|
||||
|
||||
// Get the form
|
||||
$oForm = $event->getForm();
|
||||
|
||||
/** Iterate throw dependencies map... */
|
||||
foreach ($this->aDependenciesMap[$event->getForm()->getName()] as $aData){
|
||||
|
||||
// Get the map data
|
||||
$oDependencyDescription = $aData['description'];
|
||||
$sInput = $aData['input'];
|
||||
$sOutput = $aData['output'];
|
||||
|
||||
// Compute output value
|
||||
$oValue = $event->getData();
|
||||
if(array_key_exists('outputs', $event->getForm()->getConfig()->getOptions())){
|
||||
$aOutputs = $event->getForm()->getConfig()->getOptions()['outputs'];
|
||||
if(array_key_exists($sOutput, $aOutputs)){
|
||||
$oValue = $aOutputs[$sOutput]($oValue);
|
||||
}
|
||||
}
|
||||
|
||||
// Store the input value
|
||||
$oDependencyDescription->SetData($sEventType, $sInput, $oValue);
|
||||
|
||||
// When dependencies met, add the dependent field if not already done
|
||||
if(!$oDependencyDescription->IsAdded() && $oDependencyDescription->IsDataReady($sEventType)) {
|
||||
|
||||
// Get the dependent field options
|
||||
$aOptions = $oDependencyDescription->options;
|
||||
|
||||
// Add the listener callback to the dependent field if it is also a dependency for another field
|
||||
if(is_string($oDependencyDescription->child) && array_key_exists($oDependencyDescription->child, $this->aDependenciesMap)) {
|
||||
$aOptions = array_merge($aOptions, [
|
||||
'listener_callback' => $this->GetEventListeningCallback(),
|
||||
]);
|
||||
}
|
||||
|
||||
// Add the dependent field to the form
|
||||
$oForm->getParent()->add($oDependencyDescription->child, $oDependencyDescription->type, array_merge($aOptions, $oDependencyDescription->type::GetOptionsFromInputs($oDependencyDescription->GetData($sEventType))));
|
||||
|
||||
// Mark the dependency as added
|
||||
$oDependencyDescription->SetAdded(true);
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the event type.
|
||||
*
|
||||
* @param FormEvent $event
|
||||
*
|
||||
* @return string
|
||||
* @throws Exception
|
||||
*/
|
||||
private function GetEventType(FormEvent $event): string
|
||||
{
|
||||
if($event instanceof PostSetDataEvent) {
|
||||
return FormEvents::POST_SET_DATA;
|
||||
}
|
||||
else if($event instanceof PostSubmitEvent) {
|
||||
return FormEvents::POST_SUBMIT;
|
||||
}
|
||||
|
||||
throw new Exception(sprintf("Unknown event type %s", get_class($event)));
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
//
|
||||
//namespace Combodo\iTop\Forms\Dependency;
|
||||
//
|
||||
//use Exception;
|
||||
//use Symfony\Component\Form\Event\PostSetDataEvent;
|
||||
//use Symfony\Component\Form\Event\PostSubmitEvent;
|
||||
//use Symfony\Component\Form\FormBuilderInterface;
|
||||
//use Symfony\Component\Form\FormEvent;
|
||||
//use Symfony\Component\Form\FormEvents;
|
||||
//use Symfony\Component\Form\FormInterface;
|
||||
//
|
||||
//class DependencyHandler
|
||||
//{
|
||||
// /** @var array dépendencies descriptions stored on builder add */
|
||||
// private array $aDependenciesDescription = [];
|
||||
//
|
||||
// /** @var array dependencies map computed on form pre set data */
|
||||
// private array $aDependenciesMap = [];
|
||||
//
|
||||
// /**
|
||||
// * Constructor.
|
||||
// *
|
||||
// * @param FormBuilderInterface $builder
|
||||
// */
|
||||
// public function __construct(public FormBuilderInterface $builder)
|
||||
// {
|
||||
// // Initialize the dependencies listeners once the form is built
|
||||
// $builder->addEventListener(FormEvents::PRE_SET_DATA, function (FormEvent $event) {
|
||||
// $oForm = $event->getForm();
|
||||
// $this->InitializeDependenciesMap($oForm);
|
||||
// });
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// *Initialize the dependencies map and register listeners on the dependencies inputs.
|
||||
// *
|
||||
// * @param FormInterface $oForm
|
||||
// *
|
||||
// * @return void
|
||||
// */
|
||||
// private function InitializeDependenciesMap(FormInterface $oForm): void
|
||||
// {
|
||||
//
|
||||
// /** iterate throw dependencies descriptions... @var DependencyDescription $oDependencyDescription */
|
||||
// foreach ($this->aDependenciesDescription as $oDependencyDescription) {
|
||||
//
|
||||
// /** iterate throw dependencies items... */
|
||||
// foreach ($oDependencyDescription->aDependencies as $sInput => $aData) {
|
||||
//
|
||||
// // get the dependency field name
|
||||
// $sDependency = $aData['source'];
|
||||
//
|
||||
// // get the field input name
|
||||
// $sOutput = array_key_exists('output', $aData) ? $aData['output'] : null;
|
||||
//
|
||||
// // add the dependency to the map
|
||||
// if(!array_key_exists($sDependency, $this->aDependenciesMap)){
|
||||
// $this->aDependenciesMap[$sDependency] = [];
|
||||
// }
|
||||
// $this->aDependenciesMap[$sDependency][] = ['description' => $oDependencyDescription, 'input' => $sInput, 'output' => $sOutput];
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// /** iterate throw dependencies... */
|
||||
// foreach (array_keys($this->aDependenciesMap) as $sDependency){
|
||||
//
|
||||
// // Listen the dependency
|
||||
// $this->builder->get($sDependency)->addEventListener(FormEvents::POST_SET_DATA, $this->GetEventListeningCallback());
|
||||
// $this->builder->get($sDependency)->addEventListener(FormEvents::POST_SUBMIT, $this->GetEventListeningCallback());
|
||||
// }
|
||||
//
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * Add a dependency description.
|
||||
// *
|
||||
// * @param DependencyDescription $oDependencyDescription
|
||||
// *
|
||||
// * @return void
|
||||
// */
|
||||
// public function AddDependencyDescription(DependencyDescription $oDependencyDescription): void
|
||||
// {
|
||||
// $this->aDependenciesDescription[] = $oDependencyDescription;
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * The event handling callback.
|
||||
// *
|
||||
// * @return callable
|
||||
// */
|
||||
// private function GetEventListeningCallback(): callable
|
||||
// {
|
||||
// return function (FormEvent $event){
|
||||
//
|
||||
// // Get the event type
|
||||
// $sEventType = $this->GetEventType($event);
|
||||
//
|
||||
// // Get the form
|
||||
// $oForm = $event->getForm();
|
||||
//
|
||||
// /** Iterate throw dependencies map... */
|
||||
// foreach ($this->aDependenciesMap[$event->getForm()->getName()] as $aData){
|
||||
//
|
||||
// // Get the map data
|
||||
// $oDependencyDescription = $aData['description'];
|
||||
// $sInput = $aData['input'];
|
||||
// $sOutput = $aData['output'];
|
||||
//
|
||||
// // Compute output value
|
||||
// $oValue = $event->getData();
|
||||
// if(array_key_exists('outputs', $event->getForm()->getConfig()->getOptions())){
|
||||
// $aOutputs = $event->getForm()->getConfig()->getOptions()['outputs'];
|
||||
// if(array_key_exists($sOutput, $aOutputs)){
|
||||
// $oValue = $aOutputs[$sOutput]($oValue);
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// // Store the input value
|
||||
// $oDependencyDescription->SetData($sEventType, $sInput, $oValue);
|
||||
//
|
||||
// // When dependencies met, add the dependent field if not already done
|
||||
// if(!$oDependencyDescription->IsAdded() && $oDependencyDescription->IsDataReady($sEventType)) {
|
||||
//
|
||||
// // Get the dependent field options
|
||||
// $aOptions = $oDependencyDescription->options;
|
||||
//
|
||||
// // Add the listener callback to the dependent field if it is also a dependency for another field
|
||||
// if(is_string($oDependencyDescription->child) && array_key_exists($oDependencyDescription->child, $this->aDependenciesMap)) {
|
||||
// $aOptions = array_merge($aOptions, [
|
||||
// 'listener_callback' => $this->GetEventListeningCallback(),
|
||||
// ]);
|
||||
// }
|
||||
//
|
||||
// // Add the dependent field to the form
|
||||
// $oForm->getParent()->add($oDependencyDescription->child, $oDependencyDescription->type, array_merge($aOptions, $oDependencyDescription->type::GetOptionsFromInputs($oDependencyDescription->GetData($sEventType))));
|
||||
//
|
||||
// // Mark the dependency as added
|
||||
// $oDependencyDescription->SetAdded(true);
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// };
|
||||
//
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * Get the event type.
|
||||
// *
|
||||
// * @param FormEvent $event
|
||||
// *
|
||||
// * @return string
|
||||
// * @throws Exception
|
||||
// */
|
||||
// private function GetEventType(FormEvent $event): string
|
||||
// {
|
||||
// if($event instanceof PostSetDataEvent) {
|
||||
// return FormEvents::POST_SET_DATA;
|
||||
// }
|
||||
// else if($event instanceof PostSubmitEvent) {
|
||||
// return FormEvents::POST_SUBMIT;
|
||||
// }
|
||||
//
|
||||
// throw new Exception(sprintf("Unknown event type %s", get_class($event)));
|
||||
// }
|
||||
//
|
||||
//
|
||||
//
|
||||
//
|
||||
//
|
||||
//
|
||||
//}
|
||||
112
sources/Forms/FormBuilder/DependencyHandler.php
Normal file
112
sources/Forms/FormBuilder/DependencyHandler.php
Normal file
@@ -0,0 +1,112 @@
|
||||
<?php
|
||||
/*
|
||||
* @copyright Copyright (C) 2010-2025 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
namespace Combodo\iTop\Forms\FormBuilder;
|
||||
|
||||
use Symfony\Component\Form\Event\PostSetDataEvent;
|
||||
use Symfony\Component\Form\Event\PostSubmitEvent;
|
||||
use Symfony\Component\Form\FormEvent;
|
||||
use Symfony\Component\Form\FormEvents;
|
||||
use Symfony\Component\Form\FormInterface;
|
||||
|
||||
class DependencyHandler
|
||||
{
|
||||
private array $aDependentBlocks = [];
|
||||
private FormBuilder $oFormBuilder;
|
||||
private array $aDependenciesMap = [];
|
||||
|
||||
/**
|
||||
* @param \Combodo\iTop\Forms\FormBuilder\FormBuilder $oFormBuilder
|
||||
* @param array $aDependentBlocks
|
||||
*/
|
||||
public function __construct(FormBuilder $oFormBuilder, array $aDependentBlocks)
|
||||
{
|
||||
$this->aDependentBlocks = $aDependentBlocks;
|
||||
$this->oFormBuilder = $oFormBuilder;
|
||||
|
||||
// Initialize the dependencies listeners once the form is built
|
||||
$oFormBuilder->addEventListener(FormEvents::PRE_SET_DATA, function (FormEvent $event) {
|
||||
$oForm = $event->getForm();
|
||||
$this->InitializeDependenciesMap($oForm);
|
||||
});
|
||||
}
|
||||
|
||||
private function InitializeDependenciesMap(FormInterface $oForm)
|
||||
{
|
||||
// Connections from output pov
|
||||
$aDependenciesMap = [];
|
||||
|
||||
/** @var \Combodo\iTop\Forms\Block\FormBlock $oDependentBlock */
|
||||
foreach ($this->aDependentBlocks as $oDependentBlock) {
|
||||
foreach ($oDependentBlock->GetConnections() as $sInputName => $aConnections) {
|
||||
foreach ($aConnections as $aConnection) {
|
||||
$sOutputBlockName = $aConnection['output_block'];
|
||||
$sOutputName = $aConnection['output'];
|
||||
|
||||
if (!isset($aDependenciesMap[$sOutputBlockName])) {
|
||||
$aDependenciesMap[$sOutputBlockName] = [];
|
||||
}
|
||||
if (!isset($aDependenciesMap[$sOutputBlockName][$sOutputName])) {
|
||||
$aDependenciesMap[$sOutputBlockName][$sOutputName] = [];
|
||||
}
|
||||
|
||||
$aDependenciesMap[$sOutputBlockName][$sOutputName][] = ['input_block' => $oDependentBlock, 'input_name' => $sInputName];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$this->aDependenciesMap = $aDependenciesMap;
|
||||
|
||||
foreach (array_keys($aDependenciesMap) as $sOutputBlockName) {
|
||||
|
||||
// Listen the dependency
|
||||
$this->oFormBuilder->get($sOutputBlockName)->addEventListener(FormEvents::POST_SET_DATA, $this->GetEventListeningCallback());
|
||||
$this->oFormBuilder->get($sOutputBlockName)->addEventListener(FormEvents::POST_SUBMIT, $this->GetEventListeningCallback());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private function GetEventListeningCallback(): callable
|
||||
{
|
||||
return function (FormEvent $oEvent) {
|
||||
// Get the event type
|
||||
$sEventType = $this->GetEventType($oEvent);
|
||||
|
||||
// Get the form
|
||||
$oForm = $oEvent->getForm();
|
||||
|
||||
/** Iterate throw dependencies map... */
|
||||
$sOutputBlockName = $oForm->getName();
|
||||
$oOutputBlock = $this->oFormBuilder->GetFormBlock($sOutputBlockName);
|
||||
foreach (array_keys($this->aDependenciesMap[$sOutputBlockName]) as $sOutputName) {
|
||||
$oOutput = $oOutputBlock->GetOutput($sOutputName);
|
||||
$oOutput->UpdateOutputValue($oEvent->getData(), $sEventType);
|
||||
}
|
||||
|
||||
return;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the event type.
|
||||
*
|
||||
* @param FormEvent $event
|
||||
*
|
||||
* @return string
|
||||
* @throws \Combodo\iTop\Forms\FormBuilder\FormBuilderException
|
||||
*/
|
||||
private function GetEventType(FormEvent $event): string
|
||||
{
|
||||
if ($event instanceof PostSetDataEvent) {
|
||||
return FormEvents::POST_SET_DATA;
|
||||
} else if ($event instanceof PostSubmitEvent) {
|
||||
return FormEvents::POST_SUBMIT;
|
||||
}
|
||||
|
||||
throw new FormBuilderException(sprintf('Unknown event type %s', get_class($event)));
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
namespace Combodo\iTop\Forms\FormBuilder;
|
||||
|
||||
use Combodo\iTop\Forms\Dependency\DependencyHandler;
|
||||
use Combodo\iTop\Forms\Block\AbstractFormBlock;
|
||||
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
|
||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||
use Symfony\Component\Form\DataMapperInterface;
|
||||
@@ -19,8 +19,8 @@ use Traversable;
|
||||
|
||||
class FormBuilder implements FormBuilderInterface, \IteratorAggregate
|
||||
{
|
||||
/** @var DependencyHandler|null dependencies handler */
|
||||
private DependencyHandler|null $dependencyHandler = null;
|
||||
private ?DependencyHandler $oDependencyHandler = null;
|
||||
private array $aFormBlocks = [];
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
@@ -45,14 +45,26 @@ class FormBuilder implements FormBuilderInterface, \IteratorAggregate
|
||||
return;
|
||||
}
|
||||
|
||||
$aDependentBlocks = [];
|
||||
/** @var \Combodo\iTop\Forms\Block\FormBlock $oSubFormBlock */
|
||||
foreach ($oFormBlock->GetSubFormBlocks() as $oSubFormBlock) {
|
||||
$this->aFormBlocks[$oSubFormBlock->getName()] = $oSubFormBlock;
|
||||
if ($oSubFormBlock->HasConnections()) {
|
||||
$this->builder->add($oSubFormBlock->GetName(), HiddenType::class);
|
||||
$aDependentBlocks[] = $oSubFormBlock;
|
||||
} else {
|
||||
$this->add($oSubFormBlock->GetName(), $oSubFormBlock->GetFormType(), $oSubFormBlock->getOptions());
|
||||
}
|
||||
}
|
||||
|
||||
if (count($aDependentBlocks) > 0) {
|
||||
$this->oDependencyHandler = new DependencyHandler($this, $aDependentBlocks);
|
||||
}
|
||||
}
|
||||
|
||||
public function GetFormBlock(string $sName): ?AbstractFormBlock
|
||||
{
|
||||
return $this->aFormBlocks[$sName] ?? null;
|
||||
}
|
||||
|
||||
// pure decoration of FormBuilderInterface
|
||||
|
||||
14
sources/Forms/FormBuilder/FormBuilderException.php
Normal file
14
sources/Forms/FormBuilder/FormBuilderException.php
Normal file
@@ -0,0 +1,14 @@
|
||||
<?php
|
||||
/*
|
||||
* @copyright Copyright (C) 2010-2025 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
namespace Combodo\iTop\Forms\FormBuilder;
|
||||
|
||||
use Combodo\iTop\Forms\FormsException;
|
||||
|
||||
class FormBuilderException extends FormsException
|
||||
{
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user