mirror of
https://github.com/Combodo/iTop.git
synced 2026-04-21 01:28:47 +02:00
SDK Form demonstrator introduce field dependency graph
This commit is contained in:
12
sources/Forms/Dependency/DependencyException.php
Normal file
12
sources/Forms/Dependency/DependencyException.php
Normal file
@@ -0,0 +1,12 @@
|
||||
<?php
|
||||
/*
|
||||
* @copyright Copyright (C) 2010-2025 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
namespace Combodo\iTop\Forms\Dependency;
|
||||
|
||||
class DependencyException extends \Exception
|
||||
{
|
||||
|
||||
}
|
||||
78
sources/Forms/Dependency/DependencyGraph.php
Normal file
78
sources/Forms/Dependency/DependencyGraph.php
Normal file
@@ -0,0 +1,78 @@
|
||||
<?php
|
||||
/*
|
||||
* @copyright Copyright (C) 2010-2025 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
namespace Combodo\iTop\Forms\Dependency;
|
||||
|
||||
class DependencyGraph implements \Iterator
|
||||
{
|
||||
use GraphTrait;
|
||||
private array $aFieldNameToPosition = [];
|
||||
|
||||
/**
|
||||
* @param string $sName
|
||||
* @param string $sType
|
||||
* @param array $aDependencies
|
||||
* @param array $aUserOptions
|
||||
*
|
||||
* @return void
|
||||
* @throws \Combodo\iTop\Forms\Dependency\DependencyException
|
||||
*/
|
||||
public function Add(string $sName, string $sType, array $aDependencies = [], array $aUserOptions = []): void
|
||||
{
|
||||
$oNode = new DependencyNode($sName, $sType, $aUserOptions);
|
||||
// Store field position
|
||||
$this->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;
|
||||
}
|
||||
}
|
||||
56
sources/Forms/Dependency/DependencyNode.php
Normal file
56
sources/Forms/Dependency/DependencyNode.php
Normal file
@@ -0,0 +1,56 @@
|
||||
<?php
|
||||
/*
|
||||
* @copyright Copyright (C) 2010-2025 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
namespace Combodo\iTop\Forms\Dependency;
|
||||
|
||||
class DependencyNode implements \Iterator
|
||||
{
|
||||
use GraphTrait;
|
||||
|
||||
public function __construct(private string $sName, private string $sType, private array $aUserOptions = [])
|
||||
{
|
||||
}
|
||||
|
||||
public function GetType(): string
|
||||
{
|
||||
return $this->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;
|
||||
}
|
||||
}
|
||||
50
sources/Forms/Dependency/GraphTrait.php
Normal file
50
sources/Forms/Dependency/GraphTrait.php
Normal file
@@ -0,0 +1,50 @@
|
||||
<?php
|
||||
/*
|
||||
* @copyright Copyright (C) 2010-2025 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
namespace Combodo\iTop\Forms\Dependency;
|
||||
|
||||
trait GraphTrait
|
||||
{
|
||||
private array $aChildren = [];
|
||||
private int $iPosition = 0;
|
||||
|
||||
public function AddChild(DependencyNode $node) {
|
||||
$this->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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -55,8 +55,8 @@ class ValuesFromAttcodeType extends AbstractType
|
||||
public function GetPrerequisites(array $aUserOptions): ?array
|
||||
{
|
||||
return [
|
||||
$aUserOptions['source_class'],
|
||||
$aUserOptions['source_attcode'],
|
||||
$aUserOptions['source_class'],
|
||||
];
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user