N°7063 - Forms SDK - Add Symfony forms component

error forms issue
This commit is contained in:
Benjamin Dalsass
2024-01-09 10:24:30 +01:00
parent 0461cd7d51
commit cc367fa19e
14 changed files with 120 additions and 177 deletions

View File

@@ -8,10 +8,10 @@
/* debug purpose */
.form-type-pictograms{
display: inline-block;
float: right;
margin-left: 5px;
color: grey;
cursor: pointer;
background-color: #f8f8f8;
padding: 0px 5px;
border-radius: 8px;
}

View File

@@ -5,36 +5,25 @@ namespace Combodo\iTop\FormImplementation\Controller;
use Combodo\iTop\Controller\AbstractAppController;
use Combodo\iTop\FormImplementation\Dto\ObjectSearchDto;
use Combodo\iTop\FormImplementation\Helper\FormHelper;
use Combodo\iTop\FormImplementation\Helper\SelectDataProvider;
use Combodo\iTop\FormSDK\Service\FormManager;
use Combodo\iTop\Service\Base\ObjectRepository;
use DateTime;
use Exception;
use JsonPage;
use MetaModel;
use Symfony\Component\Form\ChoiceList\Loader\CallbackChoiceLoader;
use Symfony\Component\Form\Extension\Core\Type\DateType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Attribute\MapQueryString;
use Symfony\Component\HttpKernel\Attribute\MapRequestPayload;
use Symfony\Component\Routing\Attribute\Route;
use Symfony\Component\Routing\RouterInterface;
use Symfony\Component\Validator\Constraints\Length;
use Symfony\Component\Validator\Constraints\Regex;
use utils;
class TestController extends AbstractAppController
{
#[Route('/formSDK/test_form', name: 'formSDK_test_form')]
public function form(Request $oRequest, FormManager $oFormManager, RouterInterface $oRouter): Response
#[Route('/formSDK/test_form/{mode}', name: 'formSDK_test_form')]
public function form(Request $oRequest, FormManager $oFormManager, RouterInterface $oRouter, int $mode): Response
{
// create factory
try{
$oFactory = FormHelper::CreateSampleFormFactory($oFormManager, $oRouter);
$oFactory = FormHelper::CreateSampleFormFactory($oFormManager, $oRouter, $mode);
}
catch (Exception $e) {
throw $this->createNotFoundException('unable to create sample form factory', $e);
@@ -52,12 +41,12 @@ class TestController extends AbstractAppController
// retrieve form data
$data = $oForm->getData();
// let's adaptaters save theirs data
// let's adapters save theirs data
foreach($oFactory->GetAllAdapters() as $oAdapter){
$oAdapter->UpdateFieldsData($data);
}
dump($data);
// dump($data);
// return $this->redirectToRoute('app_success');
}

View File

@@ -2,12 +2,11 @@
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\FormFactory;
use Combodo\iTop\FormSDK\Service\FormManager;
use DateInterval;
use DateTime;
use MetaModel;
use Symfony\Component\Routing\RouterInterface;
@@ -16,19 +15,35 @@ use Symfony\Component\Validator\Constraints\Regex;
class FormHelper
{
static private int $PERSON_COUNT = 5;
static private int $PERSON_COUNT = 1;
static private array $MODES_DEFINITIONS = [
0 => [
'group' => false,
'layout' => false
],
1 => [
'group' => true,
'layout' => false
],
2 => [
'group' => true,
'layout' => true
],
];
/**
* Create a sample form factory for demo purpose.
*
* @param \Combodo\iTop\FormSDK\Service\FormManager $oFormManager
* @param \Symfony\Component\Routing\RouterInterface $oRouter
* @param int $iMode
*
* @return \Combodo\iTop\FormSDK\Service\FormFactory
* @throws \ArchivedObjectException
* @throws \CoreException
*/
static public function CreateSampleFormFactory(FormManager $oFormManager, RouterInterface $oRouter)
static public function CreateSampleFormFactory(FormManager $oFormManager, RouterInterface $oRouter, int $iMode) : FormFactory
{
// create a factory
$oFormFactory = $oFormManager->CreateFactory();
@@ -42,7 +57,7 @@ class FormHelper
'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>',
'blog' => '<h2>Your <b>story</b></h2><br>bla bla bla bla bla bla<br>bla bla bla bla bla bla<br>bla bla bla bla bla bla<br>bla bla bla bla bla bla',
'notify' => true,
'language' => 'FR FR',
'mode' => '1',
@@ -57,7 +72,7 @@ class FormHelper
$oPerson = MetaModel::GetObject('Person', $i+1);
// create object adapter
$oObjectPlugin = $oFormFactory->CreateObjectAdapter($oPerson, false);
$oObjectPlugin = $oFormFactory->CreateObjectAdapter($oPerson, self::$MODES_DEFINITIONS[$iMode]['group']);
$oObjectPlugin->AddAttribute('name');
$oObjectPlugin->AddAttribute('mobile_phone');
}
@@ -193,26 +208,37 @@ class FormHelper
]
]);
// layout description
$oFormFactory->SetLayoutDescription([
if(self::$MODES_DEFINITIONS[$iMode]['layout']){
'row__1' => [
'column__1' => [
'css_classes' => 'custom-container container-flower layout-grow',
'fieldset__1' => [ 'birthday', 'city', 'tel'],
],
'column__2' => [
'css_classes' => 'custom-container container-color',
'fieldset__2' => ['mode', 'interval', 'Person_2'],
],
$aDescription = [
],
'row__2' => [
'css_classes' => 'custom-container container-color2',
'fieldset__1' => [ 'Person_1', 'Person_3'],
'fieldset__2' => [ 'Person_1_name'],
]
]);
'row__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__2' => ['mode', 'interval'],
],
],
'row__2' => [
'css_classes' => 'custom-container container-color2 mb-3',
]
];
if(self::$MODES_DEFINITIONS[$iMode]['group']){
$aDescription['row__1']['column__2']['fieldset__2'][] = 'Person_2';
$aDescription['row__2']['fieldset__1'] = [ 'Person_1', 'Person_3'];
}
else{
$aDescription['row__2']['fieldset__1'] = [ 'Person_1_name'];
}
// layout description
$oFormFactory->SetLayoutDescription($aDescription);
}
return $oFormFactory;
}

