mirror of
https://github.com/Combodo/iTop.git
synced 2026-04-19 00:28:47 +02:00
N°8772 - dynamic form
This commit is contained in:
@@ -491,6 +491,7 @@ return array(
|
||||
'Combodo\\iTop\\Forms\\Block\\DataModel\\AttributeChoiceFormBlock' => $baseDir . '/sources/Forms/Block/DataModel/AttributeChoiceFormBlock.php',
|
||||
'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\\Expression\\ExpressionFormBlock' => $baseDir . '/sources/Forms/Block/Expression/ExpressionFormBlock.php',
|
||||
'Combodo\\iTop\\Forms\\Block\\FormBlockException' => $baseDir . '/sources/Forms/Block/FormBlockException.php',
|
||||
'Combodo\\iTop\\Forms\\Block\\FormBlockIOException' => $baseDir . '/sources/Forms/Block/IO/FormBlockIOException.php',
|
||||
'Combodo\\iTop\\Forms\\Block\\FormType\\ChoiceFormType' => $baseDir . '/sources/Forms/Block/FormType/ChoiceFormType.php',
|
||||
@@ -523,6 +524,7 @@ return array(
|
||||
'Combodo\\iTop\\Forms\\FormsException' => $baseDir . '/sources/Forms/FormsException.php',
|
||||
'Combodo\\iTop\\Forms\\IFormBlock' => $baseDir . '/sources/Forms/IFormBlock.php',
|
||||
'Combodo\\iTop\\Forms\\Twig\\Extension\\FormCompatibilityExtension' => $baseDir . '/sources/Forms/Twig/Extension/FormCompatibilityExtension.php',
|
||||
'Combodo\\iTop\\ItopSdkFormDemonstrator\\Form\\Block\\ExpressionFormBlock' => $baseDir . '/sources/Forms/Block/Base/ExpressionFormBlock.php',
|
||||
'Combodo\\iTop\\PhpParser\\Evaluation\\PhpExpressionEvaluator' => $baseDir . '/sources/PhpParser/Evaluation/PhpExpressionEvaluator.php',
|
||||
'Combodo\\iTop\\Renderer\\BlockRenderer' => $baseDir . '/sources/Renderer/BlockRenderer.php',
|
||||
'Combodo\\iTop\\Renderer\\Bootstrap\\BsFieldRendererMappings' => $baseDir . '/sources/Renderer/Bootstrap/BsFieldRendererMappings.php',
|
||||
|
||||
@@ -872,6 +872,7 @@ class ComposerStaticInit7f81b4a2a468a061c306af5e447a9a9f
|
||||
'Combodo\\iTop\\Forms\\Block\\DataModel\\AttributeChoiceFormBlock' => __DIR__ . '/../..' . '/sources/Forms/Block/DataModel/AttributeChoiceFormBlock.php',
|
||||
'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\\Expression\\ExpressionFormBlock' => __DIR__ . '/../..' . '/sources/Forms/Block/Expression/ExpressionFormBlock.php',
|
||||
'Combodo\\iTop\\Forms\\Block\\FormBlockException' => __DIR__ . '/../..' . '/sources/Forms/Block/FormBlockException.php',
|
||||
'Combodo\\iTop\\Forms\\Block\\FormBlockIOException' => __DIR__ . '/../..' . '/sources/Forms/Block/IO/FormBlockIOException.php',
|
||||
'Combodo\\iTop\\Forms\\Block\\FormType\\ChoiceFormType' => __DIR__ . '/../..' . '/sources/Forms/Block/FormType/ChoiceFormType.php',
|
||||
@@ -904,6 +905,7 @@ class ComposerStaticInit7f81b4a2a468a061c306af5e447a9a9f
|
||||
'Combodo\\iTop\\Forms\\FormsException' => __DIR__ . '/../..' . '/sources/Forms/FormsException.php',
|
||||
'Combodo\\iTop\\Forms\\IFormBlock' => __DIR__ . '/../..' . '/sources/Forms/IFormBlock.php',
|
||||
'Combodo\\iTop\\Forms\\Twig\\Extension\\FormCompatibilityExtension' => __DIR__ . '/../..' . '/sources/Forms/Twig/Extension/FormCompatibilityExtension.php',
|
||||
'Combodo\\iTop\\ItopSdkFormDemonstrator\\Form\\Block\\ExpressionFormBlock' => __DIR__ . '/../..' . '/sources/Forms/Block/Base/ExpressionFormBlock.php',
|
||||
'Combodo\\iTop\\PhpParser\\Evaluation\\PhpExpressionEvaluator' => __DIR__ . '/../..' . '/sources/PhpParser/Evaluation/PhpExpressionEvaluator.php',
|
||||
'Combodo\\iTop\\Renderer\\BlockRenderer' => __DIR__ . '/../..' . '/sources/Renderer/BlockRenderer.php',
|
||||
'Combodo\\iTop\\Renderer\\Bootstrap\\BsFieldRendererMappings' => __DIR__ . '/../..' . '/sources/Renderer/Bootstrap/BsFieldRendererMappings.php',
|
||||
|
||||
@@ -26,6 +26,7 @@ class CheckboxFormBlock extends AbstractTypeFormBlock
|
||||
return CheckboxType::class;
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
function InitBlockOptions(array &$aUserOptions): void
|
||||
{
|
||||
parent::InitBlockOptions($aUserOptions);
|
||||
|
||||
96
sources/Forms/Block/Base/ExpressionFormBlock.php
Normal file
96
sources/Forms/Block/Base/ExpressionFormBlock.php
Normal file
@@ -0,0 +1,96 @@
|
||||
<?php
|
||||
/*
|
||||
* @copyright Copyright (C) 2010-2025 Combodo SAS
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
namespace Combodo\iTop\ItopSdkFormDemonstrator\Form\Block;
|
||||
|
||||
use Combodo\iTop\Forms\Block\AbstractFormBlock;
|
||||
use Combodo\iTop\Forms\Block\IO\Format\BooleanIOFormat;
|
||||
use Combodo\iTop\Forms\Block\IO\Format\RawFormat;
|
||||
use Combodo\iTop\Forms\FormsException;
|
||||
use IssueLog;
|
||||
use Symfony\Component\Form\FormEvents;
|
||||
|
||||
class ExpressionFormBlock extends AbstractFormBlock
|
||||
{
|
||||
const OUTPUT_RESULT = "result";
|
||||
|
||||
public function InitBlockOptions(array &$aUserOptions): void
|
||||
{
|
||||
parent::InitBlockOptions($aUserOptions);
|
||||
}
|
||||
|
||||
public function InitOutputs(): void
|
||||
{
|
||||
parent::InitOutputs();
|
||||
$this->AddOutput(self::OUTPUT_RESULT, BooleanIOFormat::class);
|
||||
// $this->AddOutput(self::OUTPUT_RAW, RawFormat::class);
|
||||
}
|
||||
|
||||
// public function InputHasChanged()
|
||||
// {
|
||||
// if (!$this->IsInputsDataReady()) {
|
||||
// return;
|
||||
// }
|
||||
// $sExpression = $this->GetOptions()['expression'];
|
||||
// $sValue = preg_replace_callback(
|
||||
// "/\[\[(?<input>[^\]]+)]]/",
|
||||
// function(array $aMatches): string {
|
||||
// return $this->GetInput($aMatches['input'])->GetValue();
|
||||
// },
|
||||
// $sExpression);
|
||||
//
|
||||
// foreach ($this->GetInputs() as $oFormInput) {
|
||||
// IssueLog::Info($oFormInput->GetName().' = '.$oFormInput->GetValue());
|
||||
// }
|
||||
// IssueLog::Info("Result of [$sExpression] is [$sValue]");
|
||||
//
|
||||
// $result = '';
|
||||
// eval('$result = '.$sValue.';');
|
||||
// IssueLog::Info("Result of [$sExpression] is eval to [$result]");
|
||||
//
|
||||
// $this->GetOutput(self::OUTPUT_RESULT)->SetValue(FormEvents::POST_SUBMIT, new BooleanIOFormat($result));
|
||||
// $this->GetOutput(self::OUTPUT_VALUE)->SetValue(FormEvents::POST_SUBMIT, new RawFormat($result));
|
||||
// }
|
||||
|
||||
|
||||
|
||||
public function InputHasChanged()
|
||||
{
|
||||
if (!$this->IsInputsDataReady()) {
|
||||
return;
|
||||
}
|
||||
$sExpression = $this->GetOptions()['expression'];
|
||||
|
||||
$this->Compute($sExpression, FormEvents::POST_SET_DATA);
|
||||
$this->Compute($sExpression, FormEvents::POST_SUBMIT);
|
||||
}
|
||||
|
||||
public function Compute(string $sExpression, string $sEventType): void
|
||||
{
|
||||
try{
|
||||
$sValue = preg_replace_callback(
|
||||
"/\[\[(?<input>[^\]]+)]]/",
|
||||
function(array $aMatches) use ($sEventType): ?string {
|
||||
$oInput = $this->GetInput($aMatches['input']);
|
||||
if(!$oInput->HasEventValue($sEventType)){
|
||||
throw new FormsException('Unable to compute expression: input '.$aMatches['input'].' has no value for event '.$sEventType.'.');
|
||||
}
|
||||
return $oInput->GetValue($sEventType);
|
||||
},
|
||||
$sExpression);
|
||||
|
||||
$result = '';
|
||||
eval('$result = '.$sValue.';');
|
||||
|
||||
$this->GetOutput(self::OUTPUT_RESULT)->SetValue($sEventType, new BooleanIOFormat($result));
|
||||
$this->GetOutput(self::OUTPUT_VALUE)->SetValue($sEventType, new RawFormat($result));
|
||||
}
|
||||
catch(\Exception $e){
|
||||
IssueLog::Exception('Compute expression block issue', $e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
96
sources/Forms/Block/Expression/ExpressionFormBlock.php
Normal file
96
sources/Forms/Block/Expression/ExpressionFormBlock.php
Normal file
@@ -0,0 +1,96 @@
|
||||
<?php
|
||||
/*
|
||||
* @copyright Copyright (C) 2010-2025 Combodo SAS
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
namespace Combodo\iTop\Forms\Block\Expression;
|
||||
|
||||
use Combodo\iTop\Forms\Block\AbstractFormBlock;
|
||||
use Combodo\iTop\Forms\Block\IO\Format\BooleanIOFormat;
|
||||
use Combodo\iTop\Forms\Block\IO\Format\RawFormat;
|
||||
use Combodo\iTop\Forms\FormsException;
|
||||
use IssueLog;
|
||||
use Symfony\Component\Form\FormEvents;
|
||||
|
||||
class ExpressionFormBlock extends AbstractFormBlock
|
||||
{
|
||||
const OUTPUT_RESULT = "result";
|
||||
|
||||
public function InitBlockOptions(array &$aUserOptions): void
|
||||
{
|
||||
parent::InitBlockOptions($aUserOptions);
|
||||
}
|
||||
|
||||
public function InitOutputs(): void
|
||||
{
|
||||
parent::InitOutputs();
|
||||
$this->AddOutput(self::OUTPUT_RESULT, BooleanIOFormat::class);
|
||||
// $this->AddOutput(self::OUTPUT_RAW, RawFormat::class);
|
||||
}
|
||||
|
||||
// public function InputHasChanged()
|
||||
// {
|
||||
// if (!$this->IsInputsDataReady()) {
|
||||
// return;
|
||||
// }
|
||||
// $sExpression = $this->GetOptions()['expression'];
|
||||
// $sValue = preg_replace_callback(
|
||||
// "/\[\[(?<input>[^\]]+)]]/",
|
||||
// function(array $aMatches): string {
|
||||
// return $this->GetInput($aMatches['input'])->GetValue();
|
||||
// },
|
||||
// $sExpression);
|
||||
//
|
||||
// foreach ($this->GetInputs() as $oFormInput) {
|
||||
// IssueLog::Info($oFormInput->GetName().' = '.$oFormInput->GetValue());
|
||||
// }
|
||||
// IssueLog::Info("Result of [$sExpression] is [$sValue]");
|
||||
//
|
||||
// $result = '';
|
||||
// eval('$result = '.$sValue.';');
|
||||
// IssueLog::Info("Result of [$sExpression] is eval to [$result]");
|
||||
//
|
||||
// $this->GetOutput(self::OUTPUT_RESULT)->SetValue(FormEvents::POST_SUBMIT, new BooleanIOFormat($result));
|
||||
// $this->GetOutput(self::OUTPUT_VALUE)->SetValue(FormEvents::POST_SUBMIT, new RawFormat($result));
|
||||
// }
|
||||
|
||||
|
||||
|
||||
public function InputHasChanged()
|
||||
{
|
||||
if (!$this->IsInputsDataReady()) {
|
||||
return;
|
||||
}
|
||||
$sExpression = $this->GetOptions()['expression'];
|
||||
|
||||
$this->Compute($sExpression, FormEvents::POST_SET_DATA);
|
||||
$this->Compute($sExpression, FormEvents::POST_SUBMIT);
|
||||
}
|
||||
|
||||
public function Compute(string $sExpression, string $sEventType): void
|
||||
{
|
||||
try{
|
||||
$sValue = preg_replace_callback(
|
||||
"/\[\[(?<input>[^\]]+)]]/",
|
||||
function(array $aMatches) use ($sEventType): ?string {
|
||||
$oInput = $this->GetInput($aMatches['input']);
|
||||
if(!$oInput->HasEventValue($sEventType)){
|
||||
throw new FormsException('Unable to compute expression: input '.$aMatches['input'].' has no value for event '.$sEventType.'.');
|
||||
}
|
||||
return $oInput->GetValue($sEventType);
|
||||
},
|
||||
$sExpression);
|
||||
|
||||
$result = '';
|
||||
eval('$result = '.$sValue.';');
|
||||
|
||||
$this->GetOutput(self::OUTPUT_RESULT)->SetValue($sEventType, new BooleanIOFormat($result));
|
||||
$this->GetOutput(self::OUTPUT_VALUE)->SetValue($sEventType, new RawFormat($result));
|
||||
}
|
||||
catch(\Exception $e){
|
||||
IssueLog::Exception('Compute expression block issue', $e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -245,5 +245,14 @@ class AbstractFormIO
|
||||
return $this->HasValue();
|
||||
}
|
||||
|
||||
public function HasBindingOut(): bool
|
||||
{
|
||||
return count($this->aBindingsToInputs) > 0;
|
||||
}
|
||||
|
||||
public function GetBindingsToInputs(): array
|
||||
{
|
||||
return $this->aBindingsToInputs;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -26,8 +26,8 @@ class DependencyHandler
|
||||
/** @var DependencyMap dependencies map */
|
||||
private DependencyMap $oDependenciesMap;
|
||||
|
||||
/** @var array Debug data */
|
||||
private array $aDebugData = [];
|
||||
/** @var array events */
|
||||
private array $aEvents = [];
|
||||
private readonly string $sName;
|
||||
private readonly AbstractFormBlock $oFormBlock;
|
||||
private readonly FormBuilder $oFormBuilder;
|
||||
@@ -94,12 +94,8 @@ class DependencyHandler
|
||||
continue;
|
||||
}
|
||||
|
||||
$this->aDebugData[] = [
|
||||
'builder' => $this->oFormBuilder->getName(),
|
||||
'event' => 'form.listen',
|
||||
'form' => $sOutputBlockName,
|
||||
'value' => 'NA',
|
||||
];
|
||||
// Add event
|
||||
$this->AddEvent('form.listen', $sOutputBlockName);
|
||||
|
||||
// Listen the output block POST_SET_DATA & POST_SUBMIT
|
||||
$this->oFormBuilder->get($sOutputBlockName)->addEventListener(FormEvents::POST_SET_DATA, $this->GetEventListeningCallback());
|
||||
@@ -120,12 +116,8 @@ class DependencyHandler
|
||||
// Get the event type
|
||||
$sEventType = FormHelper::GetEventType($oEvent);
|
||||
|
||||
$this->aDebugData[] = [
|
||||
'builder' => $this->oFormBuilder->getName(),
|
||||
'event' => $sEventType,
|
||||
'form' => $oEvent->getForm()->getName(),
|
||||
'value' => $oEvent->getData(),
|
||||
];
|
||||
// Add event
|
||||
$this->AddEvent($sEventType, $oEvent->getForm()->getName(), $oEvent->getData());
|
||||
|
||||
// Get the form
|
||||
$oForm = $oEvent->getForm();
|
||||
@@ -176,9 +168,13 @@ class DependencyHandler
|
||||
if ($oDependentBlock->IsVisible($sEventType) && $oDependentBlock->IsInputsDataReady($sEventType)) {
|
||||
|
||||
// Get the dependent field options
|
||||
$aBefore = $oDependentBlock->GetOptionsMergedWithDynamic();
|
||||
$oDependentBlock->UpdateDynamicOptions($sEventType);
|
||||
$aOptions = $oDependentBlock->GetOptionsMergedWithDynamic($sEventType);
|
||||
|
||||
// Options changed flag
|
||||
$bOptionsChanged = FormHelper::CompareArrayValues($aBefore, $aOptions);
|
||||
|
||||
// Add the listener callback to the dependent field if it is also a dependency for another field
|
||||
if ($this->oDependenciesMap->IsTheBlockInDependencies($oDependentBlock->getName())) {
|
||||
|
||||
@@ -188,22 +184,12 @@ class DependencyHandler
|
||||
]);
|
||||
}
|
||||
|
||||
if ($oDependentBlock->AllowAdd($sEventType)) {
|
||||
|
||||
$this->aDebugData[] = [
|
||||
'builder' => $this->oFormBuilder->getName(),
|
||||
'event' => 'form.add',
|
||||
'form' => $oDependentBlock->getName(),
|
||||
'value' => 'NA',
|
||||
];
|
||||
if ( (!$oDependentBlock->IsAdded() || $bOptionsChanged) && $oDependentBlock->AllowAdd($sEventType)) {
|
||||
|
||||
// Add events
|
||||
$this->AddEvent('form.add', $oDependentBlock->getName());
|
||||
if (array_key_exists('builder_listener', $aOptions)) {
|
||||
$this->aDebugData[] = [
|
||||
'builder' => $this->oFormBuilder->getName(),
|
||||
'event' => 'form.listen.after',
|
||||
'form' => $oDependentBlock->getName(),
|
||||
'value' => 'NA',
|
||||
];
|
||||
$this->AddEvent('form.listen.after', $oDependentBlock->getName());
|
||||
}
|
||||
|
||||
// Mark the dependency as added
|
||||
@@ -220,12 +206,8 @@ class DependencyHandler
|
||||
$oForm->remove($oDependentBlock->GetName());
|
||||
$oDependentBlock->SetAdded(false);
|
||||
|
||||
$this->aDebugData[] = [
|
||||
'builder' => $this->oFormBuilder->getName(),
|
||||
'event' => 'form.remove',
|
||||
'form' => $oDependentBlock->getName(),
|
||||
'value' => 'NA'
|
||||
];
|
||||
// Add event
|
||||
$this->AddEvent('form.remove', $oDependentBlock->getName());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -239,7 +221,7 @@ class DependencyHandler
|
||||
*/
|
||||
public function GetDebugData(): array
|
||||
{
|
||||
return $this->aDebugData;
|
||||
return $this->aEvents;
|
||||
}
|
||||
|
||||
public function GetMap(): DependencyMap
|
||||
@@ -247,13 +229,20 @@ class DependencyHandler
|
||||
return $this->oDependenciesMap;
|
||||
}
|
||||
|
||||
public function GetSubBlocks(): array
|
||||
{
|
||||
return $this->aSubBlocks;
|
||||
}
|
||||
|
||||
public function GetName(): string
|
||||
{
|
||||
return $this->sName;
|
||||
}
|
||||
|
||||
private function AddEvent(string $sEvent, string $sForm, mixed $oValue = 'NA'): void
|
||||
{
|
||||
|
||||
$this->aEvents[] = [
|
||||
'builder' => $this->oFormBuilder->getName(),
|
||||
'event' => $sEvent,
|
||||
'form' => $sForm,
|
||||
'value' => $oValue,
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -23,6 +23,9 @@ class DependencyMap
|
||||
/** @var array array of blocks with dependencies group by dependence */
|
||||
private array $aBlocksWithDependenciesGroupByDependence = [];
|
||||
|
||||
/** @var array array of binding */
|
||||
private array $aBindings = [];
|
||||
|
||||
/** @var array array of binding (OUT > OUT) grouped by block and output name */
|
||||
private array $aBindingsOutputToInput = [];
|
||||
|
||||
@@ -148,6 +151,7 @@ class DependencyMap
|
||||
|
||||
// add to map
|
||||
$map[$sBlockName][$sIOName][] = $oBinding;
|
||||
$this->aBindings[] = $oBinding;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -240,4 +244,9 @@ class DependencyMap
|
||||
{
|
||||
return $this->aBindingsOutputToOutputs;
|
||||
}
|
||||
|
||||
public function GetBindings()
|
||||
{
|
||||
return $this->aBindings;
|
||||
}
|
||||
}
|
||||
@@ -30,4 +30,22 @@ class FormHelper
|
||||
|
||||
throw new FormBuilderException(sprintf('Unknown event type %s', get_class($event)));
|
||||
}
|
||||
|
||||
public static function CompareArrayValues($mValue1, $mValue2): int
|
||||
{
|
||||
if (is_array($mValue1) && is_array($mValue2)) {
|
||||
if (count($mValue1) !== count($mValue2)) {
|
||||
return 1;
|
||||
}
|
||||
$aDiff = array_udiff_assoc($mValue1, $mValue2, [FormHelper::class, 'CompareArrayValues']);
|
||||
|
||||
return count($aDiff);
|
||||
}
|
||||
|
||||
if ($mValue1 === $mValue2) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user