N°8772 - dynamic form

This commit is contained in:
Benjamin Dalsass
2025-11-17 14:51:11 +01:00
parent 51ebbc4274
commit 753d0acce4
9 changed files with 146 additions and 16 deletions

View File

@@ -491,9 +491,12 @@ return array(
'Combodo\\iTop\\Forms\\Block\\Base\\TextFormBlock' => $baseDir . '/sources/Forms/Block/Base/TextFormBlock.php',
'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\\IDataModelBlock' => $baseDir . '/sources/Forms/Block/DataModel/IDataModelBlock.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\\FormBlockDescription' => $baseDir . '/sources/Forms/Block/FormBlockDescription.php',
'Combodo\\iTop\\Forms\\Block\\FormBlockException' => $baseDir . '/sources/Forms/Block/FormBlockException.php',
'Combodo\\iTop\\Forms\\Block\\FormBlockFactory' => $baseDir . '/sources/Forms/Block/FormBlockFactory.php',
'Combodo\\iTop\\Forms\\FormBuilder\\DependencyHandler' => $baseDir . '/sources/Forms/FormBuilder/DependencyHandler.php',
'Combodo\\iTop\\Forms\\FormBuilder\\DependencyMap' => $baseDir . '/sources/Forms/FormBuilder/DependencyMap.php',
'Combodo\\iTop\\Forms\\FormBuilder\\FormBuilder' => $baseDir . '/sources/Forms/FormBuilder/FormBuilder.php',
@@ -528,6 +531,7 @@ return array(
'Combodo\\iTop\\Forms\\Register\\IORegister' => $baseDir . '/sources/Forms/Register/IORegister.php',
'Combodo\\iTop\\Forms\\Register\\Option' => $baseDir . '/sources/Forms/Register/Option.php',
'Combodo\\iTop\\Forms\\Register\\OptionsRegister' => $baseDir . '/sources/Forms/Register/OptionsRegister.php',
'Combodo\\iTop\\Forms\\Register\\RegisterException' => $baseDir . '/sources/Forms/Register/RegisterException.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',

View File

@@ -872,9 +872,12 @@ class ComposerStaticInit7f81b4a2a468a061c306af5e447a9a9f
'Combodo\\iTop\\Forms\\Block\\Base\\TextFormBlock' => __DIR__ . '/../..' . '/sources/Forms/Block/Base/TextFormBlock.php',
'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\\IDataModelBlock' => __DIR__ . '/../..' . '/sources/Forms/Block/DataModel/IDataModelBlock.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\\FormBlockDescription' => __DIR__ . '/../..' . '/sources/Forms/Block/FormBlockDescription.php',
'Combodo\\iTop\\Forms\\Block\\FormBlockException' => __DIR__ . '/../..' . '/sources/Forms/Block/FormBlockException.php',
'Combodo\\iTop\\Forms\\Block\\FormBlockFactory' => __DIR__ . '/../..' . '/sources/Forms/Block/FormBlockFactory.php',
'Combodo\\iTop\\Forms\\FormBuilder\\DependencyHandler' => __DIR__ . '/../..' . '/sources/Forms/FormBuilder/DependencyHandler.php',
'Combodo\\iTop\\Forms\\FormBuilder\\DependencyMap' => __DIR__ . '/../..' . '/sources/Forms/FormBuilder/DependencyMap.php',
'Combodo\\iTop\\Forms\\FormBuilder\\FormBuilder' => __DIR__ . '/../..' . '/sources/Forms/FormBuilder/FormBuilder.php',
@@ -909,6 +912,7 @@ class ComposerStaticInit7f81b4a2a468a061c306af5e447a9a9f
'Combodo\\iTop\\Forms\\Register\\IORegister' => __DIR__ . '/../..' . '/sources/Forms/Register/IORegister.php',
'Combodo\\iTop\\Forms\\Register\\Option' => __DIR__ . '/../..' . '/sources/Forms/Register/Option.php',
'Combodo\\iTop\\Forms\\Register\\OptionsRegister' => __DIR__ . '/../..' . '/sources/Forms/Register/OptionsRegister.php',
'Combodo\\iTop\\Forms\\Register\\RegisterException' => __DIR__ . '/../..' . '/sources/Forms/Register/RegisterException.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',

View File

@@ -8,6 +8,7 @@ namespace Combodo\iTop\Forms\Block;
use Combodo\iTop\Forms\IO\Format\BooleanIOFormat;
use Combodo\iTop\Forms\Register\IORegister;
use Combodo\iTop\Forms\Register\OptionsRegister;
abstract class AbstractTypeFormBlock extends AbstractFormBlock
{
@@ -80,4 +81,14 @@ abstract class AbstractTypeFormBlock extends AbstractFormBlock
{
$this->bIsAddedToForm = $bIsAdded;
}
/** @inheritdoc */
public function UpdateOptions(OptionsRegister $oOptionsRegister): void
{
parent::UpdateOptions($oOptionsRegister);
if($this->GetInput(self::INPUT_ENABLE)->IsBound()){
$oOptionsRegister->SetOption('disabled', !$this->GetInputValue(self::INPUT_ENABLE));
}
}
}

View File

@@ -66,29 +66,54 @@ class FormBlock extends AbstractTypeFormBlock
* Add a child form.
*
* @param string $sName block name
* @param string $sType block class name
* @param array $aSymfonyOptions options
* @param string $sBlockClass block class name
* @param array $aOptions options
*
* @return $this
* @throws ReflectionException
* @throws \Combodo\iTop\Forms\Block\FormBlockException
* @throws FormBlockException
*/
public function Add(string $sName, string $sType, array $aSymfonyOptions, array $aOptions = []): AbstractFormBlock
public function Add(string $sName, string $sBlockClass,array $aOptions = []): AbstractFormBlock
{
$oRef = new ReflectionClass($sType);
if($oRef->isSubclassOf(AbstractFormBlock::class) === false){
throw new FormBlockException("The block type '$sType' is not a subclass of AbstractFormBlock.");
}
$this->VerifyBlockName($sName);
$this->VerifyBlockClassName($sBlockClass);
$aOptions['priority'] = -count($this->aChildrenBlocks);
$oSubFormBlock = new ($sType)($sName, $aSymfonyOptions, $aOptions);
$oSubFormBlock = new ($sBlockClass)($sName, $aOptions);
$this->aChildrenBlocks[$sName] = $oSubFormBlock;
$oSubFormBlock->SetParent($this);
return $oSubFormBlock;
}
/**
* @param string $sBlockName
*
* @return void
* @throws FormBlockException
*/
private function VerifyBlockName(string $sBlockName): void
{
if(!ctype_alnum(str_replace(array('-', '_'), '', $sBlockName))) {
throw new FormBlockException("Block name '$sBlockName' is not valid. Only alphanumeric characters, hyphens and underscores are allowed.");
}
}
/**
* @param string $sBlockClass
*
* @return void
* @throws FormBlockException
* @throws ReflectionException
*/
private function VerifyBlockClassName(string $sBlockClass): void
{
$oRef = new ReflectionClass($sBlockClass);
if($oRef->isSubclassOf(AbstractFormBlock::class) === false){
throw new FormBlockException("The block type '$sBlockClass' is not a subclass of AbstractFormBlock.");
}
}
/**
* Get the children forms.
*

View File

@@ -9,6 +9,7 @@ namespace Combodo\iTop\Forms\FormBuilder;
use Combodo\iTop\Forms\Block\AbstractFormBlock;
use Combodo\iTop\Forms\Block\AbstractTypeFormBlock;
use Combodo\iTop\Forms\Block\Base\FormBlock;
use Combodo\iTop\Forms\Block\FormBlockException;
use Exception;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Form\FormError;
@@ -156,7 +157,7 @@ class DependencyHandler
* @param string|null $sEventType
*
* @return void
* @throws \Combodo\iTop\Forms\Block\FormBlockException
* @throws FormBlockException
*/
private function CheckDependencies(FormInterface|FormBuilderInterface $oForm, string $sOutputBlock = null, string $sEventType = null): void
{

View File

@@ -22,9 +22,12 @@ class OptionsRegister
* @param bool $bTypeOption
*
* @return void
* @throws RegisterException
*/
public function SetOption(string $sOptionName, mixed $mDefaultValue = null, bool $bTypeOption = true): void
{
$this->VerifyOptionName($sOptionName);
if(isset($this->aOptions[$sOptionName])){
$this->aOptions[$sOptionName]->oValue = $mDefaultValue;
}
@@ -33,6 +36,19 @@ class OptionsRegister
}
}
/**
* @param string $sOptionName
*
* @return void
* @throws RegisterException
*/
private function VerifyOptionName(string $sOptionName): void
{
if(!ctype_alnum(str_replace(array('-', '_'), '', $sOptionName))) {
throw new RegisterException("Option name '$sOptionName' is not valid. Only alphanumeric characters, hyphens and underscores are allowed.");
}
}
/**
* Set an option array value.
*

View File

@@ -0,0 +1,18 @@
<?php
/*
* @copyright Copyright (C) 2010-2025 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
namespace Combodo\iTop\Forms\Register;
use Exception;
use Throwable;
class RegisterException extends Exception
{
public function __construct(string $message = '', int $code = 0, ?Throwable $previous = null, array $aContext = [])
{
parent::__construct($message, $code, $previous);
}
}

View File

@@ -4,12 +4,10 @@
* @license http://opensource.org/licenses/AGPL-3.0
*/
namespace Forms;
namespace Forms\Block;
use Combodo\iTop\Forms\Block\AbstractFormBlock;
use Combodo\iTop\Forms\Block\AbstractTypeFormBlock;
use Combodo\iTop\Forms\Block\Base\CheckboxFormBlock;
use Combodo\iTop\Forms\Block\Base\ChoiceFormBlock;
use Combodo\iTop\Forms\Block\Base\FormBlock;
use Combodo\iTop\Forms\Block\Base\TextFormBlock;
use Combodo\iTop\Forms\Block\FormBlockException;
@@ -26,7 +24,7 @@ use Symfony\Component\Form\Extension\Core\Type\TextType;
* Test forms block.
*
*/
class Block extends ItopDataTestCase
class BlockTest extends ItopDataTestCase
{
/**
@@ -34,7 +32,7 @@ class Block extends ItopDataTestCase
*
* @throws ReflectionException
*/
public function testFormBlockProvideSymfonyFormType(): void
public function testGetFormTypeReturnSymfonyType(): void
{
$aFormBlocks = InterfaceDiscovery::GetInstance()->FindItopClasses(iFormBlock::class);
foreach ($aFormBlocks as $sFormBlock) {
@@ -51,7 +49,7 @@ class Block extends ItopDataTestCase
*
* @throws ReflectionException
*/
public function testAddBlockFromSymfonyType(): void
public function testAddChildBlockClass(): void
{
$oFormBlock = new FormBlock('formBlock');
$this->expectException(FormBlockException::class);

View File

@@ -0,0 +1,53 @@
<?php
/*
* @copyright Copyright (C) 2010-2024 Combodo SAS
* @license http://opensource.org/licenses/AGPL-3.0
*/
namespace Forms\Register;
use Combodo\iTop\Forms\Register\OptionsRegister;
use Combodo\iTop\Forms\Register\RegisterException;
use Combodo\iTop\Test\UnitTest\ItopDataTestCase;
class OptionsRegisterTest extends ItopDataTestCase
{
private OptionsRegister $oOptionsRegister;
protected function setUp(): void
{
parent::setUp();
$this->oOptionsRegister = new OptionsRegister();
}
public function testSetOptionWithInvalidName(): void
{
$this->oOptionsRegister->SetOption('valid_option_name', 'value');
$this->expectException(RegisterException::class);
$this->oOptionsRegister->SetOption('not valid option name', 'value');
}
public function testSetNonTypeOption(): void
{
$this->oOptionsRegister->SetOption('not_a_type_option', 'value', false);
$this->assertArrayNotHasKey('not_a_type_option', $this->oOptionsRegister->GetOptions());
}
public function testSetOptionArrayValue(): void
{
$this->oOptionsRegister->SetOptionArrayValue('att', 'class', 'ibo-class');
$this->assertEquals('ibo-class', $this->oOptionsRegister->GetOption('att')['class']);
}
public function testHasOption(): void
{
$this->oOptionsRegister->SetOption('option', true);
$this->assertTrue($this->oOptionsRegister->HasOption('option'));
}
}