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
|
||||
{
|
||||
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->setDisplayField('field');
|
||||
$this->getSchema()->setColumnType('sane_default', 'json');
|
||||
$this->getSchema()->setColumnType('values_list', 'json');
|
||||
$this->loadTypeHandlers();
|
||||
}
|
||||
|
||||
|
|
|
@ -30,6 +30,14 @@ echo $this->element('genericElements/IndexTable/index_table', [
|
|||
'data_path' => 'multiple',
|
||||
'field' => 'textarea'
|
||||
],
|
||||
[
|
||||
'name' => __('Sane defaults'),
|
||||
'data_path' => 'sane_default'
|
||||
],
|
||||
[
|
||||
'name' => __('Values List'),
|
||||
'data_path' => 'values_list'
|
||||
],
|
||||
[
|
||||
'name' => __('Validation regex'),
|
||||
'sort' => 'regex',
|
||||
|
|
|
@ -1,14 +1,87 @@
|
|||
<?php
|
||||
$controlParams = [
|
||||
'options' => $fieldData['options'],
|
||||
'empty' => $fieldData['empty'] ?? false,
|
||||
'value' => $fieldData['value'] ?? null,
|
||||
'multiple' => $fieldData['multiple'] ?? false,
|
||||
'disabled' => $fieldData['disabled'] ?? false,
|
||||
'class' => ($fieldData['class'] ?? '') . ' formDropdown form-select',
|
||||
'default' => ($fieldData['default'] ?? null)
|
||||
];
|
||||
if (!empty($fieldData['label'])) {
|
||||
$controlParams['label'] = $fieldData['label'];
|
||||
$seed = 's-' . mt_rand();
|
||||
$controlParams = [
|
||||
'type' => 'select',
|
||||
'options' => $fieldData['options'],
|
||||
'empty' => $fieldData['empty'] ?? false,
|
||||
'value' => $fieldData['value'] ?? null,
|
||||
'multiple' => $fieldData['multiple'] ?? false,
|
||||
'disabled' => $fieldData['disabled'] ?? false,
|
||||
'class' => ($fieldData['class'] ?? '') . ' formDropdown form-select',
|
||||
'default' => $fieldData['default'] ?? '',
|
||||
];
|
||||
if (!empty($fieldData['field'])) { // used for multi meta-field form
|
||||
$controlParams['field'] = $fieldData['field'];
|
||||
}
|
||||
if (!empty($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';
|
||||
}
|
||||
return $option;
|
||||
}, $controlParams['options']);
|
||||
} else {
|
||||
$customInputValue = '';
|
||||
}
|
||||
echo $this->FormFieldMassage->prepareFormElement($this->Form, $controlParams, $fieldData);
|
||||
$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);
|
||||
$fieldData = [
|
||||
'label' => $metaTemplateField->label,
|
||||
'type' => $metaTemplateField->formType,
|
||||
];
|
||||
if ($metaTemplateField->formType === 'dropdown') {
|
||||
$fieldData = array_merge_recursive($fieldData, $metaTemplateField->formOptions);
|
||||
}
|
||||
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);
|
||||
} else {
|
||||
|
@ -63,7 +67,11 @@ foreach ($metaTemplate->meta_template_fields as $metaTemplateField) {
|
|||
$fieldData = [
|
||||
'field' => sprintf('MetaTemplates.%s.meta_template_fields.%s.metaFields.new.0', $metaTemplateField->meta_template_id, $metaTemplateField->id),
|
||||
'label' => $metaTemplateField->label,
|
||||
'type' => $metaTemplateField->formType,
|
||||
];
|
||||
if ($metaTemplateField->formType === 'dropdown') {
|
||||
$fieldData = array_merge_recursive($fieldData, $metaTemplateField->formOptions);
|
||||
}
|
||||
$fieldsHtml .= $this->element(
|
||||
'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
|
||||
.removeClass('has-error')
|
||||
.find('.error-message ').remove()
|
||||
$clonedContainer
|
||||
.find('label.form-label').text('')
|
||||
const $clonedInput = $clonedContainer.find('input, select')
|
||||
if ($clonedInput.length > 0) {
|
||||
const injectedTemplateId = $clicked.closest('.multi-metafields-container').find('.new-metafield').length
|
||||
|
@ -54,18 +56,21 @@ $seed = 'mfb-' . mt_rand();
|
|||
}
|
||||
}
|
||||
|
||||
function adjustClonedInputAttr($input, injectedTemplateId) {
|
||||
let explodedPath = $input.attr('field').split('.').splice(0, 5)
|
||||
explodedPath.push('new', injectedTemplateId)
|
||||
dottedPathStr = explodedPath.join('.')
|
||||
brackettedPathStr = explodedPath.map((elem, i) => {
|
||||
return i == 0 ? elem : `[${elem}]`
|
||||
}).join('')
|
||||
$input.attr('id', dottedPathStr)
|
||||
.attr('field', dottedPathStr)
|
||||
.attr('name', brackettedPathStr)
|
||||
.val('')
|
||||
.removeClass('is-invalid')
|
||||
function adjustClonedInputAttr($inputs, injectedTemplateId) {
|
||||
$inputs.each(function() {
|
||||
const $input = $(this)
|
||||
let explodedPath = $input.attr('field').split('.').splice(0, 5)
|
||||
explodedPath.push('new', injectedTemplateId)
|
||||
dottedPathStr = explodedPath.join('.')
|
||||
brackettedPathStr = explodedPath.map((elem, i) => {
|
||||
return i == 0 ? elem : `[${elem}]`
|
||||
}).join('')
|
||||
$input.attr('id', dottedPathStr)
|
||||
.attr('field', dottedPathStr)
|
||||
.attr('name', brackettedPathStr)
|
||||
.val('')
|
||||
.removeClass('is-invalid')
|
||||
})
|
||||
}
|
||||
})()
|
||||
</script>
|
|
@ -22,6 +22,7 @@ if (!empty($metaFieldsEntities)) {
|
|||
$metaFieldsEntity->meta_template_field_id,
|
||||
$metaFieldsEntity->id
|
||||
),
|
||||
'type' => $metaTemplateField->formType,
|
||||
];
|
||||
if($metaFieldsEntity->isNew()) {
|
||||
$fieldData['field'] = sprintf(
|
||||
|
@ -35,6 +36,9 @@ if (!empty($metaFieldsEntities)) {
|
|||
if ($labelPrintedOnce) { // Only the first input can have a label
|
||||
$fieldData['label'] = false;
|
||||
}
|
||||
if ($metaTemplateField->formType === 'dropdown') {
|
||||
$fieldData = array_merge_recursive($fieldData, $metaTemplateField->formOptions);
|
||||
}
|
||||
$labelPrintedOnce = true;
|
||||
$fieldsHtml .= $this->element(
|
||||
'genericElements/Form/fieldScaffold',
|
||||
|
@ -49,14 +53,19 @@ if (!empty($metaTemplateField) && !empty($multiple)) { // Add multiple field but
|
|||
$metaTemplateField->label = Inflector::humanize($metaTemplateField->field);
|
||||
$emptyMetaFieldInput = '';
|
||||
if (empty($metaFieldsEntities)) { // Include editable field for meta-template not containing a meta-field
|
||||
$fieldData = [
|
||||
'label' => $metaTemplateField->label,
|
||||
'field' => sprintf('MetaTemplates.%s.meta_template_fields.%s.metaFields.new.0', $metaTemplateField->meta_template_id, $metaTemplateField->id),
|
||||
'class' => 'new-metafield',
|
||||
'type' => $metaTemplateField->formType,
|
||||
];
|
||||
if ($metaTemplateField->formType === 'dropdown') {
|
||||
$fieldData = array_merge_recursive($fieldData, $metaTemplateField->formOptions);
|
||||
}
|
||||
$emptyMetaFieldInput = $this->element(
|
||||
'genericElements/Form/fieldScaffold',
|
||||
[
|
||||
'fieldData' => [
|
||||
'label' => $metaTemplateField->label,
|
||||
'field' => sprintf('MetaTemplates.%s.meta_template_fields.%s.metaFields.new.0', $metaTemplateField->meta_template_id, $metaTemplateField->id),
|
||||
'class' => 'new-metafield',
|
||||
],
|
||||
'fieldData' => $fieldData,
|
||||
'form' => $form,
|
||||
]
|
||||
);
|
||||
|
|
Loading…
Reference in New Issue