N°7063 - Forms SDK - Add Symfony forms component

error forms issue
This commit is contained in:
Benjamin Dalsass
2024-01-08 16:45:52 +01:00
parent 00f241e4cf
commit 0461cd7d51
17 changed files with 532 additions and 117 deletions

View File

@@ -55,3 +55,25 @@
.form_interval_horizontal > div{
margin-right: 8px;
}
.form-compound-fieldset{
padding: 8px;
border-radius: 10px;
border: 1px dashed lightgrey;
}
.form-layout-row{
display: flex;
flex-direction: row;
}
.form-layout-column{
margin: 10px;
}
.form-layout-column:first-child{
margin-left: 0px;
}
.form-layout-column:last-child{
margin-right: 0px;
}

View File

@@ -400,6 +400,7 @@ return array(
'Combodo\\iTop\\DesignDocument' => $baseDir . '/core/designdocument.class.inc.php',
'Combodo\\iTop\\DesignElement' => $baseDir . '/core/designdocument.class.inc.php',
'Combodo\\iTop\\FormImplementation\\Controller\\TestController' => $baseDir . '/sources/FormImplementation/Controller/TestController.php',
'Combodo\\iTop\\FormImplementation\\Dto\\CountDto' => $baseDir . '/sources/FormImplementation/Dto/CountDto.php',
'Combodo\\iTop\\FormImplementation\\Dto\\ObjectSearchDto' => $baseDir . '/sources/FormImplementation/Dto/ObjectSearchDto.php',
'Combodo\\iTop\\FormImplementation\\Helper\\FormHelper' => $baseDir . '/sources/FormImplementation/Helper/FormHelper.php',
'Combodo\\iTop\\FormImplementation\\Helper\\SelectDataProvider' => $baseDir . '/sources/FormImplementation/Helper/SelectDataProvider.php',
@@ -412,9 +413,10 @@ return array(
'Combodo\\iTop\\FormSDK\\Service\\FormFactoryBuilderTrait' => $baseDir . '/sources/FormSDK/Service/FormFactoryBuilderTrait.php',
'Combodo\\iTop\\FormSDK\\Service\\FormManager' => $baseDir . '/sources/FormSDK/Service/FormManager.php',
'Combodo\\iTop\\FormSDK\\Symfony\\SymfonyBridge' => $baseDir . '/sources/FormSDK/Symfony/SymfonyBridge.php',
'Combodo\\iTop\\FormSDK\\Symfony\\Type\\Compound\\FieldsetType' => $baseDir . '/sources/FormSDK/Symfony/Type/Compound/FieldsetType.php',
'Combodo\\iTop\\FormSDK\\Symfony\\Type\\Compound\\FormObjectType' => $baseDir . '/sources/FormSDK/Symfony/Type/Compound/FormObjectType.php',
'Combodo\\iTop\\FormSDK\\Symfony\\Type\\Layout\\AbstractLayoutType' => $baseDir . '/sources/FormSDK/Symfony/Type/Layout/LayoutType.php',
'Combodo\\iTop\\FormSDK\\Symfony\\Type\\Layout\\ColumnType' => $baseDir . '/sources/FormSDK/Symfony/Type/Layout/ColumnType.php',
'Combodo\\iTop\\FormSDK\\Symfony\\Type\\Layout\\FieldsetType' => $baseDir . '/sources/FormSDK/Symfony/Type/Layout/FieldsetType.php',
'Combodo\\iTop\\FormSDK\\Symfony\\Type\\Layout\\RowType' => $baseDir . '/sources/FormSDK/Symfony/Type/Layout/RowType.php',
'Combodo\\iTop\\Form\\Field\\AbstractSimpleField' => $baseDir . '/sources/Form/Field/AbstractSimpleField.php',
'Combodo\\iTop\\Form\\Field\\BlobField' => $baseDir . '/sources/Form/Field/BlobField.php',

View File

@@ -809,6 +809,7 @@ class ComposerStaticInit7f81b4a2a468a061c306af5e447a9a9f
'Combodo\\iTop\\DesignDocument' => __DIR__ . '/../..' . '/core/designdocument.class.inc.php',
'Combodo\\iTop\\DesignElement' => __DIR__ . '/../..' . '/core/designdocument.class.inc.php',
'Combodo\\iTop\\FormImplementation\\Controller\\TestController' => __DIR__ . '/../..' . '/sources/FormImplementation/Controller/TestController.php',
'Combodo\\iTop\\FormImplementation\\Dto\\CountDto' => __DIR__ . '/../..' . '/sources/FormImplementation/Dto/CountDto.php',
'Combodo\\iTop\\FormImplementation\\Dto\\ObjectSearchDto' => __DIR__ . '/../..' . '/sources/FormImplementation/Dto/ObjectSearchDto.php',
'Combodo\\iTop\\FormImplementation\\Helper\\FormHelper' => __DIR__ . '/../..' . '/sources/FormImplementation/Helper/FormHelper.php',
'Combodo\\iTop\\FormImplementation\\Helper\\SelectDataProvider' => __DIR__ . '/../..' . '/sources/FormImplementation/Helper/SelectDataProvider.php',
@@ -821,9 +822,10 @@ class ComposerStaticInit7f81b4a2a468a061c306af5e447a9a9f
'Combodo\\iTop\\FormSDK\\Service\\FormFactoryBuilderTrait' => __DIR__ . '/../..' . '/sources/FormSDK/Service/FormFactoryBuilderTrait.php',
'Combodo\\iTop\\FormSDK\\Service\\FormManager' => __DIR__ . '/../..' . '/sources/FormSDK/Service/FormManager.php',
'Combodo\\iTop\\FormSDK\\Symfony\\SymfonyBridge' => __DIR__ . '/../..' . '/sources/FormSDK/Symfony/SymfonyBridge.php',
'Combodo\\iTop\\FormSDK\\Symfony\\Type\\Compound\\FieldsetType' => __DIR__ . '/../..' . '/sources/FormSDK/Symfony/Type/Compound/FieldsetType.php',
'Combodo\\iTop\\FormSDK\\Symfony\\Type\\Compound\\FormObjectType' => __DIR__ . '/../..' . '/sources/FormSDK/Symfony/Type/Compound/FormObjectType.php',
'Combodo\\iTop\\FormSDK\\Symfony\\Type\\Layout\\AbstractLayoutType' => __DIR__ . '/../..' . '/sources/FormSDK/Symfony/Type/Layout/LayoutType.php',
'Combodo\\iTop\\FormSDK\\Symfony\\Type\\Layout\\ColumnType' => __DIR__ . '/../..' . '/sources/FormSDK/Symfony/Type/Layout/ColumnType.php',
'Combodo\\iTop\\FormSDK\\Symfony\\Type\\Layout\\FieldsetType' => __DIR__ . '/../..' . '/sources/FormSDK/Symfony/Type/Layout/FieldsetType.php',
'Combodo\\iTop\\FormSDK\\Symfony\\Type\\Layout\\RowType' => __DIR__ . '/../..' . '/sources/FormSDK/Symfony/Type/Layout/RowType.php',
'Combodo\\iTop\\Form\\Field\\AbstractSimpleField' => __DIR__ . '/../..' . '/sources/Form/Field/AbstractSimpleField.php',
'Combodo\\iTop\\Form\\Field\\BlobField' => __DIR__ . '/../..' . '/sources/Form/Field/BlobField.php',

View File

@@ -3,7 +3,7 @@
'name' => 'combodo/itop',
'pretty_version' => 'dev-develop',
'version' => 'dev-develop',
'reference' => '75fde4c9a3fb4b46ae8765e961d0f590bdad199b',
'reference' => '00f241e4cf314d4190bedeba80de9ec12461b958',
'type' => 'project',
'install_path' => __DIR__ . '/../../',
'aliases' => array(),
@@ -22,7 +22,7 @@
'combodo/itop' => array(
'pretty_version' => 'dev-develop',
'version' => 'dev-develop',
'reference' => '75fde4c9a3fb4b46ae8765e961d0f590bdad199b',
'reference' => '00f241e4cf314d4190bedeba80de9ec12461b958',
'type' => 'project',
'install_path' => __DIR__ . '/../../',
'aliases' => array(),

View File

@@ -0,0 +1,34 @@
<?php
namespace Combodo\iTop\FormImplementation\Dto;
use ArrayAccess;
class CountDto implements ArrayAccess
{
public function __construct(
public int $count1 = 11,
public int $count2 = 22,
public int $count3 = 33,
) {
}
public function offsetExists($offset) : bool
{
return property_exists($this, $offset);
}
public function offsetGet($offset) : mixed
{
return $this->$offset;
}
public function offsetSet($offset, $value) : void
{
$this->$offset = $value;
}
public function offsetUnset($offset) : void
{
unset($this->$offset);
}
}

View File

@@ -3,6 +3,9 @@
namespace Combodo\iTop\FormImplementation\Helper;
use Combodo\iTop\Controller\AbstractAppController;
use Combodo\iTop\FormImplementation\Dto\CountDto;
use Combodo\iTop\FormSDK\Field\FormFieldDescription;
use Combodo\iTop\FormSDK\Field\FormFieldTypeEnumeration;
use Combodo\iTop\FormSDK\Service\FormManager;
use DateInterval;
use DateTime;
@@ -30,6 +33,23 @@ class FormHelper
// create a factory
$oFormFactory = $oFormManager->CreateFactory();
// form data
$aData = [
'city' => 'Autun',
'tel' => '+33(6) 35 57 48 77',
'birthday' => new DateTime('1979/06/27'),
'count' => 10,
'counts' => ['count1' => 10, 'count2' => 20, 'count3' => 30],
'counts2' => new CountDto(),
'interval' => ['days' => '12', 'hours' => '13', 'years' => '10', 'months' => '6', 'minutes' => '0', 'seconds' => '0', 'weeks' => '3'],
'blog' => 'Your <b>story</b>',
'notify' => true,
'language' => 'FR FR',
'mode' => '1',
'options' => ['0', '2','4']
];
$oFormFactory->SetData($aData);
// add X person forms...
for($i = 0 ; $i < self::$PERSON_COUNT ; $i++){
@@ -37,7 +57,7 @@ class FormHelper
$oPerson = MetaModel::GetObject('Person', $i+1);
// create object adapter
$oObjectPlugin = $oFormFactory->CreateObjectAdapter($oPerson, true);
$oObjectPlugin = $oFormFactory->CreateObjectAdapter($oPerson, false);
$oObjectPlugin->AddAttribute('name');
$oObjectPlugin->AddAttribute('mobile_phone');
}
@@ -47,27 +67,51 @@ class FormHelper
'label' => 'Ma ville',
'help' => 'This is where you live',
'constraints' => new Length(['min' => 3])
], 'Autun');
]);
// tel - text with pattern
$oFormFactory->AddTextField('tel', [
'label' => 'Tel',
'constraints' => new Regex(['pattern' => '/\+33\(\d\) \d\d \d\d \d\d \d\d/'], null, '+{33}(0) 00 00 00 00'),
'required' => false
], '+33(6) 35 57 48 77');
]);
// birthday - date
$oFormFactory->AddDateField('birthday', [
'label' => 'Anniversaire',
'widget' => 'single_text',
'required' => false
], new DateTime('1979/06/27'));
]);
// count - number
$oFormFactory->AddNumberField('count', [
'label' => 'Compteur',
'required' => false
], 10);
]);
// counts - fieldset
$oCount1 = new FormFieldDescription('count1', FormFieldTypeEnumeration::NUMBER, []);
$oCount2 = new FormFieldDescription('count2', FormFieldTypeEnumeration::NUMBER, []);
$oCount3 = new FormFieldDescription('count3', FormFieldTypeEnumeration::NUMBER, []);
$oFormFactory->AddFieldSet('counts', [
'label' => 'Compteurs',
'required' => false,
'fields' => [
$oCount1, $oCount2, $oCount3
]
]);
// counts - fieldset alternative
$oCount1 = new FormFieldDescription('count1', FormFieldTypeEnumeration::NUMBER, []);
$oCount2 = new FormFieldDescription('count2', FormFieldTypeEnumeration::NUMBER, []);
$oCount3 = new FormFieldDescription('count3', FormFieldTypeEnumeration::NUMBER, []);
$oFormFactory->AddFieldSet('counts2', [
'label' => 'Compteurs',
'required' => false,
'fields' => [
$oCount1, $oCount2, $oCount3 // OR $oData
]
]);
// interval - duration
$oFormFactory->AddDurationField('interval', [
@@ -80,24 +124,24 @@ class FormHelper
'attr' => [
'class' => 'form_interval_horizontal'
]
], ['days' => '12', 'hours' => '13', 'years' => '10', 'months' => '6', 'minutes' => '0', 'seconds' => '0', 'weeks' => '3']);
]);
// ready
$oFormFactory->AddSwitchField('notify', [
'label' => 'Veuillez m\'avertir en cas de changement',
], true);
]);
// blog - date
$oFormFactory->AddAreaField('blog', [
'label' => 'Blog',
'required' => false
], 'Your story');
]);
// language - select with static data
$oFormFactory->AddSelectField('language', [
'label' => 'Ma langue',
'choices' => SelectDataProvider::GetApplicationLanguages()
], 'FR FR');
]);
// dog - select with ajax API
$oFormFactory->AddSelectAjaxField('dog', [
@@ -136,10 +180,10 @@ class FormHelper
'label_attr' => [
'class' => 'radio-inline'
]
], '1');
]);
// options - select with static data
$oFormFactory->AddSelectField('option', [
$oFormFactory->AddSelectField('options', [
'label' => 'Mes options',
'choices' => SelectDataProvider::GetOptions(),
'expanded' => true,
@@ -147,7 +191,7 @@ class FormHelper
'label_attr' => [
'class' => 'checkbox-inline'
]
], ['0', '2','4']);
]);
// layout description
$oFormFactory->SetLayoutDescription([

View File

@@ -33,8 +33,8 @@ enum FormFieldTypeEnumeration : string
case DATE = 'DATE';
case SELECT = 'SELECT';
case SWITCH = 'SWITCH';
case DB_OBJECT = 'DB_OBJECT';
case DURATION = 'DURATION';
case FIELDSET = 'FIELDSET';
/**
* Return available options.
@@ -44,7 +44,7 @@ enum FormFieldTypeEnumeration : string
public function GetAvailableOptions() : array
{
// global options
$aOptions = ['required', 'disabled', 'attr', 'label', 'label_attr', 'help'];
$aOptions = ['required', 'disabled', 'attr', 'label', 'label_attr', 'help', 'inherit_data'];
// specific options
$test = match ($this) {
@@ -60,7 +60,7 @@ enum FormFieldTypeEnumeration : string
FormFieldTypeEnumeration::DURATION => array_merge($aOptions,
['input', 'with_minutes', 'with_seconds', 'with_weeks', 'with_days']
),
FormFieldTypeEnumeration::DB_OBJECT => array_merge($aOptions,
FormFieldTypeEnumeration::FIELDSET => array_merge($aOptions,
['fields']
),
default => $aOptions,

View File

@@ -192,9 +192,9 @@ final class FormFactoryObjectAdapter implements FormFactoryAdapterInterface
}
if($this->bGroup){
$oGroupDescriptions = new FormFieldDescription($this->GetIdentifier(), FormFieldTypeEnumeration::DB_OBJECT, [
$oGroupDescriptions = new FormFieldDescription($this->GetIdentifier(), FormFieldTypeEnumeration::FIELDSET, [
'fields' => $aFieldsDescriptions,
'label' => $this->GetLabel()
'label' => $this->GetLabel(),
]);
return [$this->GetIdentifier() => $oGroupDescriptions];
}

View File

@@ -49,8 +49,11 @@ class FormFactory
/** @var array $aFieldsDescriptions form types descriptions */
private array $aFieldsDescriptions = [];
/** @var array $aFieldsData form data */
private array $aFieldsData = [];
/** @var mixed $oFieldsData form data */
private mixed $oFieldsData = [];
/** @var array $aLayoutDecription description of the layout */
private array $aLayoutDecription;
/** builder */
use FormFactoryBuilderTrait;
@@ -70,27 +73,34 @@ class FormFactory
}
/**
* Return fields descriptions and data arrays.
* Return fields descriptions.
*
* @return array{descriptions:array, data:array}
* @return array{fields_descriptions:array, layout_description:array}
*/
public function GetFieldsDescriptionsAndData() : array
public function GetFieldsDescriptions() : array
{
// prepare data
$aResult = [
'descriptions' => $this->aFieldsDescriptions,
'data' => $this->aFieldsData,
];
$aResult = $this->aFieldsDescriptions;
// merge each adapter data...
foreach ($this->GetAllAdapters() as $oAdapter){
$aResult['descriptions'] = array_merge($aResult['descriptions'], $oAdapter->GetFieldsDescriptions());
$aResult['data'] = array_merge($aResult['data'], $oAdapter->GetFieldsData());
$aResult = array_merge($aResult, $oAdapter->GetFieldsDescriptions());
}
return $aResult;
}
/**
* Return layout description.
*
* @return array
*/
public function GetLayoutDescription() : array
{
return $this->aLayoutDecription;
}
/**
* Create an object adapter.
*
@@ -130,6 +140,43 @@ class FormFactory
return $this->aAdapters;
}
/**
* Set layout description.
*
* @param array $aLayoutDescription
*
* @return $this
*/
public function SetLayoutDescription(array $aLayoutDescription)
{
$this->aLayoutDecription = $aLayoutDescription;
return $this;
}
/**
* @param mixed $oData
*
* @return void
*/
public function SetData(mixed $oData) : void
{
$this->oFieldsData = $oData;
}
/***
* @return array
*/
public function GetData() : mixed
{
$aData = $this->oFieldsData;
foreach ($this->GetAllAdapters() as $adapter){
$aData = array_merge($aData, $adapter->GetFieldsData());
}
return $aData;
}
/**
* Create form.
*
@@ -138,8 +185,8 @@ class FormFactory
*/
public function CreateForm(?string $sName = null) : mixed
{
['descriptions' => $aDescriptions, 'data' => $aData] = $this->GetFieldsDescriptionsAndData();
return $this->oSymfonyBridge->CreateForm($aDescriptions, $aData, $sName);
$aFieldsDescriptions = $this->GetFieldsDescriptions();
return $this->oSymfonyBridge->CreateForm($aFieldsDescriptions, $this->GetData(), $sName, $this->GetLayoutDescription());
}
}

