N°7063 - Forms SDK - Add Symfony forms component

error forms issue
This commit is contained in:
Benjamin Dalsass
2023-12-27 16:53:39 +01:00
parent d6970f6486
commit 2bcc4d9989
25 changed files with 450 additions and 370 deletions

View File

@@ -1,7 +1,11 @@
html{
font-family: "Montserrat";
}
/**
* Widgets Factory.
*
* @package FormSDK
* @since 3.2.0
*/
/* debug purpose */
.form-type-pictograms{
display: inline-block;
margin-left: 5px;
@@ -11,56 +15,31 @@ html{
padding: 0px 5px;
border-radius: 8px;
}
.form-type-pictograms i{
width: 15px;
}
.form-object legend{
font-weight: bold;
color: grey;
}
.form-object{
border: 1px lightgrey dashed;
padding: 8px 12px;
border-radius: 8px;
}
.complete .pattern:after{
.complete .form-type-pictograms .pattern:after{
content: '\f00c';
font-family: "Font Awesome 6 Free";
font-weight: 900;
background-color: #20b220;
border-radius: 50%;
font-size: 9px;
font-size: 7px;
color: white;
position: relative;
top: -10px;
padding: 2px;
}
.ajax-query-type{
font-size: .8rem;
/* 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;
}
.theme_viewer{
display: flex;
}
.theme{
width: 100%;
padding: 20px;
}
.theme1 h4{
color: #cc8d17;
border-bottom: 1px #cc8d17 solid;
margin-bottom: 20px;
}
.theme2 h4{
color: #c7306e;
border-bottom: 1px #c7306e solid;
margin-bottom: 20px;
}

View File

@@ -1,4 +1,9 @@
/**
* Widgets Factory.
*
* @package FormSDK
* @since 3.2.0
*/
const iTopFormWidgetFactory = new function(){
/**
@@ -6,42 +11,63 @@ const iTopFormWidgetFactory = new function(){
*
* @constructor
*/
const AutoInstall = function()
{
const AutoInstall = function() {
console.log('AutoInstall');
// SELECT widget implementation
$('[data-widget="SelectWidget"]').each(function(e){
const oElement = $(this);
if(!oElement.data('widget-state-initialized')){
const sId = oElement.attr('id');
const aOptions = oElement.data('widget-options');
iTopFormWidgetFactory.CreateSelectWidget(sId, aOptions);
oElement.data('widget-state-initialized', true);
}
});
// widgets catalog
const WIDGETS = {
SELECT: {name: 'SelectWidget', builder: CreateSelectWidget},
TEXT: {name: 'TextWidget', builder: CreateTextWidget},
AREA: {name: 'AreaWidget', builder: CreateAreaWidget}
}
$('[data-widget="TextWidget"]').each(function(e){
const oElement = $(this);
if(!oElement.data('widget-state-initialized')){
const sId = oElement.attr('id');
const aOptions = oElement.data('widget-options');
iTopFormWidgetFactory.CreateTextWidget(sId, aOptions);
oElement.data('widget-state-initialized', true);
}
});
// instanciate widgets...
for (const widgetsKey in WIDGETS) {
// widget configuration
const aWidgetConfiguration = WIDGETS[widgetsKey];
// instanciate widget
$(`[data-widget="${aWidgetConfiguration.name}"]`).each(function (e) {
const oElement = $(this);
if (!oElement.data('widget-state-initialized')) {
const sId = oElement.attr('id');
const aOptions = oElement.data('widget-options');
aWidgetConfiguration.builder(sId, aOptions);
console.log(aWidgetConfiguration.name, sId, aOptions);
oElement.data('widget-state-initialized', true);
}
});
}
$('[data-widget="AreaWidget"]').each(function(e){
const oElement = $(this);
if(!oElement.data('widget-state-initialized')){
const sId = oElement.attr('id');
const aOptions = oElement.data('widget-options');
iTopFormWidgetFactory.CreateAreaWidget(sId, aOptions);
oElement.data('widget-state-initialized', true);
}
});
}
/**
* CreateTextWidget.
*
* @param {string} sId
* @param {array} aOptions
* @constructor
*/
const CreateTextWidget = function(sId, aOptions){
if(aOptions['pattern'] !== undefined){
const oElement = $(`#${sId}`);
const oMask = IMask(oElement[0],
{
mask: aOptions['pattern'],
lazy: false,
}
);
oMask.on('accept', () => {
masked = oMask.masked;
oElement.closest('div').toggleClass('complete', masked.isComplete)
});
}
};
/**
* CreateAreaWidget.
*
@@ -51,9 +77,6 @@ const iTopFormWidgetFactory = new function(){
*/
const CreateAreaWidget = function(sId, aOptions){
console.log('CreateAreaWidget', sId, aOptions);
editor = CKEDITOR.replace(sId, {
language: 'fr',
stylesSet: 'my_styles',
@@ -72,34 +95,6 @@ const iTopFormWidgetFactory = new function(){
};
/**
* CreateTextWidget.
*
* @param {string} sId
* @param {array} aOptions
* @constructor
*/
const CreateTextWidget = function(sId, aOptions){
console.log('CreateTextWidget', sId, aOptions);
if(aOptions['pattern'] !== undefined){
const oElement = $(`#${sId}`);
const oMask = IMask(oElement[0],
{
mask: aOptions['pattern'],
lazy: false,
}
);
oMask.on('accept', () => {
masked = oMask.masked;
oElement.closest('div').toggleClass('complete', masked.isComplete)
});
}
};
/**
* CreateSelectAjaxWidget.
*
@@ -107,10 +102,8 @@ const iTopFormWidgetFactory = new function(){
* @param {array} aOptions
* @constructor
*/
const CreateSelectWidget = function(sId, aOptions){
console.log('CreateSelectAjaxWidget', sId, aOptions);
const CreateSelectWidget = function(sId, aOptions)
{
const aPlugins = {};
if(aOptions['max_items'] != '1'){
@@ -144,10 +137,7 @@ const iTopFormWidgetFactory = new function(){
};
return {
AutoInstall,
CreateTextWidget,
CreateAreaWidget,
CreateSelectWidget,
AutoInstall
}
};

View File

@@ -399,16 +399,17 @@ return array(
'Combodo\\iTop\\Core\\MetaModel\\HierarchicalKey' => $baseDir . '/sources/Core/MetaModel/HierarchicalKey.php',
'Combodo\\iTop\\DesignDocument' => $baseDir . '/core/designdocument.class.inc.php',
'Combodo\\iTop\\DesignElement' => $baseDir . '/core/designdocument.class.inc.php',
'Combodo\\iTop\\FormSDK\\Controller\\TestController' => $baseDir . '/sources/FormSDK/Controller/TestController.php',
'Combodo\\iTop\\FormSDK\\Dto\\ObjectSearchDto' => $baseDir . '/sources/FormSDK/Dto/ObjectSearchDto.php',
'Combodo\\iTop\\FormSDK\\Field\\Description\\FormFieldDescription' => $baseDir . '/sources/FormSDK/Field/Description/FormFieldDescription.php',
'Combodo\\iTop\\FormSDK\\Field\\Description\\FormFieldTypeEnumeration' => $baseDir . '/sources/FormSDK/Field/Description/FormFieldTypeEnumeration.php',
'Combodo\\iTop\\FormSDK\\Helper\\FormHelper' => $baseDir . '/sources/FormSDK/Helper/FormHelper.php',
'Combodo\\iTop\\FormSDK\\Helper\\SelectHelper' => $baseDir . '/sources/FormSDK/Helper/SelectDataProvider.php',
'Combodo\\iTop\\FormSDK\\Helper\\TwigHelper' => $baseDir . '/sources/FormSDK/Helper/TwigHelper.php',
'Combodo\\iTop\\FormSDK\\Service\\FactoryAddon\\FormFactoryAddonInterface' => $baseDir . '/sources/FormSDK/Service/FactoryAdapter/FormFactoryAdapterInterface.php',
'Combodo\\iTop\\FormSDK\\Service\\FactoryAddon\\FormFactoryObjectAddon' => $baseDir . '/sources/FormSDK/Service/FactoryAdapter/FormFactoryObjectAdapter.php',
'Combodo\\iTop\\FormImplementation\\Controller\\TestController' => $baseDir . '/sources/FormImplementation/Controller/TestController.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',
'Combodo\\iTop\\FormImplementation\\Helper\\TwigHelper' => $baseDir . '/sources/FormImplementation/Helper/TwigHelper.php',
'Combodo\\iTop\\FormSDK\\Field\\FormFieldDescription' => $baseDir . '/sources/FormSDK/Field/FormFieldDescription.php',
'Combodo\\iTop\\FormSDK\\Field\\FormFieldTypeEnumeration' => $baseDir . '/sources/FormSDK/Field/FormFieldTypeEnumeration.php',
'Combodo\\iTop\\FormSDK\\Service\\FactoryAdapter\\FormFactoryAdapterInterface' => $baseDir . '/sources/FormSDK/Service/FactoryAdapter/FormFactoryAdapterInterface.php',
'Combodo\\iTop\\FormSDK\\Service\\FactoryAdapter\\FormFactoryObjectAdapter' => $baseDir . '/sources/FormSDK/Service/FactoryAdapter/FormFactoryObjectAdapter.php',
'Combodo\\iTop\\FormSDK\\Service\\FormFactory' => $baseDir . '/sources/FormSDK/Service/FormFactory.php',
'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\\FormObjectType' => $baseDir . '/sources/FormSDK/Symfony/Type/Compound/FormObjectType.php',

View File

@@ -808,16 +808,17 @@ class ComposerStaticInit7f81b4a2a468a061c306af5e447a9a9f
'Combodo\\iTop\\Core\\MetaModel\\HierarchicalKey' => __DIR__ . '/../..' . '/sources/Core/MetaModel/HierarchicalKey.php',
'Combodo\\iTop\\DesignDocument' => __DIR__ . '/../..' . '/core/designdocument.class.inc.php',
'Combodo\\iTop\\DesignElement' => __DIR__ . '/../..' . '/core/designdocument.class.inc.php',
'Combodo\\iTop\\FormSDK\\Controller\\TestController' => __DIR__ . '/../..' . '/sources/FormSDK/Controller/TestController.php',
'Combodo\\iTop\\FormSDK\\Dto\\ObjectSearchDto' => __DIR__ . '/../..' . '/sources/FormSDK/Dto/ObjectSearchDto.php',
'Combodo\\iTop\\FormSDK\\Field\\Description\\FormFieldDescription' => __DIR__ . '/../..' . '/sources/FormSDK/Field/Description/FormFieldDescription.php',
'Combodo\\iTop\\FormSDK\\Field\\Description\\FormFieldTypeEnumeration' => __DIR__ . '/../..' . '/sources/FormSDK/Field/Description/FormFieldTypeEnumeration.php',
'Combodo\\iTop\\FormSDK\\Helper\\FormHelper' => __DIR__ . '/../..' . '/sources/FormSDK/Helper/FormHelper.php',
'Combodo\\iTop\\FormSDK\\Helper\\SelectHelper' => __DIR__ . '/../..' . '/sources/FormSDK/Helper/SelectDataProvider.php',
'Combodo\\iTop\\FormSDK\\Helper\\TwigHelper' => __DIR__ . '/../..' . '/sources/FormSDK/Helper/TwigHelper.php',
'Combodo\\iTop\\FormSDK\\Service\\FactoryAddon\\FormFactoryAddonInterface' => __DIR__ . '/../..' . '/sources/FormSDK/Service/FactoryAdapter/FormFactoryAdapterInterface.php',
'Combodo\\iTop\\FormSDK\\Service\\FactoryAddon\\FormFactoryObjectAddon' => __DIR__ . '/../..' . '/sources/FormSDK/Service/FactoryAdapter/FormFactoryObjectAdapter.php',
'Combodo\\iTop\\FormImplementation\\Controller\\TestController' => __DIR__ . '/../..' . '/sources/FormImplementation/Controller/TestController.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',
'Combodo\\iTop\\FormImplementation\\Helper\\TwigHelper' => __DIR__ . '/../..' . '/sources/FormImplementation/Helper/TwigHelper.php',
'Combodo\\iTop\\FormSDK\\Field\\FormFieldDescription' => __DIR__ . '/../..' . '/sources/FormSDK/Field/FormFieldDescription.php',
'Combodo\\iTop\\FormSDK\\Field\\FormFieldTypeEnumeration' => __DIR__ . '/../..' . '/sources/FormSDK/Field/FormFieldTypeEnumeration.php',
'Combodo\\iTop\\FormSDK\\Service\\FactoryAdapter\\FormFactoryAdapterInterface' => __DIR__ . '/../..' . '/sources/FormSDK/Service/FactoryAdapter/FormFactoryAdapterInterface.php',
'Combodo\\iTop\\FormSDK\\Service\\FactoryAdapter\\FormFactoryObjectAdapter' => __DIR__ . '/../..' . '/sources/FormSDK/Service/FactoryAdapter/FormFactoryObjectAdapter.php',
'Combodo\\iTop\\FormSDK\\Service\\FormFactory' => __DIR__ . '/../..' . '/sources/FormSDK/Service/FormFactory.php',
'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\\FormObjectType' => __DIR__ . '/../..' . '/sources/FormSDK/Symfony/Type/Compound/FormObjectType.php',

View File

@@ -3,7 +3,7 @@
'name' => 'combodo/itop',
'pretty_version' => 'dev-develop',
'version' => 'dev-develop',
'reference' => '47aeb0cbd303fc27c06bae27b6447b1bef4d8e35',
'reference' => '20949605c30bc7a83d0b554f4af23b6cf802c292',
'type' => 'project',
'install_path' => __DIR__ . '/../../',
'aliases' => array(),
@@ -22,7 +22,7 @@
'combodo/itop' => array(
'pretty_version' => 'dev-develop',
'version' => 'dev-develop',
'reference' => '47aeb0cbd303fc27c06bae27b6447b1bef4d8e35',
'reference' => '20949605c30bc7a83d0b554f4af23b6cf802c292',
'type' => 'project',
'install_path' => __DIR__ . '/../../',
'aliases' => array(),

View File

@@ -1,11 +1,11 @@
<?php
namespace Combodo\iTop\FormSDK\Controller;
namespace Combodo\iTop\FormImplementation\Controller;
use Combodo\iTop\Controller\AbstractAppController;
use Combodo\iTop\FormSDK\Dto\ObjectSearchDto;
use Combodo\iTop\FormSDK\Helper\FormHelper;
use Combodo\iTop\FormSDK\Helper\SelectDataProvider;
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;
@@ -29,7 +29,7 @@ use utils;
class TestController extends AbstractAppController
{
#[Route('/formSDK/test_Form', name: 'formSDK_test_form')]
#[Route('/formSDK/test_form', name: 'formSDK_test_form')]
public function form(Request $oRequest, FormManager $oFormManager, RouterInterface $oRouter): Response
{
// create factory
@@ -82,7 +82,7 @@ class TestController extends AbstractAppController
$oForm2->handleRequest($oRequest);
return $this->render('formSDK/theme.html.twig', [
'name1' => 'Portal',
'name1' => 'Portail',
'name2' => 'Console',
'form1' => $oForm1->createView(),
'form2' => $oForm2->createView(),

View File

@@ -1,6 +1,6 @@
<?php
namespace Combodo\iTop\FormSDK\Dto;
namespace Combodo\iTop\FormImplementation\Dto;
use Symfony\Component\Validator\Constraints as Assert;

View File

@@ -1,6 +1,6 @@
<?php
namespace Combodo\iTop\FormSDK\Helper;
namespace Combodo\iTop\FormImplementation\Helper;
use Combodo\iTop\Controller\AbstractAppController;
use Combodo\iTop\FormSDK\Service\FormManager;
@@ -24,10 +24,10 @@ class FormHelper
*/
static public function CreateSampleFormFactory(FormManager $oFormManager, RouterInterface $oRouter)
{
// build the form
// create a factory
$oFormFactory = $oFormManager->CreateFactory();
// add 2 person forms...
// add X person forms...
for($i = 0 ; $i < 2 ; $i++){
// retrieve person
@@ -61,6 +61,11 @@ class FormHelper
'required' => false
], new DateTime('1979/06/27'));
// ready
$oFormFactory->AddSwitchField('notify', [
'label' => 'Veuillez m\'avertir en cas de changement',
], true);
// blog - date
$oFormFactory->AddAreaField('blog', [
'label' => 'Blog',
@@ -81,7 +86,7 @@ class FormHelper
], [
'url' => 'http://localhost' . $oRouter->generate('formSDK_ajax_select'),
'ajax_query_parameter' => 'query',
'query_parameter' => 'query',
'value_field' => 'breed',
'label_field' => 'breed',
'search_field' => 'breed',
@@ -95,21 +100,33 @@ class FormHelper
'required' => false
], 'Person', 'SELECT Person', [], '', 20);
// requests - select with OQL
$oFormFactory->AddSelectOqlField('requests', [
'label' => 'Tickets',
'required' => false
], 'UserRequest', 'SELECT UserRequest', [], '', 20);
// mode - select with static data
$oFormFactory->AddSelectField('mode', [
'label' => 'Mon mode',
'choices' => SelectDataProvider::GetModes(),
'expanded' => true,
'multiple' => false
], '');
'multiple' => false,
'label_attr' => [
'class' => 'radio-inline'
]
], '1');
// options - select with static data
$oFormFactory->AddSelectField('option', [
'label' => 'Mes options',
'choices' => SelectDataProvider::GetOptions(),
'expanded' => true,
'multiple' => true
], null);
'multiple' => true,
'label_attr' => [
'class' => 'checkbox-inline'
]
], ['0', '2','4']);
return $oFormFactory;
}

View File

@@ -17,7 +17,7 @@
* You should have received a copy of the GNU Affero General Public License
*/
namespace Combodo\iTop\FormSDK\Helper;
namespace Combodo\iTop\FormImplementation\Helper;
use Dict;

View File

@@ -1,6 +1,6 @@
<?php
namespace Combodo\iTop\FormSDK\Helper;
namespace Combodo\iTop\FormImplementation\Helper;
use MetaModel;
use Twig\Extension\AbstractExtension;

View File

@@ -17,7 +17,7 @@
* You should have received a copy of the GNU Affero General Public License
*/
namespace Combodo\iTop\FormSDK\Field\Description;
namespace Combodo\iTop\FormSDK\Field;
/**
* Description of a form field.
@@ -31,12 +31,12 @@ class FormFieldDescription
/**
* Constructor.
*
* @param string $sPath
* @param string $sName
* @param FormFieldTypeEnumeration $oType
* @param array $aOptions
*/
public function __construct(
private readonly string $sPath,
private readonly string $sName,
private readonly FormFieldTypeEnumeration $oType,
private readonly array $aOptions
)
@@ -45,7 +45,7 @@ class FormFieldDescription
}
/**
* Get type.
* Get field type.
*
* @return FormFieldTypeEnumeration
*/
@@ -55,7 +55,7 @@ class FormFieldDescription
}
/**
* Get options.
* Get field options.
*
* @return array
*/
@@ -65,10 +65,12 @@ class FormFieldDescription
}
/**
* Get field name.
*
* @return string
*/
public function GetPath() : string
public function GetName() : string
{
return $this->sPath;
return $this->sName;
}
}

View File

@@ -17,10 +17,10 @@
* You should have received a copy of the GNU Affero General Public License
*/
namespace Combodo\iTop\FormSDK\Field\Description;
namespace Combodo\iTop\FormSDK\Field;
/**
* Form types.
* Types of fields.
*
* @package FormSDK
* @since 3.2.0
@@ -31,6 +31,7 @@ enum FormFieldTypeEnumeration : string
case AREA = 'AREA';
case DATE = 'DATE';
case SELECT = 'SELECT';
case SWITCH = 'SWITCH';
case DB_OBJECT = 'DB_OBJECT';
/**
@@ -40,10 +41,10 @@ enum FormFieldTypeEnumeration : string
*/
public function GetAvailableOptions() : array
{
$aOptions = ['label', 'required', 'disabled'];
$aOptions = ['required', 'disabled', 'attr', 'label', 'label_attr'];
return match ($this->value) {
FormFieldTypeEnumeration::SELECT => array_merge($aOptions, ['']),
FormFieldTypeEnumeration::SELECT => array_merge($aOptions, ['placeholder']),
default => $aOptions,
};
}

View File

@@ -37,7 +37,7 @@ interface FormFactoryAdapterInterface
/**
* Return description the form.
*
* @return \Combodo\iTop\FormSDK\Field\Description\FormFieldDescription[]
* @return \Combodo\iTop\FormSDK\Field\FormFieldDescription[]
*/
public function GetFormDescriptions() : array;

View File

@@ -21,8 +21,8 @@ namespace Combodo\iTop\FormSDK\Service\FactoryAdapter;
use AttributeDefinition;
use AttributeString;
use Combodo\iTop\FormSDK\Field\Description\FormFieldDescription;
use Combodo\iTop\FormSDK\Field\Description\FormFieldTypeEnumeration;
use Combodo\iTop\FormSDK\Field\FormFieldDescription;
use Combodo\iTop\FormSDK\Field\FormFieldTypeEnumeration;
use DBObject;
use Exception;
use ExceptionLog;
@@ -89,8 +89,14 @@ final class FormFactoryObjectAdapter implements FormFactoryAdapterInterface
{
$aOptions = [];
$sLabel = $oAttributeDefinition->GetLabel();
if(!$this->bGroup){
$sLabel = $this->GetLabel() . ' ••• ' . $sLabel;
}
if($oAttributeDefinition instanceof AttributeString) {
$aOptions['required'] = !$oAttributeDefinition->IsNullAllowed();
$aOptions['label'] = $sLabel;
}
return $aOptions;
@@ -101,7 +107,7 @@ final class FormFactoryObjectAdapter implements FormFactoryAdapterInterface
*
* @param string $sAttributeCode
*
* @return \Combodo\iTop\FormSDK\Field\Description\FormFieldDescription|null
* @return \Combodo\iTop\FormSDK\Field\FormFieldDescription|null
* @throws \Exception
*/
private function GetAttributeDescription(string $sAttributeCode) : ?FormFieldDescription
@@ -110,7 +116,7 @@ final class FormFactoryObjectAdapter implements FormFactoryAdapterInterface
if($oAttributeDefinition instanceof AttributeString) {
return new FormFieldDescription(
$this->GetAttributePath($sAttributeCode),
$this->GetAttributeName($sAttributeCode),
FormFieldTypeEnumeration::TEXT,
array_merge(
$this->GetAttributeOptions($oAttributeDefinition),
@@ -122,16 +128,15 @@ final class FormFactoryObjectAdapter implements FormFactoryAdapterInterface
}
/**
* Return attribute path.
* Return attribute name.
*
* @param string $sAttributeCode
*
* @return string
*/
private function GetAttributePath(string $sAttributeCode) : string
private function GetAttributeName(string $sAttributeCode) : string
{
return $this->bGroup ? $sAttributeCode : $this->GetIdentifier() . '-' . $sAttributeCode;
// return $this->GetIdentifier() . '-' . $sAttributeCode;
}
/** @inheritdoc */
@@ -140,10 +145,10 @@ final class FormFactoryObjectAdapter implements FormFactoryAdapterInterface
$aData = [];
foreach ($this->aAttributes as $sAttributeCode => $oValue){
try {
$aData[$this->GetAttributePath($sAttributeCode)] = $this->GetAttributeData($sAttributeCode);
$aData[$this->GetAttributeName($sAttributeCode)] = $this->GetAttributeData($sAttributeCode);
}
catch (Exception $e) {
$aData[$this->GetAttributePath($sAttributeCode)] = null;
$aData[$this->GetAttributeName($sAttributeCode)] = null;
ExceptionLog::LogException($e);
}
}
@@ -163,9 +168,9 @@ final class FormFactoryObjectAdapter implements FormFactoryAdapterInterface
{
$aDescriptions = [];
foreach ($this->aAttributes as $sKey => $oValue){
foreach ($this->aAttributes as $sAttCode => $oValue){
try {
$aDescriptions[$this->GetIdentifier() .'_' .$sKey] = $this->GetAttributeDescription($sKey);
$aDescriptions[$this->GetAttributeName($sAttCode)] = $this->GetAttributeDescription($sAttCode);
}
catch (Exception $e) {
ExceptionLog::LogException($e);
@@ -174,15 +179,21 @@ final class FormFactoryObjectAdapter implements FormFactoryAdapterInterface
if($this->bGroup){
$oGroupDescriptions = new FormFieldDescription($this->GetIdentifier(), FormFieldTypeEnumeration::DB_OBJECT, [
'descriptions' => $aDescriptions
'descriptions' => $aDescriptions,
'label' => $this->GetLabel()
]);
return [$oGroupDescriptions];
return [$this->GetIdentifier() => $oGroupDescriptions];
}
else{
return $aDescriptions;
}
}
public function GetLabel(): string
{
return get_class($this->oDBObject) . ' ' . $this->oDBObject->GetKey();
}
/** @inheritdoc */
public function GetIdentifier(): string
{

View File

@@ -20,11 +20,12 @@
namespace Combodo\iTop\FormSDK\Service;
use Combodo\iTop\FormSDK\Helper\SelectDataProvider;
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\Description\FormFieldDescription;
use Combodo\iTop\FormSDK\Field\Description\FormFieldTypeEnumeration;
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;
@@ -52,6 +53,9 @@ class FormFactory
/** @var array $aData form data */
private array $aData = [];
/** builder */
use FormFactoryBuilderTrait;
/**
* Constructor.
*
@@ -67,6 +71,8 @@ class FormFactory
}
/**
* Return descriptions and data arrays.
*
* @return array{descriptions:array, data:array}
*/
public function GetFormDescriptionsAndData() : array
@@ -129,188 +135,12 @@ class FormFactory
* Get form.
*
* @param string|null $sName
*
* @return \Symfony\Component\Form\FormInterface
* @return mixed
*/
public function GetForm(?string $sName): FormInterface
public function GetForm(?string $sName = null) : mixed
{
['descriptions' => $aDescriptions, 'data' => $aData] = $this->GetFormDescriptionsAndData();
return $this->oSymfonyBridge->GetForm($aDescriptions, $aData, $sName);
}
/**
* Add text field.
*
* @param string $sKey
* @param array $aOptions
* @param mixed $oData
*
* @return $this
*/
public function AddTextField(string $sKey, array $aOptions, mixed $oData = null) : FormFactory
{
// test widget for regex constraint
if(array_key_exists('constraints', $aOptions)){
$oConstraint = $aOptions['constraints'];
if($oConstraint instanceof Regex){
$aWidgetOptions = [
'pattern' => $oConstraint->pattern,
];
$aOptions = array_merge([
'attr' => [
'data-widget' => 'TextWidget',
'data-pattern' => $oConstraint->pattern,
'data-widget-options' => json_encode($aWidgetOptions)
]
], $aOptions);
}
}
$this->aDescriptions[$sKey] = new FormFieldDescription($sKey, FormFieldTypeEnumeration::TEXT, $aOptions);
$this->aData[$sKey] = $oData;
return $this;
}
/**
* Add area field.
*
* @param string $sKey
* @param array $aOptions
* @param mixed $oData
*
* @return $this
*/
public function AddAreaField(string $sKey, array $aOptions, mixed $oData = null) : FormFactory
{
$aOptions = array_merge([
'attr' => [
'data-widget' => 'AreaWidget',
'data-widget-options' => json_encode([])
]
], $aOptions);
$this->aDescriptions[$sKey] = new FormFieldDescription($sKey, FormFieldTypeEnumeration::AREA, $aOptions);
$this->aData[$sKey] = $oData;
return $this;
}
/**
* Add date field.
*
* @param string $sKey
* @param array $aOptions
* @param mixed $oData
*
* @return $this
*/
public function AddDateField(string $sKey, array $aOptions, mixed $oData = null) : FormFactory
{
$this->aDescriptions[$sKey] = new FormFieldDescription($sKey, FormFieldTypeEnumeration::DATE, $aOptions);
$this->aData[$sKey] = $oData;
return $this;
}
/**
* Add select field.
*
* @param string $sKey
* @param array $aOptions
* @param mixed $oData
*
* @return $this
*/
public function AddSelectField(string $sKey, array $aOptions, mixed $oData = null) : FormFactory
{
$this->aDescriptions[$sKey] = new FormFieldDescription($sKey, FormFieldTypeEnumeration::SELECT, $aOptions);
$this->aData[$sKey] = $oData;
return $this;
}
/**
* Add dynamic ajax select field.
*
* @param string $sKey
* @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
{
// merge ajax options
$aAjaxOptions = array_merge([
'url' => '',
'query_parameter' => 'query',
'value_field' => 'value',
'label_field' => 'label',
'search_field' => 'search',
'preload' => false,
'threshold' => -1,
'configuration' => 'AJAX'
], $aAjaxOptions);
// merge options
$aOptions = array_merge([
'placeholder' => 'Select...',
'attr' => [
'data-widget' => 'SelectWidget',
'data-ajax-query-type' => $aAjaxOptions['configuration'],
'data-widget-options' => json_encode($aAjaxOptions)
],
// 'choice_loader' => new CallbackChoiceLoader(function() use ($aAjaxOptions, $aAjaxData): array {
// $curl_data = utils::DoPostRequest($aAjaxOptions['url'], []);
// $response_data = json_decode($curl_data);
// if(count($response_data->items) > $aAjaxOptions['threshold']) return [];
// $result = [];
// foreach ($response_data->items as $e) {
// $result[$e->breed] = $e->breed;
// }
// return $result;
// }),
], $aOptions);
return $this->AddSelectField($sKey, $aOptions, $oData);
}
/**
* Add dynamic OQL select field.
*
* @param string $sKey
* @param array $aOptions
* @param string $sObjectClass
* @param string $sOql
* @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
{
$aAjaxData = [
'class' => $sObjectClass,
'oql' => $sOql,
'fields' => '{'.implode($aFieldsToLoad).'}',
];
$sUrl = 'http://localhost' . $this->oRouter->generate('formSDK_object_search') . '?' . http_build_query($aAjaxData);
$aAjaxOptions = [
'url' => $sUrl,
'query_parameter' => 'search',
'value_field' => 'key',
'label_field' => 'friendlyname',
'search_field' => 'friendlyname',
'threshold' => $iAjaxThershold,
'configuration' => 'OQL'
];
return $this->AddSelectAjaxField($sKey, $aOptions, $aAjaxOptions, $aAjaxData, $oData);
}
}

View File

@@ -0,0 +1,210 @@
<?php
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
{
/**
* Add text field.
*
* @param string $sKey
* @param array $aOptions
* @param mixed $oData
*
* @return $this
*/
public function AddTextField(string $sKey, array $aOptions, mixed $oData = null) : FormFactory
{
// test widget for regex constraint
if(array_key_exists('constraints', $aOptions)){
$oConstraint = $aOptions['constraints'];
if($oConstraint instanceof Regex){
$aWidgetOptions = [
'pattern' => $oConstraint->pattern,
];
$aOptions = array_merge([
'attr' => [
'data-widget' => 'TextWidget',
'data-pattern' => $oConstraint->pattern,
'data-widget-options' => json_encode($aWidgetOptions)
]
], $aOptions);
}
}
$this->aDescriptions[$sKey] = new FormFieldDescription($sKey, FormFieldTypeEnumeration::TEXT, $aOptions);
$this->aData[$sKey] = $oData;
return $this;
}
/**
* Add area field.
*
* @param string $sKey
* @param array $aOptions
* @param mixed $oData
*
* @return $this
*/
public function AddAreaField(string $sKey, array $aOptions, mixed $oData = null) : FormFactory
{
$aOptions = array_merge([
'attr' => [
'data-widget' => 'AreaWidget',
'data-widget-options' => json_encode([])
]
], $aOptions);
$this->aDescriptions[$sKey] = new FormFieldDescription($sKey, FormFieldTypeEnumeration::AREA, $aOptions);
$this->aData[$sKey] = $oData;
return $this;
}
/**
* Add date field.
*
* @param string $sKey
* @param array $aOptions
* @param mixed $oData
*
* @return $this
*/
public function AddDateField(string $sKey, array $aOptions, mixed $oData = null) : FormFactory
{
$this->aDescriptions[$sKey] = new FormFieldDescription($sKey, FormFieldTypeEnumeration::DATE, $aOptions);
$this->aData[$sKey] = $oData;
return $this;
}
/**
* Add select field.
*
* @param string $sKey
* @param array $aOptions
* @param mixed $oData
*
* @return $this
*/
public function AddSelectField(string $sKey, array $aOptions, mixed $oData = null) : FormFactory
{
$this->aDescriptions[$sKey] = new FormFieldDescription($sKey, FormFieldTypeEnumeration::SELECT, $aOptions);
$this->aData[$sKey] = $oData;
return $this;
}
/**
* Add dynamic ajax select field.
*
* @param string $sKey
* @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
{
// merge ajax options
$aAjaxOptions = array_merge([
'url' => '',
'query_parameter' => 'query',
'value_field' => 'value',
'label_field' => 'label',
'search_field' => 'search',
'preload' => false,
'threshold' => -1,
'configuration' => 'AJAX'
], $aAjaxOptions);
// merge options
$aOptions = array_merge([
'placeholder' => 'Select...',
'attr' => [
'data-widget' => 'SelectWidget',
'data-ajax-query-type' => $aAjaxOptions['configuration'],
'data-widget-options' => json_encode($aAjaxOptions)
],
// 'choice_loader' => new CallbackChoiceLoader(function() use ($aAjaxOptions, $aAjaxData): array {
// $curl_data = utils::DoPostRequest($aAjaxOptions['url'], []);
// $response_data = json_decode($curl_data);
// if(count($response_data->items) > $aAjaxOptions['threshold']) return [];
// $result = [];
// foreach ($response_data->items as $e) {
// $result[$e->breed] = $e->breed;
// }
// return $result;
// }),
], $aOptions);
return $this->AddSelectField($sKey, $aOptions, $oData);
}
/**
* Add dynamic OQL select field.
*
* @param string $sKey
* @param array $aOptions
* @param string $sObjectClass
* @param string $sOql
* @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
{
$aAjaxData = [
'class' => $sObjectClass,
'oql' => $sOql,
'fields' => '{'.implode($aFieldsToLoad).'}',
];
$sUrl = 'http://localhost' . $this->oRouter->generate('formSDK_object_search') . '?' . http_build_query($aAjaxData);
$aAjaxOptions = [
'url' => $sUrl,
'query_parameter' => 'search',
'value_field' => 'key',
'label_field' => 'friendlyname',
'search_field' => 'friendlyname',
'threshold' => $iAjaxThershold,
'configuration' => 'OQL'
];
return $this->AddSelectAjaxField($sKey, $aOptions, $aAjaxOptions, $aAjaxData, $oData);
}
/**
* Add switch field.
*
* @param string $sKey
* @param array $aOptions
* @param mixed $oData
*
* @return $this
*/
public function AddSwitchField(string $sKey, array $aOptions, mixed $oData = null) : FormFactory
{
$aOptions = array_merge([
'label_attr' => ['class' => 'checkbox-switch'],
], $aOptions);
$this->aDescriptions[$sKey] = new FormFieldDescription($sKey, FormFieldTypeEnumeration::SWITCH, $aOptions);
$this->aData[$sKey] = $oData;
return $this;
}
}

View File

@@ -30,7 +30,7 @@ use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
* @package FormSDK
* @since 3.2.0
*/
class FormManager
final class FormManager
{
/**

View File

@@ -20,9 +20,10 @@
namespace Combodo\iTop\FormSDK\Symfony;
use Combodo\iTop\FormSDK\Symfony\Type\Compound\FormObjectType;
use Combodo\iTop\FormSDK\Field\Description\FormFieldDescription;
use Combodo\iTop\FormSDK\Field\Description\FormFieldTypeEnumeration;
use Combodo\iTop\FormSDK\Field\FormFieldDescription;
use Combodo\iTop\FormSDK\Field\FormFieldTypeEnumeration;
use LogAPI;
use Symfony\Component\Form\Extension\Core\Type\CheckboxType;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\Extension\Core\Type\DateType;
use Symfony\Component\Form\Extension\Core\Type\FormType;
@@ -54,7 +55,7 @@ class SymfonyBridge
/**
* Transform description to Symfony description.
*
* @param \Combodo\iTop\FormSDK\Field\Description\FormFieldDescription $oFormDescription
* @param \Combodo\iTop\FormSDK\Field\FormFieldDescription $oFormDescription
*
* @return array|null
*/
@@ -64,32 +65,39 @@ class SymfonyBridge
case FormFieldTypeEnumeration::TEXT:
return [
'path' => $oFormDescription->GetPath(),
'name' => $oFormDescription->GetName(),
'type' => TextType::class,
'options' => $oFormDescription->GetOptions()
];
case FormFieldTypeEnumeration::AREA:
return [
'path' => $oFormDescription->GetPath(),
'name' => $oFormDescription->GetName(),
'type' => TextareaType::class,
'options' => $oFormDescription->GetOptions()
];
case FormFieldTypeEnumeration::DATE:
return [
'path' => $oFormDescription->GetPath(),
'name' => $oFormDescription->GetName(),
'type' => DateType::class,
'options' => $oFormDescription->GetOptions()
];
case FormFieldTypeEnumeration::SELECT:
return [
'path' => $oFormDescription->GetPath(),
'name' => $oFormDescription->GetName(),
'type' => ChoiceType::class,
'options' => $oFormDescription->GetOptions()
];
case FormFieldTypeEnumeration::SWITCH:
return [
'name' => $oFormDescription->GetName(),
'type' => CheckboxType::class,
'options' => $oFormDescription->GetOptions()
];
case FormFieldTypeEnumeration::DB_OBJECT:
$aOptions = $oFormDescription->GetOptions();
$aItems = [];
@@ -99,7 +107,7 @@ class SymfonyBridge
}
$aOptions['descriptions'] = $aItems;
return [
'path' => $oFormDescription->GetPath(),
'name' => $oFormDescription->GetName(),
'type' => FormObjectType::class,
'options' => $aOptions
];
@@ -131,10 +139,12 @@ class SymfonyBridge
// iterate throw descriptions...
foreach ($aDescriptions as $oFormDescription){
// symfony form type description
$aSymfony = $this->ToSymfonyFormType($oFormDescription);
// add type to form
$oFormBuilder->add(
$aSymfony['path'],
$aSymfony['name'],
$aSymfony['type'],
$aSymfony['options']
);
@@ -147,7 +157,7 @@ class SymfonyBridge
* @see https://itecnote.com/tecnote/php-disable-backend-validation-for-choice-field-in-symfony-2-type/
*/
if($aSymfony['type'] === ChoiceType::class){
$oFormBuilder->get($aSymfony['path'])->resetViewTransformers();
$oFormBuilder->get($aSymfony['name'])->resetViewTransformers();
}
}

View File

@@ -19,7 +19,7 @@
namespace Combodo\iTop\FormSDK\Symfony\Type\Compound;
use Combodo\iTop\FormSDK\Field\Description\FormFieldDescription;
use Combodo\iTop\FormSDK\Field\FormFieldDescription;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\FormType;
use Symfony\Component\Form\FormBuilderInterface;
@@ -39,7 +39,7 @@ class FormObjectType extends AbstractType
{
/** @var FormFieldDescription $oDescription */
foreach ($options['descriptions'] as $oDescription){
$builder->add($oDescription['path'], $oDescription['type'], $oDescription['options']);
$builder->add($oDescription['name'], $oDescription['type'], $oDescription['options']);
}
}

View File

@@ -1,5 +1,5 @@
controllers:
resource:
path: ../../sources/FormSDK/Controller/
namespace: Combodo\iTop\FormSDK\Controller
path: ../../sources/FormImplementation/Controller/
namespace: Combodo\iTop\FormImplementation\Controller
type: attribute

View File

@@ -17,7 +17,7 @@ services:
resource: '../../sources'
exclude:
- '../../sources/alias.php'
- '../../sources/FormSDK/Symfony/Type'
- '../../sources/FormImplementation/Symfony/Type'
- '../../sources/Application/WebPage'
# add more service definitions when explicit configuration is needed

View File

@@ -3,7 +3,9 @@
{% block body %}
<div class="container">
<div class="container p-5">
<h3>Form SDK : Components</h3>
{# specific form theme #}
{% if theme is defined %}

View File

@@ -1,9 +1,35 @@
{% extends 'base.html.twig' %}
{% block stylesheets %}
<style>
.theme_viewer{
display: flex;
}
.theme{
width: 100%;
padding: 20px;
}
.theme1 h4{
color: #cc8d17;
border-bottom: 1px #cc8d17 solid;
margin-bottom: 20px;
}
.theme2 h4{
color: #c7306e;
border-bottom: 1px #c7306e solid;
margin-bottom: 20px;
}
</style>
{% endblock %}
{% block body %}
<div class="container-fluid">
<div class="container-fluid p-5">
{# specific form theme #}
{% if theme1 is defined %}

View File

@@ -32,7 +32,7 @@
{%- endblock form_label -%}
{%- block form_object_row -%}
{%- set row_attr = row_attr|merge({class: 'mb-3 form-object'}) -%}
{%- set row_attr = row_attr|merge({class: 'mb-3 form-object-row'}) -%}
{{ form_row(form, {'row_attr': row_attr}) }}
{%- endblock form_object_row -%}