View File

@@ -36,6 +36,8 @@ class FormFieldDescription
* @param string $sName
* @param FormFieldTypeEnumeration $oType
* @param array $aOptions
*
* @throws \Exception
*/
public function __construct(
private readonly string $sName,

View File

@@ -47,7 +47,7 @@ enum FormFieldTypeEnumeration : string
$aOptions = ['required', 'disabled', 'attr', 'label', 'label_attr', 'help', 'inherit_data'];
// specific options
$test = match ($this) {
return match ($this) {
FormFieldTypeEnumeration::TEXT => array_merge($aOptions,
['constraints']
),
@@ -61,12 +61,10 @@ enum FormFieldTypeEnumeration : string
['input', 'with_minutes', 'with_seconds', 'with_weeks', 'with_days']
),
FormFieldTypeEnumeration::FIELDSET => array_merge($aOptions,
['fields']
['fields', 'layout']
),
default => $aOptions,
};
return $test;
}
/**

View File

@@ -139,20 +139,6 @@ final class FormFactoryObjectAdapter implements FormFactoryAdapterInterface
return $this->bGroup ? $sAttributeCode : $this->GetIdentifier() . '_' . $sAttributeCode;
}
public function GetLayoutDescription()
{
return [
'row__1' => [
'column__1' => [
'fieldset__1' => [ 'name'],
],
'column__2' => [
'fieldset__2' => ['mobile_phone'],
],
],
];
}
/** @inheritdoc */
public function GetFieldsData() : array
{
@@ -177,7 +163,9 @@ final class FormFactoryObjectAdapter implements FormFactoryAdapterInterface
}
}
/** @inheritdoc */
/** @inheritdoc
* @throws \Exception
*/
public function GetFieldsDescriptions() : array
{
$aFieldsDescriptions = [];
@@ -195,6 +183,16 @@ final class FormFactoryObjectAdapter implements FormFactoryAdapterInterface
$oGroupDescriptions = new FormFieldDescription($this->GetIdentifier(), FormFieldTypeEnumeration::FIELDSET, [
'fields' => $aFieldsDescriptions,
'label' => $this->GetLabel(),
'layout' => [
'row__1' => [
'column__1' => [
'fieldset__1' => [ 'name'],
],
'column__2' => [
'fieldset__2' => ['mobile_phone'],
],
],
]
]);
return [$this->GetIdentifier() => $oGroupDescriptions];
}

View File

