diff --git a/sources/Forms/Dependency/DependencyException.php b/sources/Forms/Dependency/DependencyException.php new file mode 100644 index 000000000..60246f3f0 --- /dev/null +++ b/sources/Forms/Dependency/DependencyException.php @@ -0,0 +1,12 @@ +aFieldNameToPosition[$sName] = count($this->aFieldNameToPosition); + + if (empty($aDependencies)) { + $this->AddChild($oNode); + return; + } + + // Search the last added dependency + $oParentNode = null; + $iParentPosition = -1; + foreach ($aDependencies as $sNodeName) { + if ($sNodeName === $sName) { + throw new DependencyException("Form field '$sName' cannot reference itself"); + } + if (!isset($this->aFieldNameToPosition[$sNodeName])) { + throw new DependencyException("Form field '$sNodeName' is not existing"); + } + if ($this->aFieldNameToPosition[$sNodeName] > $iParentPosition) { + $oParentNode = $this->SearchNode($sNodeName); + $iParentPosition = $this->aFieldNameToPosition[$sNodeName]; + } + } + + if (is_null($oParentNode)) { + throw new DependencyException("Could not find dependency for field '$sName'"); + } + + // Add to the latest dependency + $oParentNode->AddChild($oNode); + } + + private function SearchNode(string $sName) : ?DependencyNode + { + foreach($this as $oChildNode) { + $oNode = $oChildNode->SearchNode($sName); + if ($oNode instanceof DependencyNode) { + return $oNode; + } + } + + return null; + } + + public function __toString(): string + { + $sResult = "\n"; + foreach ($this as $oNode) { + $sResult .= $oNode->Display(); + } + + return $sResult; + } +} \ No newline at end of file diff --git a/sources/Forms/Dependency/DependencyNode.php b/sources/Forms/Dependency/DependencyNode.php new file mode 100644 index 000000000..4c7fb0cee --- /dev/null +++ b/sources/Forms/Dependency/DependencyNode.php @@ -0,0 +1,56 @@ +sType; + } + + public function GetName() : string + { + return $this->sName; + } + + public function GetUserOptions(): array + { + return $this->aUserOptions; + } + + public function SearchNode(string $sName) : ?DependencyNode + { + if ($sName === $this->GetName()) { + return $this; + } + + foreach ($this as $oChildNode) { + $oNode = $oChildNode->SearchNode($sName); + if ($oNode instanceof DependencyNode) { + return $oNode; + } + } + + return null; + } + + public function Display(int $iDepth = 1) + { + $sResult = str_repeat(' ', $iDepth).$this->GetName()."\n"; + foreach ($this as $oNode) { + $sResult .= $oNode->Display($iDepth + 1); + } + + return $sResult; + } +} \ No newline at end of file diff --git a/sources/Forms/Dependency/GraphTrait.php b/sources/Forms/Dependency/GraphTrait.php new file mode 100644 index 000000000..fa1aac5e9 --- /dev/null +++ b/sources/Forms/Dependency/GraphTrait.php @@ -0,0 +1,50 @@ +aChildren[] = $node; + } + + public function IsLast(): bool + { + return $this->iPosition === count($this->aChildren) - 1; + } + + /* + * Iterator interface + */ + public function current(): mixed + { + return $this->aChildren[$this->iPosition]; + } + + public function next(): void + { + $this->iPosition++; + } + + public function key(): mixed + { + return $this->iPosition; + } + + public function valid(): bool + { + return isset($this->aChildren[$this->iPosition]); + } + + public function rewind(): void + { + $this->iPosition = 0; + } +} \ No newline at end of file diff --git a/sources/Forms/FormType/Base/FormBuilder.php b/sources/Forms/FormType/Base/FormBuilder.php index 1d212c12b..07dad6ea3 100644 --- a/sources/Forms/FormType/Base/FormBuilder.php +++ b/sources/Forms/FormType/Base/FormBuilder.php @@ -6,6 +6,7 @@ namespace Combodo\iTop\Forms\FormType\Base; +use Combodo\iTop\Forms\Dependency\DependencyGraph; use Combodo\iTop\Forms\FormType\Orm\AttCodeGroupByType; use Combodo\iTop\Forms\FormType\Orm\ValuesFromAttcodeType; use Symfony\Component\EventDispatcher\EventDispatcherInterface; @@ -26,9 +27,12 @@ use Symfony\Component\PropertyAccess\PropertyPathInterface; class FormBuilder implements FormBuilderInterface, \IteratorAggregate { public array $aModelData = []; + private DependencyGraph $oDependencies; public function __construct(private FormBuilderInterface $builder) { + $this->oDependencies = new DependencyGraph(); + $this->addEventListener(FormEvents::PRE_SUBMIT, function (FormEvent $event) { $this->aModelData = []; }); @@ -36,6 +40,8 @@ class FormBuilder implements FormBuilderInterface, \IteratorAggregate public function Finalize(): void { + \IssueLog::Info($this->oDependencies); + $aCallbacks['query'] = function (FormEvent $event) { if ($event instanceof PostSubmitEvent) { $this->aModelData['query'] = $event->getForm()->getData(); @@ -102,13 +108,10 @@ class FormBuilder implements FormBuilderInterface, \IteratorAggregate $oType = new $type(); $aPrerequisites = $oType->GetPrerequisites($options); if (is_null($aPrerequisites)) { + $this->oDependencies->Add($child, $type); $this->builder->add($child, $type, $options); } else { - $this->aDynamicFields[$child] = [ - 'type' => $type, - 'prerequisites' => $aPrerequisites, - 'user_options' => $options, - ]; + $this->oDependencies->Add($child, $type, $aPrerequisites, $options); $this->builder->add($child, HiddenType::class, ['mapped' => false]); } return $this; diff --git a/sources/Forms/FormType/Orm/ValuesFromAttcodeType.php b/sources/Forms/FormType/Orm/ValuesFromAttcodeType.php index dd59c17f7..f38b70dd8 100644 --- a/sources/Forms/FormType/Orm/ValuesFromAttcodeType.php +++ b/sources/Forms/FormType/Orm/ValuesFromAttcodeType.php @@ -55,8 +55,8 @@ class ValuesFromAttcodeType extends AbstractType public function GetPrerequisites(array $aUserOptions): ?array { return [ - $aUserOptions['source_class'], $aUserOptions['source_attcode'], + $aUserOptions['source_class'], ]; } } \ No newline at end of file