mirror of
https://github.com/Combodo/iTop.git
synced 2026-04-23 02:28:44 +02:00
N°7063 - Forms SDK - Add Symfony forms component
error forms issue
This commit is contained in:
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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];
|
||||
// }
|
||||
|
||||
}
|
||||
@@ -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'],
|
||||
],
|
||||
]
|
||||
]);
|
||||
|
||||
@@ -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];
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user