mirror of
https://github.com/Combodo/iTop.git
synced 2026-04-24 11:08:45 +02:00
N°7063 - Forms SDK - Add Symfony forms component
error forms issue
This commit is contained in:
@@ -53,6 +53,7 @@ class FormHelper
|
|||||||
* @return \Combodo\iTop\FormSDK\Service\FormFactory
|
* @return \Combodo\iTop\FormSDK\Service\FormFactory
|
||||||
* @throws \ArchivedObjectException
|
* @throws \ArchivedObjectException
|
||||||
* @throws \CoreException
|
* @throws \CoreException
|
||||||
|
* @throws \Exception
|
||||||
*/
|
*/
|
||||||
static public function CreateSampleFormFactory(FormManager $oFormManager, RouterInterface $oRouter, int $iMode) : FormFactory
|
static public function CreateSampleFormFactory(FormManager $oFormManager, RouterInterface $oRouter, int $iMode) : FormFactory
|
||||||
{
|
{
|
||||||
@@ -260,6 +261,12 @@ class FormHelper
|
|||||||
]
|
]
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
// file - file download
|
||||||
|
$oFormFactory->AddFileField('file', [
|
||||||
|
'label' => 'Download a file',
|
||||||
|
'required' => false
|
||||||
|
]);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if(self::$MODES_DEFINITIONS[$iMode]['layout']){
|
if(self::$MODES_DEFINITIONS[$iMode]['layout']){
|
||||||
@@ -273,12 +280,13 @@ class FormHelper
|
|||||||
],
|
],
|
||||||
'column__2' => [
|
'column__2' => [
|
||||||
'css_classes' => 'custom-container container-color mb-3',
|
'css_classes' => 'custom-container container-color mb-3',
|
||||||
'fieldset__2' => ['mode', 'interval'],
|
'fieldset__1' => ['mode', 'interval'],
|
||||||
],
|
],
|
||||||
|
|
||||||
],
|
],
|
||||||
'row__2' => [
|
'row__2' => [
|
||||||
'css_classes' => 'custom-container container-color2 mb-3',
|
'css_classes' => 'custom-container container-color2 mb-3',
|
||||||
|
'fieldset__2' => ['file'],
|
||||||
]
|
]
|
||||||
];
|
];
|
||||||
|
|
||||||
@@ -297,4 +305,95 @@ class FormHelper
|
|||||||
return $oFormFactory;
|
return $oFormFactory;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
private function Definitions()
|
||||||
|
{
|
||||||
|
// condensé // + lisible - organisé // generic id
|
||||||
|
$aDescription = [
|
||||||
|
|
||||||
|
'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__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];
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -36,6 +36,7 @@ enum FormFieldTypeEnumeration
|
|||||||
case DURATION;
|
case DURATION;
|
||||||
case FIELDSET;
|
case FIELDSET;
|
||||||
case COLLECTION;
|
case COLLECTION;
|
||||||
|
case FILE;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return available options.
|
* Return available options.
|
||||||
@@ -47,7 +48,7 @@ enum FormFieldTypeEnumeration
|
|||||||
// global options
|
// global options
|
||||||
$aOptions = ['required', 'disabled', 'attr', 'label', 'label_attr', 'help'];
|
$aOptions = ['required', 'disabled', 'attr', 'label', 'label_attr', 'help'];
|
||||||
|
|
||||||
// specific options
|
// : specific options
|
||||||
return match ($this) {
|
return match ($this) {
|
||||||
FormFieldTypeEnumeration::TEXT => array_merge($aOptions,
|
FormFieldTypeEnumeration::TEXT => array_merge($aOptions,
|
||||||
['constraints']
|
['constraints']
|
||||||
@@ -86,7 +87,7 @@ enum FormFieldTypeEnumeration
|
|||||||
// retrieve available options
|
// retrieve available options
|
||||||
$aAvailableOptions = $this->GetAvailableOptions();
|
$aAvailableOptions = $this->GetAvailableOptions();
|
||||||
|
|
||||||
// check each option...
|
// check each options...
|
||||||
foreach($aOptions as $sKey => $oOption){
|
foreach($aOptions as $sKey => $oOption){
|
||||||
if(!in_array($sKey, $aAvailableOptions)){
|
if(!in_array($sKey, $aAvailableOptions)){
|
||||||
$aInvalidOptions[] = $sKey;
|
$aInvalidOptions[] = $sKey;
|
||||||
|
|||||||
@@ -35,13 +35,13 @@ use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
|
|||||||
*/
|
*/
|
||||||
final class FormFactory
|
final class FormFactory
|
||||||
{
|
{
|
||||||
/** @var array $aFieldsDescriptions form types descriptions */
|
/** @var array $aFieldsDescriptions form fields descriptions */
|
||||||
private array $aFieldsDescriptions = [];
|
private array $aFieldsDescriptions = [];
|
||||||
|
|
||||||
/** @var mixed $oFieldsData form data */
|
/** @var mixed $oFieldsData form fields data */
|
||||||
private mixed $oFieldsData = [];
|
private mixed $oFieldsData = [];
|
||||||
|
|
||||||
/** @var array $aLayoutDescription description of the layout */
|
/** @var array $aLayoutDescription layout description */
|
||||||
private array $aLayoutDescription = [];
|
private array $aLayoutDescription = [];
|
||||||
|
|
||||||
/** @var \Combodo\iTop\FormSDK\Service\FactoryAdapter\FormFactoryAdapterInterface[] $aAdapters list of adapters */
|
/** @var \Combodo\iTop\FormSDK\Service\FactoryAdapter\FormFactoryAdapterInterface[] $aAdapters list of adapters */
|
||||||
@@ -180,8 +180,7 @@ final class FormFactory
|
|||||||
*/
|
*/
|
||||||
public function CreateForm(?string $sName = null) : mixed
|
public function CreateForm(?string $sName = null) : mixed
|
||||||
{
|
{
|
||||||
$aFieldsDescriptions = $this->GetFieldsDescriptions();
|
return $this->oSymfonyBridge->CreateForm($this->GetFieldsDescriptions(), $this->GetData(), $sName, $this->GetLayoutDescription());
|
||||||
return $this->oSymfonyBridge->CreateForm($aFieldsDescriptions, $this->GetData(), $sName, $this->GetLayoutDescription());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -287,4 +287,20 @@ trait FormFactoryBuilderTrait
|
|||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add file field.
|
||||||
|
*
|
||||||
|
* @param string $sKey
|
||||||
|
* @param array $aOptions
|
||||||
|
*
|
||||||
|
* @return \Combodo\iTop\FormSDK\Service\FormFactory
|
||||||
|
* @throws \Exception
|
||||||
|
*/
|
||||||
|
public function AddFileField(string $sKey, array $aOptions) : FormFactory
|
||||||
|
{
|
||||||
|
$this->aFieldsDescriptions[$sKey] = new FormFieldDescription($sKey, FormFieldTypeEnumeration::FILE, $aOptions);
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -30,6 +30,7 @@ use Symfony\Component\Form\Extension\Core\Type\CheckboxType;
|
|||||||
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
|
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
|
||||||
use Symfony\Component\Form\Extension\Core\Type\DateIntervalType;
|
use Symfony\Component\Form\Extension\Core\Type\DateIntervalType;
|
||||||
use Symfony\Component\Form\Extension\Core\Type\DateType;
|
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\FormType;
|
||||||
use Symfony\Component\Form\Extension\Core\Type\NumberType;
|
use Symfony\Component\Form\Extension\Core\Type\NumberType;
|
||||||
use Symfony\Component\Form\Extension\Core\Type\TextareaType;
|
use Symfony\Component\Form\Extension\Core\Type\TextareaType;
|
||||||
@@ -135,6 +136,14 @@ class SymfonyBridge
|
|||||||
'options' => $aOptions
|
'options' => $aOptions
|
||||||
];
|
];
|
||||||
|
|
||||||
|
case FormFieldTypeEnumeration::FILE:
|
||||||
|
$aOptions = $oFormDescription->GetOptions();
|
||||||
|
return [
|
||||||
|
'name' => $oFormDescription->GetName(),
|
||||||
|
'type' => FileType::class,
|
||||||
|
'options' => $aOptions
|
||||||
|
];
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -147,7 +156,8 @@ class SymfonyBridge
|
|||||||
*
|
*
|
||||||
* @return void
|
* @return void
|
||||||
*/
|
*/
|
||||||
private function TransformFieldsetOptions(array &$aOptions){
|
private function TransformFieldsetOptions(array &$aOptions) : void
|
||||||
|
{
|
||||||
|
|
||||||
$aFields = [];
|
$aFields = [];
|
||||||
foreach ($aOptions['fields'] as $oChildFormDescription){
|
foreach ($aOptions['fields'] as $oChildFormDescription){
|
||||||
@@ -164,7 +174,8 @@ class SymfonyBridge
|
|||||||
*
|
*
|
||||||
* @return void
|
* @return void
|
||||||
*/
|
*/
|
||||||
private function TransformCollectionOptions(array &$aOptions){
|
private function TransformCollectionOptions(array &$aOptions) : void
|
||||||
|
{
|
||||||
|
|
||||||
$aOptions['entry_type'] = FieldsetType::class;
|
$aOptions['entry_type'] = FieldsetType::class;
|
||||||
|
|
||||||
|
|||||||
@@ -61,6 +61,6 @@ class CollectionType extends AbstractType
|
|||||||
/** @inheritdoc */
|
/** @inheritdoc */
|
||||||
public function getBlockPrefix()
|
public function getBlockPrefix()
|
||||||
{
|
{
|
||||||
return 'itop_collection';
|
return 'itop_collection'; // need a specific block prefix as we create a CollectionType that's already exist in Symfony namespace
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -37,8 +37,6 @@ class FieldsetType extends AbstractType
|
|||||||
/** @inheritdoc */
|
/** @inheritdoc */
|
||||||
public function buildForm(FormBuilderInterface $builder, array $options) : void
|
public function buildForm(FormBuilderInterface $builder, array $options) : void
|
||||||
{
|
{
|
||||||
|
|
||||||
|
|
||||||
foreach ($options['fields'] as $oField){
|
foreach ($options['fields'] as $oField){
|
||||||
$builder->add($oField['name'], $oField['type'], $oField['options']);
|
$builder->add($oField['name'], $oField['type'], $oField['options']);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ class LayoutType extends AbstractType
|
|||||||
{
|
{
|
||||||
$resolver->setDefaults([
|
$resolver->setDefaults([
|
||||||
'fields' => [],
|
'fields' => [],
|
||||||
'inherit_data' => true // this type is abstract and used for grouping
|
'inherit_data' => true // this type is abstract and used for logical grouping
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -26,6 +26,8 @@
|
|||||||
<span class="ajax-query-type">OQL</span>
|
<span class="ajax-query-type">OQL</span>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
|
<i class="fa-solid fa-fingerprint" title="{{ form.vars.id }}"></i>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user