SDK Form demonstrator implement field dependency graph

This commit is contained in:
Eric Espie
2025-04-17 17:26:42 +02:00
parent 81a256aa0e
commit a44b7b13f0
3 changed files with 61 additions and 54 deletions

View File

@@ -44,6 +44,19 @@ class DependencyNode implements \Iterator
return null;
}
/**
* @return array<\Combodo\iTop\Forms\Dependency\DependencyNode>
*/
public function GetSubNodes() : array
{
$aResult = [];
foreach ($this as $oChildNode) {
$aResult = array_merge($aResult, $oChildNode->GetSubNodes());
}
return $aResult;
}
public function Display(int $iDepth = 1)
{
$sResult = str_repeat(' ', $iDepth).$this->GetName()."\n";

View File

@@ -15,15 +15,14 @@ trait GraphTrait
$this->aChildren[] = $node;
}
public function IsLast(): bool
{
return $this->iPosition === count($this->aChildren) - 1;
public function HasChildren() : bool {
return count($this->aChildren) > 0;
}
/*
* Iterator interface
*/
public function current(): mixed
public function current(): DependencyNode
{
return $this->aChildren[$this->iPosition];
}
@@ -33,7 +32,7 @@ trait GraphTrait
$this->iPosition++;
}
public function key(): mixed
public function key(): int
{
return $this->iPosition;
}

View File

@@ -7,13 +7,12 @@
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 Combodo\iTop\Forms\Dependency\DependencyNode;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\Form\DataMapperInterface;
use Symfony\Component\Form\DataTransformerInterface;
use Symfony\Component\Form\Event\PostSubmitEvent;
use Symfony\Component\Form\Event\PostSetDataEvent;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Form\FormConfigInterface;
use Symfony\Component\Form\FormEvent;
@@ -38,57 +37,53 @@ class FormBuilder implements FormBuilderInterface, \IteratorAggregate
});
}
private function GetCookedCallback(DependencyNode $oField, bool $IsHookedOnRootForm): callable
{
return function (FormEvent $event) use ($IsHookedOnRootForm, $oField) {
if ($IsHookedOnRootForm) {
$this->aModelData[$oField->GetName()] = $event->getData()[$oField->GetName()];
$oForm = $event->getForm();
} else {
$this->aModelData[$oField->GetName()] = $event->getForm()->getData();
$oForm = $event->getForm()->getParent();
}
foreach ($oField as $oDependentField) {
$aUserOptions = $oDependentField->GetUserOptions();
$sClass = $oDependentField->GetType();
$oType = new $sClass;
$aFieldOptions = $oType->BuildOptions($aUserOptions, $this->aModelData);
if (!is_null($aFieldOptions)) {
if ($oDependentField->HasChildren()) {
$aFieldOptions['callback'] = $this->GetCookedCallback($oDependentField, false);
$aFieldOptions['hook_type'] = ($event instanceof PostSetDataEvent) ? FormEvents::POST_SET_DATA : FormEvents::POST_SUBMIT;
}
$oForm->add($oDependentField->GetName(), $sClass, $aFieldOptions);
} else {
// Remove field and dependencies
$this->HideField($oForm, $oDependentField->GetName());
foreach ($oDependentField->GetSubNodes() as $oSubNode) {
$this->HideField($oForm, $oSubNode->GetName());
}
}
}
};
}
private function HideField(FormInterface $oForm, string $sName): void
{
$oForm->add($sName, HiddenType::class, ['mapped' => false]);
}
public function Finalize(): void
{
\IssueLog::Info($this->oDependencies);
$aCallbacks['query'] = function (FormEvent $event) {
if ($event instanceof PostSubmitEvent) {
$this->aModelData['query'] = $event->getForm()->getData();
$options['hook_type'] = FormEvents::POST_SUBMIT;
} else {
$this->aModelData['query'] = $event->getData()['query'];
$options['hook_type'] = FormEvents::POST_SET_DATA;
foreach ($this->oDependencies as $oField) {
if ($oField->HasChildren()) {
$this->addEventListener(FormEvents::POST_SET_DATA, $this->GetCookedCallback($oField, true));
$this->get($oField->GetName())->addEventListener(FormEvents::POST_SUBMIT, $this->GetCookedCallback($oField, false));
}
$options['callback'] = function (FormEvent $event) {
$this->aModelData['group_by'] = $event->getForm()->getData();
$oParentForm = $event->getForm()->getParent();
$aUserOptions = [
'source_attcode' => 'group_by',
'source_class' => 'query',
];
// TODO Filtrer seulement les données requises dans aModelData
$aFieldOptions = (new ValuesFromAttcodeType())->BuildOptions($aUserOptions, $this->aModelData);
if (!is_null($aFieldOptions)) {
$oParentForm->add('values', ValuesFromAttcodeType::class, $aFieldOptions);
} else {
// Remove field and dependencies
$oParentForm->add('values', HiddenType::class, ['mapped' => false]);
}
};
if ($event instanceof PostSubmitEvent) {
$oForm = $event->getForm()->getParent();
} else {
$oForm = $event->getForm();
}
$aUserOptions = [
'source_class' => 'query',
];
// TODO Filtrer seulement les données requises dans aModelData
$aFieldOptions = (new AttCodeGroupByType())->BuildOptions($aUserOptions, $this->aModelData);
if (!is_null($aFieldOptions)) {
$oForm->add('group_by', AttCodeGroupByType::class, array_merge($options, $aFieldOptions));
} else {
// Remove field and dependencies
$oForm->add('group_by', HiddenType::class, ['mapped' => false]);
$oForm->add('values', HiddenType::class, ['mapped' => false]);
}
};
foreach($aCallbacks as $sField => $cCallback) {
$this->addEventListener(FormEvents::POST_SET_DATA, $cCallback);
$this->get($sField)->addEventListener(FormEvents::POST_SUBMIT, $cCallback);
}
}