View File

@@ -16,11 +16,10 @@ trait FormFactoryBuilderTrait
*
* @param string $sKey
* @param array $aOptions
* @param mixed $oData
*
* @return $this
*/
public function AddTextField(string $sKey, array $aOptions, mixed $oData = null) : FormFactory
public function AddTextField(string $sKey, array $aOptions) : FormFactory
{
// test widget for regex constraint
if(array_key_exists('constraints', $aOptions)){
@@ -40,7 +39,6 @@ trait FormFactoryBuilderTrait
}
$this->aFieldsDescriptions[$sKey] = new FormFieldDescription($sKey, FormFieldTypeEnumeration::TEXT, $aOptions);
$this->aFieldsData[$sKey] = $oData;
return $this;
}
@@ -50,16 +48,12 @@ trait FormFactoryBuilderTrait
*
* @param string $sKey
* @param array $aOptions
* @param mixed $oData
*
* @return $this
*/
public function AddNumberField(string $sKey, array $aOptions, mixed $oData = null) : FormFactory
public function AddNumberField(string $sKey, array $aOptions) : FormFactory
{
// $aOptions['property_path'] = $sKey;
$this->aFieldsDescriptions[$sKey] = new FormFieldDescription($sKey, FormFieldTypeEnumeration::NUMBER, $aOptions);
$this->aFieldsData[$sKey] = $oData;
return $this;
}
@@ -69,11 +63,10 @@ trait FormFactoryBuilderTrait
*
* @param string $sKey
* @param array $aOptions
* @param mixed $oData
*
* @return $this
*/
public function AddAreaField(string $sKey, array $aOptions, mixed $oData = null) : FormFactory
public function AddAreaField(string $sKey, array $aOptions) : FormFactory
{
$aOptions = array_merge([
'attr' => [
@@ -83,7 +76,6 @@ trait FormFactoryBuilderTrait
], $aOptions);
$this->aFieldsDescriptions[$sKey] = new FormFieldDescription($sKey, FormFieldTypeEnumeration::AREA, $aOptions);
$this->aFieldsData[$sKey] = $oData;
return $this;
}
@@ -93,14 +85,12 @@ trait FormFactoryBuilderTrait
*
* @param string $sKey
* @param array $aOptions
* @param mixed $oData
*
* @return $this
*/
public function AddDateField(string $sKey, array $aOptions, mixed $oData = null) : FormFactory
public function AddDateField(string $sKey, array $aOptions) : FormFactory
{
$this->aFieldsDescriptions[$sKey] = new FormFieldDescription($sKey, FormFieldTypeEnumeration::DATE, $aOptions);
$this->aFieldsData[$sKey] = $oData;
return $this;
}
@@ -110,14 +100,12 @@ trait FormFactoryBuilderTrait
*
* @param string $sKey
* @param array $aOptions
* @param mixed $oData
*
* @return $this
*/
public function AddDurationField(string $sKey, array $aOptions, mixed $oData = null) : FormFactory
public function AddDurationField(string $sKey, array $aOptions) : FormFactory
{
$this->aFieldsDescriptions[$sKey] = new FormFieldDescription($sKey, FormFieldTypeEnumeration::DURATION, $aOptions);
$this->aFieldsData[$sKey] = $oData;
return $this;
}
@@ -127,14 +115,12 @@ trait FormFactoryBuilderTrait
*
* @param string $sKey
* @param array $aOptions
* @param mixed $oData
*
* @return $this
*/
public function AddSelectField(string $sKey, array $aOptions, mixed $oData = null) : FormFactory
public function AddSelectField(string $sKey, array $aOptions) : FormFactory
{
$this->aFieldsDescriptions[$sKey] = new FormFieldDescription($sKey, FormFieldTypeEnumeration::SELECT, $aOptions);
$this->aFieldsData[$sKey] = $oData;
return $this;
}
@@ -146,11 +132,10 @@ trait FormFactoryBuilderTrait
* @param array $aOptions
* @param array $aAjaxOptions
* @param array $aAjaxData
* @param mixed $oData
*
* @return \Combodo\iTop\FormSDK\Service\FormFactory
*/
public function AddSelectAjaxField(string $sKey, array $aOptions, array $aAjaxOptions, array $aAjaxData = [], mixed $oData = null) : FormFactory
public function AddSelectAjaxField(string $sKey, array $aOptions, array $aAjaxOptions, array $aAjaxData = []) : FormFactory
{
// merge ajax options
$aAjaxOptions = array_merge([
@@ -184,7 +169,7 @@ trait FormFactoryBuilderTrait
// }),
], $aOptions);
return $this->AddSelectField($sKey, $aOptions, $oData);
return $this->AddSelectField($sKey, $aOptions);
}
@@ -198,11 +183,10 @@ trait FormFactoryBuilderTrait
* @param array $aFieldsToLoad
* @param string $sSearch
* @param int $iAjaxThershold
* @param mixed $oData
*
* @return \Combodo\iTop\FormSDK\Service\FormFactory
*/
public function AddSelectOqlField(string $sKey, array $aOptions, string $sObjectClass, string $sOql, array $aFieldsToLoad, string $sSearch, int $iAjaxThershold, mixed $oData = null) : FormFactory
public function AddSelectOqlField(string $sKey, array $aOptions, string $sObjectClass, string $sOql, array $aFieldsToLoad, string $sSearch, int $iAjaxThershold) : FormFactory
{
$aAjaxData = [
'class' => $sObjectClass,
@@ -219,7 +203,7 @@ trait FormFactoryBuilderTrait
'threshold' => $iAjaxThershold,
'configuration' => 'OQL'
];
return $this->AddSelectAjaxField($sKey, $aOptions, $aAjaxOptions, $aAjaxData, $oData);
return $this->AddSelectAjaxField($sKey, $aOptions, $aAjaxOptions, $aAjaxData);
}
/**
@@ -227,18 +211,32 @@ trait FormFactoryBuilderTrait
*
* @param string $sKey
* @param array $aOptions
* @param mixed $oData
*
* @return $this
*/
public function AddSwitchField(string $sKey, array $aOptions, mixed $oData = null) : FormFactory
public function AddSwitchField(string $sKey, array $aOptions) : FormFactory
{
$aOptions = array_merge([
'label_attr' => ['class' => 'checkbox-switch'],
], $aOptions);
$this->aFieldsDescriptions[$sKey] = new FormFieldDescription($sKey, FormFieldTypeEnumeration::SWITCH, $aOptions);
$this->aFieldsData[$sKey] = $oData;
return $this;
}
/**
* Add fieldset.
*
* @param string $sKey
* @param array $aOptions
*
* @return $this
*/
public function AddFieldSet(string $sKey, array $aOptions) : FormFactory
{
$this->aFieldsDescriptions[$sKey] = new FormFieldDescription($sKey, FormFieldTypeEnumeration::FIELDSET, $aOptions);
return $this;
}

View File

@@ -19,9 +19,12 @@
namespace Combodo\iTop\FormSDK\Symfony;
use Combodo\iTop\FormSDK\Symfony\Type\Compound\FormObjectType;
use Combodo\iTop\FormSDK\Field\FormFieldDescription;
use Combodo\iTop\FormSDK\Field\FormFieldTypeEnumeration;
use Combodo\iTop\FormSDK\Symfony\Type\Compound\FieldsetType;
use Combodo\iTop\FormSDK\Symfony\Type\Compound\FormObjectType;
use Combodo\iTop\FormSDK\Symfony\Type\Layout\ColumnType;
use Combodo\iTop\FormSDK\Symfony\Type\Layout\RowType;
use LogAPI;
use Symfony\Component\Form\Extension\Core\Type\CheckboxType;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
@@ -100,7 +103,7 @@ class SymfonyBridge
'options' => $oFormDescription->GetOptions()
];
case FormFieldTypeEnumeration::DB_OBJECT:
case FormFieldTypeEnumeration::FIELDSET:
$aOptions = $oFormDescription->GetOptions();
$aFields = [];
foreach ($aOptions['fields'] as $oChildFormDescription){
@@ -110,7 +113,7 @@ class SymfonyBridge
$aOptions['fields'] = $aFields;
return [
'name' => $oFormDescription->GetName(),
'type' => FormObjectType::class,
'type' => FieldsetType::class,
'options' => $aOptions
];
@@ -137,43 +140,51 @@ class SymfonyBridge
* Create Symfony form.
*
* @param array $aDescriptions
* @param array $aData
* @param mixed $oData
* @param string|null $sName
* @param array|null $aLayout
*
* @return \Symfony\Component\Form\FormInterface
*/
public function CreateForm(array $aDescriptions, array $aData, ?string $sName = null): FormInterface
public function CreateForm(array $aDescriptions, mixed $oData, ?string $sName = null, array $aLayout = []): FormInterface
{
// create Symfony form builder
if($sName !== null){
$oFormBuilder = $this->oFormFactory->createNamedBuilder($sName, FormType::class, $aData);
$oFormBuilder = $this->oFormFactory->createNamedBuilder($sName, FormType::class, $oData);
}
else{
$oFormBuilder = $this->oFormFactory->createBuilder(FormType::class, $aData);
$oFormBuilder = $this->oFormFactory->createBuilder(FormType::class, $oData);
}
// iterate throw descriptions...
foreach ($aDescriptions as $oFormDescription){
// transform fields descriptions...
$aSymfonyTypesDeclaration = [];
foreach ($aDescriptions as $sKey => $oFormDescription){
$aSymfonyTypesDeclaration[$sKey] = $this->ToSymfonyFormType($oFormDescription);
}
// symfony form type description
$aSymfony = $this->ToSymfonyFormType($oFormDescription);
// create layout types
['types' => $aItems] = $this->CreateLayoutTypes($aLayout, $oFormBuilder, $aSymfonyTypesDeclaration);
$oTest = array_merge($aItems, $aSymfonyTypesDeclaration);
// add symfony types to builder...
foreach ($oTest as $oSymfonyTypeDeclaration){
// add type to form
$oFormBuilder->add(
$aSymfony['name'],
$aSymfony['type'],
$aSymfony['options']
$oSymfonyTypeDeclaration['name'],
$oSymfonyTypeDeclaration['type'],
$oSymfonyTypeDeclaration['options']
);
/**
* Allow choices to be loaded client side via ajax.
* without this, field value needs to be part of initial choices that may be empty.
* Need reflexion because, value can be hacked with invalid value without extra validation.
* without this, field value needs to be part of initial choices that may be empty.
* Need reflexion because, value can be hacked with invalid value without validation.
* @see https://symfony.com/doc/current/reference/forms/types/choice.html#choice-loader
* @see https://itecnote.com/tecnote/php-disable-backend-validation-for-choice-field-in-symfony-2-type/
*/
if($aSymfony['type'] === ChoiceType::class){
$oFormBuilder->get($aSymfony['name'])->resetViewTransformers();
*/
if($oSymfonyTypeDeclaration['type'] === ChoiceType::class){
$oFormBuilder->get($oSymfonyTypeDeclaration['name'])->resetViewTransformers();
}
}
@@ -181,4 +192,62 @@ class SymfonyBridge
return $oFormBuilder->getForm();
}
/**
* @param $aLayout
* @param $oFormBuilder
* @param array $aDescriptions
*
* @return array
*/
private function CreateLayoutTypes($aLayout, $oFormBuilder, array &$aDescriptions){
$aResult = [];
$sClasses = '';
foreach ($aLayout as $sKey => $oLayoutElement)
{
if($sKey === 'css_classes'){
$sClasses = $oLayoutElement;
}
else if(str_starts_with($sKey, 'row__')){
$aResult[$sKey] = $this->CreateLayoutContainerType($oLayoutElement, $oFormBuilder, $sKey, RowType::class, $aDescriptions);
}
else if(str_starts_with($sKey, 'column__')){
$aResult[$sKey] = $this->CreateLayoutContainerType($oLayoutElement, $oFormBuilder, $sKey, ColumnType::class, $aDescriptions);
}
else if(str_starts_with($sKey, 'fieldset__')){
$aResult[$sKey] = $this->CreateLayoutContainerType($oLayoutElement, $oFormBuilder, $sKey, FieldsetType::class, $aDescriptions);
}
else {
if (array_key_exists($oLayoutElement, $aDescriptions)) {
$aResult[$oLayoutElement] = $aDescriptions[$oLayoutElement];
unset($aDescriptions[$oLayoutElement]);
}
}
}
return [
'types' => $aResult,
'css_classes' => $sClasses
];
}
private function CreateLayoutContainerType($oLayoutElement, $oFormBuilder, $sKey, $oType, &$aDescriptions)
{
['types' => $aItems, 'css_classes' => $sCssClasses] = $this->CreateLayoutTypes($oLayoutElement, $oFormBuilder, $aDescriptions);
return [
'name' => $sKey,
'type' => $oType,
'options' => [
'fields' => $aItems,
'attr' => [
'class' => $sCssClasses
],
'inherit_data' => true
],
];
}
}

View File

@@ -0,0 +1,72 @@
<?php
/*
* Copyright (C) 2013-2023 Combodo SARL
*
* This file is part of iTop.
*
* iTop is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* iTop is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
*/
namespace Combodo\iTop\FormSDK\Symfony\Type\Compound;
use Combodo\iTop\FormSDK\Field\FormFieldDescription;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\FormType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
/**
* Type representing a fieldset;
*
* @package FormSDK
* @since 3.2.0
*/
class FieldsetType extends AbstractType
{
/** @inheritdoc */
public function buildForm(FormBuilderInterface $builder, array $options) : void
{
foreach ($options['fields'] as $oField){
$builder->add($oField['name'], $oField['type'], $oField['options']);
}
}
private function handleRow(FormBuilderInterface $builder, array $aData){
}
private function handleColumn(FormBuilderInterface $builder, array $aData){
}
/** @inheritdoc */
public function configureOptions(OptionsResolver $resolver): void
{
$resolver->setDefaults([
'fields' => [],
'view' => [],
]);
}
/** @inheritdoc */
public function getParent(): string
{
return FormType::class;
}
}

View File

@@ -34,41 +34,12 @@ use Symfony\Component\OptionsResolver\OptionsResolver;
class FormObjectType extends AbstractType
{
/*
* View Definition.
*
* [
* 'row' => [
* 'col' => [
* 'description' => [
* 'label' => '',
* 'css_classes' => ''
* ],
* 'items' => ['name', 'birthday']
* 'fieldset' => ['address', 'city', 'country']
* ]
* ]
* ]
*
*
*/
/** @inheritdoc */
public function buildForm(FormBuilderInterface $builder, array $options) : void
{
foreach ($options['view'] as $oItem) {
if($oItem === 'row'){
$this->handleRow();
}
else if($oItem === 'col'){
$this->handleColumn();
}
else{
}
}
foreach ($options['fields'] as $oField){
$builder->add($oField['name'], $oField['type'], $oField['options']);
@@ -90,7 +61,7 @@ class FormObjectType extends AbstractType
'fields' => [],
'view' => [],
'attr' => [
'class' => ''
'class' => 'form-object-row'
]
]);
}

View File

@@ -0,0 +1,43 @@
<?php
/*
* Copyright (C) 2013-2023 Combodo SARL
*
* This file is part of iTop.
*
* iTop is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* iTop is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
*/
namespace Combodo\iTop\FormSDK\Symfony\Type\Layout;
use Combodo\iTop\FormSDK\Field\FormFieldDescription;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\FormType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
/**
* Type representing a layout column;
*
* @package FormSDK
* @since 3.2.0
*/
class ColumnType extends AbstractType
{
/** @inheritdoc */
public function getParent(): string
{
return LayoutType::class;
}
}

View File

@@ -0,0 +1,62 @@
<?php
/*
* Copyright (C) 2013-2023 Combodo SARL
*
* This file is part of iTop.
*
* iTop is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* iTop is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
*/
namespace Combodo\iTop\FormSDK\Symfony\Type\Layout;
use Combodo\iTop\FormSDK\Field\FormFieldDescription;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\FormType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
/**
* Type representing an abstract layout type;
*
* @package FormSDK
* @since 3.2.0
*/
class LayoutType extends AbstractType
{
/** @inheritdoc */
public function buildForm(FormBuilderInterface $builder, array $options) : void
{
foreach ($options['fields'] as $oField){
$builder->add($oField['name'], $oField['type'], $oField['options']);
}
}
/** @inheritdoc */
public function configureOptions(OptionsResolver $resolver): void
{
$resolver->setDefaults([
'fields' => [],
'view' => [],
'inherit_data' => true // this type is abstract and used for grouping
]);
}
/** @inheritdoc */
public function getParent(): string
{
return FormType::class;
}
}

View File

@@ -0,0 +1,43 @@
<?php
/*
* Copyright (C) 2013-2023 Combodo SARL
*
* This file is part of iTop.
*
* iTop is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* iTop is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
*/
namespace Combodo\iTop\FormSDK\Symfony\Type\Layout;
use Combodo\iTop\FormSDK\Field\FormFieldDescription;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\FormType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
/**
* Type representing a layout row;
*
* @package FormSDK
* @since 3.2.0
*/
class RowType extends AbstractType
{
/** @inheritdoc */
public function getParent(): string
{
return LayoutType::class;
}
}

View File

@@ -31,8 +31,14 @@
{%- endblock form_label -%}
{%- block form_object_row -%}
{%- set row_attr = row_attr|merge({class: 'mb-3 form-object-row'}) -%}
{{ form_row(form, {'row_attr': row_attr}) }}
{%- endblock form_object_row -%}
{%- block row_row -%}
{{ form_widget(form, {'attr' : {'class' : attr.class|default('') ~ ' form-layout-row'}}) }}
{%- endblock row_row -%}
{%- block column_row -%}
{{ form_widget(form, {'attr' : {'class' : attr.class|default('') ~ ' form-layout-column'}}) }}
{%- endblock column_row -%}
{%- block fieldset_row -%}
{{ form_row(form, {'attr' : {'class' : attr.class|default('') ~ ' form-compound-fieldset'}}) }}
{%- endblock fieldset_row -%}