@@ -19,19 +19,11 @@
namespace Combodo\iTop\FormSDK\Service;
use Combodo\iTop\FormSDK\Service\FormFactoryBuilderTrait;
use Combodo\iTop\FormSDK\Service\FactoryAdapter\FormFactoryObjectAdapter;
use Combodo\iTop\FormSDK\Service\FactoryAdapter\FormFactoryAdapterInterface;
use Combodo\iTop\FormSDK\Symfony\SymfonyBridge;
use Combodo\iTop\FormSDK\Field\FormFieldDescription;
use Combodo\iTop\FormSDK\Field\FormFieldTypeEnumeration;
use DBObject;
use Symfony\Component\Form\ChoiceList\Loader\CallbackChoiceLoader;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
use Symfony\Component\Validator\Constraints\Length;
use Symfony\Component\Validator\Constraints\Regex;
use utils;
/**
* Form factory.
@@ -52,8 +44,8 @@ class FormFactory
/** @var mixed $oFieldsData form data */
private mixed $oFieldsData = [];
/** @var array $aLayoutDecription description of the layout */
private array $aLayoutDecription;
/** @var array $aLayoutDescription description of the layout */
private array $aLayoutDescription = [];
/** builder */
use FormFactoryBuilderTrait;
@@ -65,8 +57,8 @@ class FormFactory
* @param \Symfony\Component\Routing\Generator\UrlGeneratorInterface $oRouter
*/
public function __construct(
private SymfonyBridge $oSymfonyBridge,
private UrlGeneratorInterface $oRouter
private readonly SymfonyBridge $oSymfonyBridge,
private readonly UrlGeneratorInterface $oRouter
)
{
@@ -82,10 +74,14 @@ class FormFactory
// prepare data
$aResult = $this->aFieldsDescriptions;
// merge each adapter data...
foreach ($this->GetAllAdapters() as $oAdapter){
$aResult = array_merge($aResult, $oAdapter->GetFieldsDescriptions());
try{
$aResult = array_merge($aResult, $oAdapter->GetFieldsDescriptions());
}
catch(Exception $e){
ExceptionLog::LogException($e);
}
}
return $aResult;
@@ -98,7 +94,7 @@ class FormFactory
*/
public function GetLayoutDescription() : array
{
return $this->aLayoutDecription;
return $this->aLayoutDescription;
}
/**
@@ -147,13 +143,15 @@ class FormFactory
*
* @return $this
*/
public function SetLayoutDescription(array $aLayoutDescription)
public function SetLayoutDescription(array $aLayoutDescription) : FormFactory
{
$this->aLayoutDecription = $aLayoutDescription;
$this->aLayoutDescription = $aLayoutDescription;
return $this;
}
/**
* Set form data.
*
* @param mixed $oData
*
* @return void
@@ -164,6 +162,8 @@ class FormFactory
}
/***
* Get form data.
*
* @return array
*/
public function GetData() : mixed

View File

@@ -4,8 +4,6 @@ namespace Combodo\iTop\FormSDK\Service;
use Combodo\iTop\FormSDK\Field\FormFieldDescription;
use Combodo\iTop\FormSDK\Field\FormFieldTypeEnumeration;
use Combodo\iTop\FormSDK\Service\FormFactory;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
use Symfony\Component\Validator\Constraints\Regex;
trait FormFactoryBuilderTrait
@@ -17,7 +15,8 @@ trait FormFactoryBuilderTrait
* @param string $sKey
* @param array $aOptions
*
* @return $this
* @return \Combodo\iTop\FormSDK\Service\FormFactory
* @throws \Exception
*/
public function AddTextField(string $sKey, array $aOptions) : FormFactory
{
@@ -49,7 +48,8 @@ trait FormFactoryBuilderTrait
* @param string $sKey
* @param array $aOptions
*
* @return $this
* @return \Combodo\iTop\FormSDK\Service\FormFactory
* @throws \Exception
*/
public function AddNumberField(string $sKey, array $aOptions) : FormFactory
{
@@ -64,7 +64,8 @@ trait FormFactoryBuilderTrait
* @param string $sKey
* @param array $aOptions
*
* @return $this
* @return \Combodo\iTop\FormSDK\Service\FormFactory
* @throws \Exception
*/
public function AddAreaField(string $sKey, array $aOptions) : FormFactory
{
@@ -86,7 +87,8 @@ trait FormFactoryBuilderTrait
* @param string $sKey
* @param array $aOptions
*
* @return $this
* @return \Combodo\iTop\FormSDK\Service\FormFactory
* @throws \Exception
*/
public function AddDateField(string $sKey, array $aOptions) : FormFactory
{
@@ -101,7 +103,8 @@ trait FormFactoryBuilderTrait
* @param string $sKey
* @param array $aOptions
*
* @return $this
* @return \Combodo\iTop\FormSDK\Service\FormFactory
* @throws \Exception
*/
public function AddDurationField(string $sKey, array $aOptions) : FormFactory
{
@@ -116,7 +119,8 @@ trait FormFactoryBuilderTrait
* @param string $sKey
* @param array $aOptions
*
* @return $this
* @return \Combodo\iTop\FormSDK\Service\FormFactory
* @throws \Exception
*/
public function AddSelectField(string $sKey, array $aOptions) : FormFactory
{
@@ -212,7 +216,8 @@ trait FormFactoryBuilderTrait
* @param string $sKey
* @param array $aOptions
*
* @return $this
* @return \Combodo\iTop\FormSDK\Service\FormFactory
* @throws \Exception
*/
public function AddSwitchField(string $sKey, array $aOptions) : FormFactory
{
@@ -232,7 +237,8 @@ trait FormFactoryBuilderTrait
* @param string $sKey
* @param array $aOptions
*
* @return $this
* @return \Combodo\iTop\FormSDK\Service\FormFactory
* @throws \Exception
*/
public function AddFieldSet(string $sKey, array $aOptions) : FormFactory
{

View File

@@ -22,7 +22,6 @@ namespace Combodo\iTop\FormSDK\Symfony;
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;
@@ -36,7 +35,6 @@ use Symfony\Component\Form\Extension\Core\Type\TextareaType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\FormFactoryInterface;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\Validator\Constraints\Length;
/**
* Symfony implementation bridge.
@@ -108,7 +106,7 @@ class SymfonyBridge
$aFields = [];
foreach ($aOptions['fields'] as $oChildFormDescription){
$aSymfony = $this->ToSymfonyFormType($oChildFormDescription);
$aFields[] = $aSymfony;
$aFields[$oChildFormDescription->GetName()] = $aSymfony;
}
$aOptions['fields'] = $aFields;
return [
@@ -162,12 +160,21 @@ class SymfonyBridge
$aSymfonyTypesDeclaration[$sKey] = $this->ToSymfonyFormType($oFormDescription);
}
// create layout types
// prepare fieldset types layout...
foreach ($aSymfonyTypesDeclaration as &$aSymfonyTypeDeclaration){
if($aSymfonyTypeDeclaration['type'] === FieldsetType::class && isset($aSymfonyTypeDeclaration['options']['layout'])){
['types' => $aItems] = $this->CreateLayoutTypes($aSymfonyTypeDeclaration['options']['layout'], $oFormBuilder, $aSymfonyTypeDeclaration['options']['fields']);
$aSymfonyTypeDeclaration['options']['fields'] = array_merge($aItems, $aSymfonyTypeDeclaration['options']['fields']);
$aTest = 'test';
}
}
// prepare general layout types
['types' => $aItems] = $this->CreateLayoutTypes($aLayout, $oFormBuilder, $aSymfonyTypesDeclaration);
$oTest = array_merge($aItems, $aSymfonyTypesDeclaration);
$aSymfonyTypesDeclaration = array_merge($aItems, $aSymfonyTypesDeclaration);
// add symfony types to builder...
foreach ($oTest as $oSymfonyTypeDeclaration){
foreach ($aSymfonyTypesDeclaration as $oSymfonyTypeDeclaration){
// add type to form
$oFormBuilder->add(

View File

@@ -60,6 +60,7 @@ class FieldsetType extends AbstractType
$resolver->setDefaults([
'fields' => [],
'view' => [],
'layout' => []
]);
}

View File

@@ -1,75 +0,0 @@
<?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 an iTop object;
*
* @package FormSDK
* @since 3.2.0
*/
class FormObjectType 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' => [],
'attr' => [
'class' => 'form-object-row'
]
]);
}
/** @inheritdoc */
public function getParent(): string
{
return FormType::class;
}
}

View File

@@ -19,11 +19,7 @@
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;

View File

@@ -48,7 +48,6 @@ class LayoutType extends AbstractType
{
$resolver->setDefaults([
'fields' => [],
'view' => [],
'inherit_data' => true // this type is abstract and used for grouping
]);
}

View File

@@ -19,11 +19,7 @@
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;