new: [metaFields] Adding support of sane_default + improving form & crud - WiP
parent
866fbc2d51
commit
7d6696e079
|
@ -0,0 +1,34 @@
|
||||||
|
<?php
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
use Migrations\AbstractMigration;
|
||||||
|
use Phinx\Db\Adapter\MysqlAdapter;
|
||||||
|
|
||||||
|
|
||||||
|
class MetaFieldSaneDefault extends AbstractMigration
|
||||||
|
{
|
||||||
|
|
||||||
|
public $autoId = false; // turn off automatic `id` column create. We want it to be `int(10) unsigned`
|
||||||
|
|
||||||
|
|
||||||
|
public function change()
|
||||||
|
{
|
||||||
|
$exists = $this->table('meta_template_fields')->hasColumn('sane_default');
|
||||||
|
if (!$exists) {
|
||||||
|
$this->table('meta_template_fields')
|
||||||
|
->addColumn('sane_default', 'text', [
|
||||||
|
'default' => null,
|
||||||
|
'null' => true,
|
||||||
|
'limit' => MysqlAdapter::TEXT_LONG,
|
||||||
|
'comment' => 'List of sane default values to be proposed',
|
||||||
|
])
|
||||||
|
->addColumn('values_list', 'text', [
|
||||||
|
'default' => null,
|
||||||
|
'null' => true,
|
||||||
|
'limit' => MysqlAdapter::TEXT_LONG,
|
||||||
|
'comment' => 'List of values that have to be used',
|
||||||
|
])
|
||||||
|
->update();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -7,5 +7,32 @@ use Cake\ORM\Entity;
|
||||||
|
|
||||||
class MetaTemplateField extends AppModel
|
class MetaTemplateField extends AppModel
|
||||||
{
|
{
|
||||||
|
protected $_virtual = ['form_type', 'form_options', ];
|
||||||
|
|
||||||
|
protected function _getFormType()
|
||||||
|
{
|
||||||
|
$formType = 'text';
|
||||||
|
if (!empty($this->sane_default) || !empty($this->values_list)) {
|
||||||
|
$formType = 'dropdown';
|
||||||
|
}
|
||||||
|
return $formType;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function _getFormOptions()
|
||||||
|
{
|
||||||
|
$formOptions = [];
|
||||||
|
if ($this->formType === 'dropdown') {
|
||||||
|
$selectOptions = !empty($this->sane_default) ? $this->sane_default : $this->values_list;
|
||||||
|
$selectOptions = array_combine($selectOptions, $selectOptions);
|
||||||
|
if (!empty($this->sane_default)) {
|
||||||
|
// $selectOptions['_custom'] = __('-- custom value --');
|
||||||
|
$selectOptions[] = ['value' => '_custom', 'text' => __('-- custom value --'), 'class' => 'custom-value'];
|
||||||
|
}
|
||||||
|
$selectOptions[''] = __('-- no value --');
|
||||||
|
$formOptions['options'] = $selectOptions;
|
||||||
|
// $formOptions['empty'] = [['value' => '', 'text' => __('-- select an options --'), 'hidden disabled selected value']];
|
||||||
|
}
|
||||||
|
return $formOptions;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,6 +27,8 @@ class MetaTemplateFieldsTable extends AppTable
|
||||||
$this->hasMany('MetaFields');
|
$this->hasMany('MetaFields');
|
||||||
|
|
||||||
$this->setDisplayField('field');
|
$this->setDisplayField('field');
|
||||||
|
$this->getSchema()->setColumnType('sane_default', 'json');
|
||||||
|
$this->getSchema()->setColumnType('values_list', 'json');
|
||||||
$this->loadTypeHandlers();
|
$this->loadTypeHandlers();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -30,6 +30,14 @@ echo $this->element('genericElements/IndexTable/index_table', [
|
||||||
'data_path' => 'multiple',
|
'data_path' => 'multiple',
|
||||||
'field' => 'textarea'
|
'field' => 'textarea'
|
||||||
],
|
],
|
||||||
|
[
|
||||||
|
'name' => __('Sane defaults'),
|
||||||
|
'data_path' => 'sane_default'
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'name' => __('Values List'),
|
||||||
|
'data_path' => 'values_list'
|
||||||
|
],
|
||||||
[
|
[
|
||||||
'name' => __('Validation regex'),
|
'name' => __('Validation regex'),
|
||||||
'sort' => 'regex',
|
'sort' => 'regex',
|
||||||
|
|
|
@ -1,14 +1,87 @@
|
||||||
<?php
|
<?php
|
||||||
$controlParams = [
|
$seed = 's-' . mt_rand();
|
||||||
|
$controlParams = [
|
||||||
|
'type' => 'select',
|
||||||
'options' => $fieldData['options'],
|
'options' => $fieldData['options'],
|
||||||
'empty' => $fieldData['empty'] ?? false,
|
'empty' => $fieldData['empty'] ?? false,
|
||||||
'value' => $fieldData['value'] ?? null,
|
'value' => $fieldData['value'] ?? null,
|
||||||
'multiple' => $fieldData['multiple'] ?? false,
|
'multiple' => $fieldData['multiple'] ?? false,
|
||||||
'disabled' => $fieldData['disabled'] ?? false,
|
'disabled' => $fieldData['disabled'] ?? false,
|
||||||
'class' => ($fieldData['class'] ?? '') . ' formDropdown form-select',
|
'class' => ($fieldData['class'] ?? '') . ' formDropdown form-select',
|
||||||
'default' => ($fieldData['default'] ?? null)
|
'default' => $fieldData['default'] ?? '',
|
||||||
];
|
];
|
||||||
if (!empty($fieldData['label'])) {
|
if (!empty($fieldData['field'])) { // used for multi meta-field form
|
||||||
|
$controlParams['field'] = $fieldData['field'];
|
||||||
|
}
|
||||||
|
if (!empty($fieldData['label'])) {
|
||||||
$controlParams['label'] = $fieldData['label'];
|
$controlParams['label'] = $fieldData['label'];
|
||||||
|
}
|
||||||
|
if ($controlParams['options'] instanceof \Cake\ORM\Query) {
|
||||||
|
$controlParams['options'] = $controlParams['options']->all()->toList();
|
||||||
|
}
|
||||||
|
if (in_array('_custom', array_keys($controlParams['options']))) {
|
||||||
|
$customInputValue = $this->Form->getSourceValue($fieldData['field']);
|
||||||
|
if (!in_array($customInputValue, $controlParams['options'])) {
|
||||||
|
$controlParams['options'] = array_map(function ($option) {
|
||||||
|
if (is_array($option) && $option['value'] == '_custom') {
|
||||||
|
$option[] = 'selected';
|
||||||
}
|
}
|
||||||
echo $this->FormFieldMassage->prepareFormElement($this->Form, $controlParams, $fieldData);
|
return $option;
|
||||||
|
}, $controlParams['options']);
|
||||||
|
} else {
|
||||||
|
$customInputValue = '';
|
||||||
|
}
|
||||||
|
$customControlParams = [
|
||||||
|
'value' => $fieldData['value'] ?? null,
|
||||||
|
'class' => 'd-none',
|
||||||
|
];
|
||||||
|
$controlParams['class'] .= ' dropdown-custom-value' . "-$seed";
|
||||||
|
$adaptedField = $fieldData['field'] . '_custom';
|
||||||
|
$controlParams['templates']['formGroup'] = sprintf(
|
||||||
|
'<label class="col-sm-2 col-form-label form-label" {{attrs}}>{{label}}</label><div class="col-sm-10 multi-metafield-input-container"><div class="d-flex form-dropdown-with-freetext input-group">{{input}}{{error}}%s</div></div>',
|
||||||
|
sprintf('<input type="text" class="form-control custom-value" field="%s" value="%s">', h($adaptedField), h($customInputValue))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
echo $this->FormFieldMassage->prepareFormElement($this->Form, $controlParams, $fieldData);
|
||||||
|
?>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
(function() {
|
||||||
|
$(document).ready(function() {
|
||||||
|
const $select = $('select.dropdown-custom-value-<?= $seed ?>')
|
||||||
|
toggleFreetextSelectField($select[0]);
|
||||||
|
$select.attr('onclick', 'toggleFreetextSelectField(this)')
|
||||||
|
$select.parent().find('input.custom-value').attr('oninput', 'updateAssociatedSelect(this)')
|
||||||
|
// updateAssociatedSelect($select.parent().find('input.custom-value')[0])
|
||||||
|
|
||||||
|
// Multiple saves in dropdown doesn't work
|
||||||
|
// But multiple saves for custom works but save the first element as `_custom`
|
||||||
|
})
|
||||||
|
|
||||||
|
})()
|
||||||
|
|
||||||
|
function toggleFreetextSelectField(selectEl) {
|
||||||
|
const $select = $(selectEl)
|
||||||
|
const show = $select.val() == '_custom'
|
||||||
|
const $container = $(selectEl).parent()
|
||||||
|
let $freetextInput = $container.find('input.custom-value')
|
||||||
|
if (show) {
|
||||||
|
$freetextInput.removeClass('d-none')
|
||||||
|
} else {
|
||||||
|
$freetextInput.addClass('d-none')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateAssociatedSelect(input) {
|
||||||
|
const $input = $(input)
|
||||||
|
const $select = $input.parent().find('select')
|
||||||
|
const $customOption = $select.find('option.custom-value')
|
||||||
|
$customOption.val($input.val())
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
form div.form-dropdown-with-freetext input.custom-value {
|
||||||
|
flex-grow: 3;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -31,7 +31,11 @@ foreach ($metaTemplate->meta_template_fields as $metaTemplateField) {
|
||||||
$metaField = reset($metaTemplateField->metaFields);
|
$metaField = reset($metaTemplateField->metaFields);
|
||||||
$fieldData = [
|
$fieldData = [
|
||||||
'label' => $metaTemplateField->label,
|
'label' => $metaTemplateField->label,
|
||||||
|
'type' => $metaTemplateField->formType,
|
||||||
];
|
];
|
||||||
|
if ($metaTemplateField->formType === 'dropdown') {
|
||||||
|
$fieldData = array_merge_recursive($fieldData, $metaTemplateField->formOptions);
|
||||||
|
}
|
||||||
if (isset($metaField->id)) {
|
if (isset($metaField->id)) {
|
||||||
$fieldData['field'] = sprintf('MetaTemplates.%s.meta_template_fields.%s.metaFields.%s.value', $metaField->meta_template_id, $metaField->meta_template_field_id, $metaField->id);
|
$fieldData['field'] = sprintf('MetaTemplates.%s.meta_template_fields.%s.metaFields.%s.value', $metaField->meta_template_id, $metaField->meta_template_field_id, $metaField->id);
|
||||||
} else {
|
} else {
|
||||||
|
@ -63,7 +67,11 @@ foreach ($metaTemplate->meta_template_fields as $metaTemplateField) {
|
||||||
$fieldData = [
|
$fieldData = [
|
||||||
'field' => sprintf('MetaTemplates.%s.meta_template_fields.%s.metaFields.new.0', $metaTemplateField->meta_template_id, $metaTemplateField->id),
|
'field' => sprintf('MetaTemplates.%s.meta_template_fields.%s.metaFields.new.0', $metaTemplateField->meta_template_id, $metaTemplateField->id),
|
||||||
'label' => $metaTemplateField->label,
|
'label' => $metaTemplateField->label,
|
||||||
|
'type' => $metaTemplateField->formType,
|
||||||
];
|
];
|
||||||
|
if ($metaTemplateField->formType === 'dropdown') {
|
||||||
|
$fieldData = array_merge_recursive($fieldData, $metaTemplateField->formOptions);
|
||||||
|
}
|
||||||
$fieldsHtml .= $this->element(
|
$fieldsHtml .= $this->element(
|
||||||
'genericElements/Form/fieldScaffold',
|
'genericElements/Form/fieldScaffold',
|
||||||
[
|
[
|
||||||
|
@ -74,4 +82,7 @@ foreach ($metaTemplate->meta_template_fields as $metaTemplateField) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
echo $fieldsHtml;
|
$fieldContainer = $this->Bootstrap->genNode('div', [
|
||||||
|
'class' => [],
|
||||||
|
], $fieldsHtml);
|
||||||
|
echo $fieldContainer;
|
|
@ -45,6 +45,8 @@ $seed = 'mfb-' . mt_rand();
|
||||||
$clonedContainer
|
$clonedContainer
|
||||||
.removeClass('has-error')
|
.removeClass('has-error')
|
||||||
.find('.error-message ').remove()
|
.find('.error-message ').remove()
|
||||||
|
$clonedContainer
|
||||||
|
.find('label.form-label').text('')
|
||||||
const $clonedInput = $clonedContainer.find('input, select')
|
const $clonedInput = $clonedContainer.find('input, select')
|
||||||
if ($clonedInput.length > 0) {
|
if ($clonedInput.length > 0) {
|
||||||
const injectedTemplateId = $clicked.closest('.multi-metafields-container').find('.new-metafield').length
|
const injectedTemplateId = $clicked.closest('.multi-metafields-container').find('.new-metafield').length
|
||||||
|
@ -54,7 +56,9 @@ $seed = 'mfb-' . mt_rand();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function adjustClonedInputAttr($input, injectedTemplateId) {
|
function adjustClonedInputAttr($inputs, injectedTemplateId) {
|
||||||
|
$inputs.each(function() {
|
||||||
|
const $input = $(this)
|
||||||
let explodedPath = $input.attr('field').split('.').splice(0, 5)
|
let explodedPath = $input.attr('field').split('.').splice(0, 5)
|
||||||
explodedPath.push('new', injectedTemplateId)
|
explodedPath.push('new', injectedTemplateId)
|
||||||
dottedPathStr = explodedPath.join('.')
|
dottedPathStr = explodedPath.join('.')
|
||||||
|
@ -66,6 +70,7 @@ $seed = 'mfb-' . mt_rand();
|
||||||
.attr('name', brackettedPathStr)
|
.attr('name', brackettedPathStr)
|
||||||
.val('')
|
.val('')
|
||||||
.removeClass('is-invalid')
|
.removeClass('is-invalid')
|
||||||
|
})
|
||||||
}
|
}
|
||||||
})()
|
})()
|
||||||
</script>
|
</script>
|
|
@ -22,6 +22,7 @@ if (!empty($metaFieldsEntities)) {
|
||||||
$metaFieldsEntity->meta_template_field_id,
|
$metaFieldsEntity->meta_template_field_id,
|
||||||
$metaFieldsEntity->id
|
$metaFieldsEntity->id
|
||||||
),
|
),
|
||||||
|
'type' => $metaTemplateField->formType,
|
||||||
];
|
];
|
||||||
if($metaFieldsEntity->isNew()) {
|
if($metaFieldsEntity->isNew()) {
|
||||||
$fieldData['field'] = sprintf(
|
$fieldData['field'] = sprintf(
|
||||||
|
@ -35,6 +36,9 @@ if (!empty($metaFieldsEntities)) {
|
||||||
if ($labelPrintedOnce) { // Only the first input can have a label
|
if ($labelPrintedOnce) { // Only the first input can have a label
|
||||||
$fieldData['label'] = false;
|
$fieldData['label'] = false;
|
||||||
}
|
}
|
||||||
|
if ($metaTemplateField->formType === 'dropdown') {
|
||||||
|
$fieldData = array_merge_recursive($fieldData, $metaTemplateField->formOptions);
|
||||||
|
}
|
||||||
$labelPrintedOnce = true;
|
$labelPrintedOnce = true;
|
||||||
$fieldsHtml .= $this->element(
|
$fieldsHtml .= $this->element(
|
||||||
'genericElements/Form/fieldScaffold',
|
'genericElements/Form/fieldScaffold',
|
||||||
|
@ -49,14 +53,19 @@ if (!empty($metaTemplateField) && !empty($multiple)) { // Add multiple field but
|
||||||
$metaTemplateField->label = Inflector::humanize($metaTemplateField->field);
|
$metaTemplateField->label = Inflector::humanize($metaTemplateField->field);
|
||||||
$emptyMetaFieldInput = '';
|
$emptyMetaFieldInput = '';
|
||||||
if (empty($metaFieldsEntities)) { // Include editable field for meta-template not containing a meta-field
|
if (empty($metaFieldsEntities)) { // Include editable field for meta-template not containing a meta-field
|
||||||
$emptyMetaFieldInput = $this->element(
|
$fieldData = [
|
||||||
'genericElements/Form/fieldScaffold',
|
|
||||||
[
|
|
||||||
'fieldData' => [
|
|
||||||
'label' => $metaTemplateField->label,
|
'label' => $metaTemplateField->label,
|
||||||
'field' => sprintf('MetaTemplates.%s.meta_template_fields.%s.metaFields.new.0', $metaTemplateField->meta_template_id, $metaTemplateField->id),
|
'field' => sprintf('MetaTemplates.%s.meta_template_fields.%s.metaFields.new.0', $metaTemplateField->meta_template_id, $metaTemplateField->id),
|
||||||
'class' => 'new-metafield',
|
'class' => 'new-metafield',
|
||||||
],
|
'type' => $metaTemplateField->formType,
|
||||||
|
];
|
||||||
|
if ($metaTemplateField->formType === 'dropdown') {
|
||||||
|
$fieldData = array_merge_recursive($fieldData, $metaTemplateField->formOptions);
|
||||||
|
}
|
||||||
|
$emptyMetaFieldInput = $this->element(
|
||||||
|
'genericElements/Form/fieldScaffold',
|
||||||
|
[
|
||||||
|
'fieldData' => $fieldData,
|
||||||
'form' => $form,
|
'form' => $form,
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
|
|
Loading…
Reference in New Issue