N°7063 - Forms SDK - Add Symfony forms component

error forms issue
This commit is contained in:
Benjamin Dalsass
2024-01-12 17:21:19 +01:00
parent e55f1f1d66
commit 7963690a10
5 changed files with 258 additions and 142 deletions

View File

@@ -39,17 +39,6 @@
margin-left: 3px;
}
/* form object row */
.form-object-row{
border: 1px lightgrey dashed;
padding: 8px 12px;
border-radius: 8px;
}
.form-object-row legend{
font-weight: bold;
color: grey;
}
.cke_focus .cke_contents{
border: 1px #86b7fe solid;
outline: 0;
@@ -70,7 +59,6 @@
border: 1px dashed lightgrey;
}
.form-layout-row{
display: flex;
flex-direction: row;

View File

@@ -8,7 +8,6 @@ use Combodo\iTop\FormImplementation\Dto\ObjectSearchDto;
use Combodo\iTop\FormImplementation\Helper\FormHelper;
use Combodo\iTop\FormSDK\Service\FormManager;
use Combodo\iTop\Service\Base\ObjectRepository;
use Exception;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;

View File

@@ -276,31 +276,54 @@ class FormHelper
$aDescription = [
'row__1' => [
'column__1' => [
'__css_classes' => 'custom-container container-flower layout-grow',
'fieldset__1' => ['__label' => 'Informations Utilisateur' , 'birthday', 'city', 'tel'],
':row_1' => [
'@rank' => 2,
':column_1' => [
'@css_classes' => ['custom-container', 'container-flower', 'layout-grow'],
'@rank' => 2,
':fieldset_1' => [
'@label' => 'Informations Utilisateur',
'@rank' => 2,
'birthday' => [
'@rank' => 3,
],
'city' => [
'@rank' => 0,
],
'tel' => [
'@rank' => 1,
],
],
],
'column__2' => [
'__css_classes' => 'custom-container container-color mb-3',
'fieldset__1' => ['mode', 'interval'],
':column_2' => [
'@css_classes' => ['custom-container', 'container-color', 'mb-3'],
'@rank' => 1,
':fieldset_1' => [
'@label' => 'Options',
'@rank' => 1,
'mode',
'interval'
],
],
],
'row__2' => [
'__css_classes' => 'custom-container container-color2 mb-3',
'column__2' => [
'fieldset__1' => ['file'],
':row_2' => [
'@rank' => 1,
'@css_classes' => ['custom-container', 'container-color2', 'mb-3'],
':column_2' => [
':fieldset_1' => [
'file'
],
],
]
];
if(self::$MODES_DEFINITIONS[$iMode]['group']){
$aDescription['row__1']['column__2']['fieldset__2'][] = 'Person_2';
$aDescription['row__2']['column__1']['fieldset__1'] = [ 'Person_1', 'Person_3'];
$aDescription[':row_1'][':column_2'][':fieldset_2'][] = 'Person_2';
$aDescription[':row_2'][':column_1'][':fieldset_1'] = [ 'Person_1', 'Person_3'];
}
else{
$aDescription['row__2']['column__1']['fieldset__1'] = [ 'Person_1_name'];
$aDescription[':row_2'][':column_1'][':fieldset_1'] = [ 'Person_1_name'];
}
// layout description
@@ -312,92 +335,136 @@ class FormHelper
private function Definitions()
{
// condensé // + lisible - organisé // generic id
$aDescription = [
'row__1' => [
'column__1' => [
'__css_classes' => 'custom-container container-flower layout-grow',
'__label' => 'custom-container container-flower layout-grow',
'fieldset__1' => [ 'birthday', 'city', 'tel'],
],
'column__2' => [
'css_classes' => 'custom-container container-color mb-3',
'fieldset__1' => ['mode', 'interval'],
],
],
'row__2' => [
'css_classes' => 'custom-container container-color2 mb-3',
'fieldset__2' => ['file'],
]
];
// etendu // - lisible + organisé // free id
$aDescription2 = [
'1' => [
'properties' => [
'type' => 'row',
'css_classes' => 'custom-container container-flower layout-grow',
'rank' => 1
],
'content' => [
'11' => [
'properties' => [
'type' => 'column',
'rank' => 1
],
'content' => [
'111' => [
'properties' => [
'type' => 'fieldset',
'rank' => 1
],
],
'112' => [
'properties' => [
'type' => 'fieldset',
'rank' => 1
],
]
]
]
],
'column__1' => [
'css_classes' => 'custom-container container-flower layout-grow',
'fieldset__1' => [ 'birthday', 'city', 'tel'],
],
'column__2' => [
'css_classes' => 'custom-container container-color mb-3',
'fieldset__1' => ['mode', 'interval'],
],
],
'row__2' => [
'css_classes' => 'custom-container container-color2 mb-3',
'fieldset__2' => ['file'],
]
];
return [$aDescription, $aDescription2];
}
// private function Definitions()
// {
// // condensé // + lisible - organisé // generic id
// $aDescription = [
//
// 'row__1' => [
// 'column__1' => [
// '@label' => 'Informations',
// '@css_classes' => 'custom-container container-flower layout-grow',
// 'fieldset__1' => [ 'birthday', 'city', 'tel'],
// ],
// 'column__2' => [
// '@label' => 'Options',
// '@css_classes' => 'custom-container container-color mb-3',
// 'fieldset__1' => ['mode', 'interval'],
// ],
//
// ],
// 'row__2' => [
// '@label' => 'Telechargement',
// '@css_classes' => 'custom-container container-color2 mb-3',
// 'fieldset__2' => ['file'],
// ]
// ];
//
// // condensé // + lisible - organisé // generic id
// $aDescription2 = [
//
// '+row__#1' => [
// '+column__#1' => [
// '@label' => 'Informations',
// '@css_classes' => ['custom-container', 'container-flower', 'layout-grow'],
// '+fieldset__#1' => [ 'birthday', 'city', 'tel'],
// ],
// '+column__#2' => [
// '@label' => 'Options',
// '@css_classes' => ['custom-container', 'container-color', 'mb-3'],
// '+fieldset__#1' => ['mode', 'interval'],
// ],
//
// ],
// '+row__2' => [
// '@label' => 'Telechargement',
// '@css_classes' => ['custom-container', 'container-color2', 'mb-3'],
// '+fieldset__#2' => ['file'],
// ]
// ];
//
// // etendu // - lisible + organisé // free id
// $aDescription3 = [
//
// '1' => [
//
// 'properties' => [
// 'type' => 'row',
// 'css_classes' => 'custom-container container-flower layout-grow',
// 'rank' => 1
// ],
//
// 'content' => [
//
// '11' => [
//
// 'properties' => [
// 'label' => 'Informations',
// 'type' => 'column',
// 'rank' => 1
// ],
//
// 'content' => [
//
// '111' => [
//
// 'properties' => [
// 'type' => 'fieldset',
// 'rank' => 1
// ],
//
// 'content' => [ 'birthday', 'city', 'tel']
// ],
// ],
//
// '22' => [
//
// 'properties' => [
// 'label' => 'Informations',
// 'type' => 'column',
// 'rank' => 1
// ],
//
// 'content' => [
//
// '222' => [
//
// 'properties' => [
// 'type' => 'fieldset',
// 'rank' => 2
// ],
//
// ]
//
// ]
// ]
//
// ],
//
// ],
// '2' => [
//
// 'properties' => [
// 'type' => 'row',
// 'css_classes' => 'custom-container container-color2 mb-3',
// 'rank' => 2
// ],
//
// 'content' => [
//
// 'properties' => [
// 'type' => 'column',
// 'rank' => 1
// ],
//
// '111' => [
//
// 'css_classes' => 'custom-container container-color2 mb-3',
// 'fieldset__2' => ['file'],
// ]
// ];
//
// return [$aDescription, $aDescription2];
// }
}

View File

@@ -183,9 +183,9 @@ final class FormFactoryObjectAdapter implements FormFactoryAdapterInterface
$oGroupDescriptions = new FormFieldDescription($this->GetIdentifier(), FormFieldTypeEnumeration::FIELDSET, [
'fields' => $aFieldsDescriptions,
'layout' => [
'row__1' => [
'column__1' => [ 'name'],
'column__2' => ['mobile_phone'],
':row_1' => [
':column_1' => ['name'],
':column_2' => ['mobile_phone'],
],
]
]);

View File

@@ -31,6 +31,7 @@ use Symfony\Component\Form\Extension\Core\Type\DateIntervalType;
use Symfony\Component\Form\Extension\Core\Type\DateType;
use Symfony\Component\Form\Extension\Core\Type\FileType;
use Symfony\Component\Form\Extension\Core\Type\FormType;
use Symfony\Component\Form\Extension\Core\Type\IntegerType;
use Symfony\Component\Form\Extension\Core\Type\NumberType;
use Symfony\Component\Form\Extension\Core\Type\TextareaType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
@@ -89,7 +90,7 @@ class SymfonyBridge
FormFieldTypeEnumeration::SELECT => ChoiceType::class,
FormFieldTypeEnumeration::SWITCH => CheckboxType::class,
FormFieldTypeEnumeration::FIELDSET => FieldsetType::class,
FormFieldTypeEnumeration::NUMBER => NumberType::class,
FormFieldTypeEnumeration::NUMBER => IntegerType::class,
FormFieldTypeEnumeration::DURATION => DateIntervalType::class,
FormFieldTypeEnumeration::COLLECTION => CollectionType::class,
FormFieldTypeEnumeration::FILE => FileType::class,
@@ -124,6 +125,7 @@ class SymfonyBridge
private function TransformDurationOptions(array $aOptions) : array
{
$aOptions['input'] = 'array';
// $aOptions['widget'] = 'integer';
return $aOptions;
}
@@ -139,8 +141,8 @@ class SymfonyBridge
{
$aOptions['types_declarations'] = [];
foreach ($aOptions['fields'] as $oChildFormDescription){
$aSymfony = $this->ToSymfonyFormType($oChildFormDescription);
$aOptions['types_declarations'][$oChildFormDescription->GetName()] = $aSymfony;
$aSymfonyTypeDeclaration = $this->ToSymfonyFormType($oChildFormDescription);
$aOptions['types_declarations'][$oChildFormDescription->GetName()] = $aSymfonyTypeDeclaration;
}
unset($aOptions['fields']);
@@ -179,6 +181,7 @@ class SymfonyBridge
* @param array $aLayout
*
* @return \Symfony\Component\Form\FormInterface
* @throws \Exception
*/
public function CreateForm(array $aFieldsDescriptions, mixed $oData, ?string $sName = null, array $aLayout = []): FormInterface
{
@@ -199,7 +202,7 @@ class SymfonyBridge
// handle fieldset types layouts...
foreach ($aSymfonyTypesDeclaration as &$aSymfonyTypeDeclaration){
if($aSymfonyTypeDeclaration['type'] === FieldsetType::class
&& isset($aSymfonyTypeDeclaration['options']['layout'])){
&& isset($aSymfonyTypeDeclaration['options']['layout'])){
['types_declarations' => $aLayoutSymfonyTypesDeclarations] = $this->CreateLayoutTypes($aSymfonyTypeDeclaration['options']['layout'], $oFormBuilder, $aSymfonyTypeDeclaration['options']['types_declarations']);
$aSymfonyTypeDeclaration['options']['types_declarations'] = array_merge($aLayoutSymfonyTypesDeclarations, $aSymfonyTypeDeclaration['options']['types_declarations']);
}
@@ -244,44 +247,102 @@ class SymfonyBridge
* @param array $aDescriptions array of descriptions
*
* @return array created layout types
* @throws \Exception
*/
private function CreateLayoutTypes(array $aLayout, FormBuilderInterface $oFormBuilder, array &$aDescriptions) : array
{
// variables
$aResult = [];
$sClasses = '';
$aCssClasses = [];
$sLabel = null;
$iRank = -1;
$aRanks = [];
// scan layout...
// scan layout hierarchy...
foreach ($aLayout as $sKey => $oLayoutElement)
{
if($sKey === '__css_classes'){
$sClasses = $oLayoutElement;
// properties
if(str_starts_with($sKey, '@')){
if($sKey === '@css_classes'){
$aCssClasses = $oLayoutElement;
}
else if($sKey === '@label'){
$sLabel = $oLayoutElement;
}
else if($sKey === '@rank'){
$iRank = intval($oLayoutElement);
}
}
else if($sKey === '__label'){
$sLabel = $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);
// layout elements
else if(str_starts_with($sKey, ':')){
// layout type
if(str_starts_with($sKey, ':row_')){
$sType = RowType::class;
}
else if(str_starts_with($sKey, ':column_')){
$sType = ColumnType::class;
}
else if(str_starts_with($sKey, ':fieldset_')){
$sType = FieldsetType::class;
}
else{
throw new Exception('invalid layout type');
}
// create layout type
['type' => $aType, 'rank' => $iTypeRank] = $this->CreateLayoutContainerType($oLayoutElement, $oFormBuilder, substr($sKey, 1), $sType, $aDescriptions);
$aRanks[$sKey] = $iTypeRank;
$aResult[$sKey] = $aType;
}
// fields
else {
if (array_key_exists($oLayoutElement, $aDescriptions)) {
// field with information
if (array_key_exists($sKey, $aDescriptions)) {
$aRanks[$sKey] = -1;
if(isset($oLayoutElement['@rank'])){
$aRanks[$sKey] = intval($oLayoutElement['@rank']);
}
// create field type
$aResult[$sKey] = $aDescriptions[$sKey];
// remove description
unset($aDescriptions[$sKey]);
}
// only field
else if (array_key_exists($oLayoutElement, $aDescriptions)) {
$aRanks[$oLayoutElement] = -1;
// create field type
$aResult[$oLayoutElement] = $aDescriptions[$oLayoutElement];
// remove description
unset($aDescriptions[$oLayoutElement]);
}
}
}
// order fields by rank
uksort($aResult, function($a, $b) use($aRanks){
return ( $aRanks[$a] > $aRanks[$b] ) ? 1 : -1;
});
return [
'types_declarations' => $aResult,
'label' => $sLabel,
'css_classes' => $sClasses
'css_classes' => $aCssClasses,
'rank' => $iRank
];
}
@@ -298,20 +359,21 @@ class SymfonyBridge
*/
private function CreateLayoutContainerType(array $aLayout, FormBuilderInterface $oFormBuilder, string $sKey, string $sTypeClassName, array &$aDescriptions) : array
{
['types_declarations' => $aTypesDeclarations, 'label' => $sLabel, 'css_classes' => $sCssClasses] = $this->CreateLayoutTypes($aLayout, $oFormBuilder, $aDescriptions);
['types_declarations' => $aTypesDeclarations,'label' => $sLabel, 'css_classes' => $aCssClasses, 'rank' => $iRank] = $this->CreateLayoutTypes($aLayout, $oFormBuilder, $aDescriptions);
return [
return ['type' => [
'name' => $sKey,
'type' => $sTypeClassName,
'options' => [
'label' => $sLabel !== null ? $sLabel : false,
'types_declarations' => $aTypesDeclarations,
'attr' => [
'class' => $sCssClasses
'class' => implode(' ', $aCssClasses)
],
'inherit_data' => true
],
];
],
'rank' => $iRank];
}
}