new: [crud:filter] Added support of IN searches using dropdown

refacto/CRUDComponent
Sami Mokaddem 2023-02-23 12:55:18 +01:00
parent fdd876b1b2
commit 4d4642770f
No known key found for this signature in database
GPG Key ID: 164C473F627A06FA
3 changed files with 79 additions and 30 deletions

View File

@ -226,22 +226,41 @@ class CRUDComponent extends Component
} else { } else {
$this->Controller->set('metaFieldsEnabled', false); $this->Controller->set('metaFieldsEnabled', false);
} }
$filters = !empty($this->Controller->filterFields) ? $this->Controller->filterFields : []; $filtersConfigRaw= !empty($this->Controller->filterFields) ? $this->Controller->filterFields : [];
$filtersConfig = [];
foreach ($filtersConfigRaw as $fieldConfig) {
if (is_array($fieldConfig)) {
$filtersConfig[$fieldConfig['name']] = $fieldConfig;
} else {
$filtersConfig[$fieldConfig] = ['name' => $fieldConfig];
}
}
$filtersName = $this->getFilterFieldsName();
$typeMap = $this->Table->getSchema()->typeMap(); $typeMap = $this->Table->getSchema()->typeMap();
$associatedtypeMap = !empty($this->Controller->filterFields) ? $this->_getAssociatedTypeMap() : []; $associatedtypeMap = !empty($filtersName) ? $this->_getAssociatedTypeMap() : [];
$typeMap = array_merge( $typeMap = array_merge(
$this->Table->getSchema()->typeMap(), $this->Table->getSchema()->typeMap(),
$associatedtypeMap $associatedtypeMap
); );
$typeMap = array_filter($typeMap, function ($field) use ($filters) { $typeMap = array_filter($typeMap, function ($field) use ($filtersName) {
return in_array($field, $filters); return in_array($field, $filtersName);
}, ARRAY_FILTER_USE_KEY); }, ARRAY_FILTER_USE_KEY);
$this->Controller->set('typeMap', $typeMap); $this->Controller->set('typeMap', $typeMap);
$this->Controller->set('filters', $filters); $this->Controller->set('filters', $filtersName);
$this->Controller->set('filtersConfig', $filtersConfig);
$this->Controller->viewBuilder()->setLayout('ajax'); $this->Controller->viewBuilder()->setLayout('ajax');
$this->Controller->render('/genericTemplates/filters'); $this->Controller->render('/genericTemplates/filters');
} }
public function getFilterFieldsName(): array
{
$filters = !empty($this->Controller->filterFields) ? $this->Controller->filterFields : [];
$filters = array_map(function($item) {
return is_array($item) ? $item['name'] : $item;
}, $filters);
return $filters;
}
/** /**
* getResponsePayload Returns the adaquate response payload based on the request context * getResponsePayload Returns the adaquate response payload based on the request context
* *
@ -1613,7 +1632,7 @@ class CRUDComponent extends Component
protected function _getAssociatedTypeMap(): array protected function _getAssociatedTypeMap(): array
{ {
$typeMap = []; $typeMap = [];
foreach ($this->Controller->filterFields as $filter) { foreach ($this->getFilterFieldsName() as $filter) {
$exploded = explode('.', $filter); $exploded = explode('.', $filter);
if (count($exploded) > 1) { if (count($exploded) > 1) {
$model = $exploded[0]; $model = $exploded[0];

View File

@ -2,7 +2,7 @@
$seed = 's-' . mt_rand(); $seed = 's-' . mt_rand();
$controlParams = [ $controlParams = [
'type' => 'select', '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,
@ -20,8 +20,10 @@ if ($controlParams['options'] instanceof \Cake\ORM\Query) {
$controlParams['options'] = $controlParams['options']->all()->toList(); $controlParams['options'] = $controlParams['options']->all()->toList();
} }
if (!empty($fieldData['select2'])) { if (!empty($fieldData['select2'])) {
$fieldData['select2'] = $fieldData['select2'] === true ? [] : $fieldData['select2'];
$controlParams['class'] .= ' select2-input'; $controlParams['class'] .= ' select2-input';
} }
$controlParams['class'] .= ' dropdown-custom-value' . "-$seed";
if (in_array('_custom', array_keys($controlParams['options']))) { if (in_array('_custom', array_keys($controlParams['options']))) {
$customInputValue = $this->Form->getSourceValue($fieldData['field']); $customInputValue = $this->Form->getSourceValue($fieldData['field']);
if (!in_array($customInputValue, $controlParams['options'])) { if (!in_array($customInputValue, $controlParams['options'])) {
@ -34,7 +36,6 @@ if (in_array('_custom', array_keys($controlParams['options']))) {
} else { } else {
$customInputValue = ''; $customInputValue = '';
} }
$controlParams['class'] .= ' dropdown-custom-value' . "-$seed";
$adaptedField = $fieldData['field'] . '_custom'; $adaptedField = $fieldData['field'] . '_custom';
$controlParams['templates']['formGroup'] = sprintf( $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>', '<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>',
@ -57,9 +58,12 @@ echo $this->FormFieldMassage->prepareFormElement($this->Form, $controlParams, $f
if ($container.length == 0) { if ($container.length == 0) {
$container = $(document.body) $container = $(document.body)
} }
$select.select2({ const defaultSelect2Options = {
dropdownParent: $container, dropdownParent: $container,
}) }
const passedSelect2Options = <?= json_encode($fieldData['select2']) ?>;
const select2Options = Object.assign({}, passedSelect2Options, defaultSelect2Options)
$select.select2(select2Options)
<?php endif; ?> <?php endif; ?>
}) })

View File

@ -47,19 +47,28 @@ $filteringForm = $this->Bootstrap->table(
__('Value'), __('Value'),
sprintf('<sup class="fa fa-info" title="%s"><sup>', __('Supports strict matches and LIKE matches with the `%` character.&#10;Example: `%.com`')) sprintf('<sup class="fa fa-info" title="%s"><sup>', __('Supports strict matches and LIKE matches with the `%` character.&#10;Example: `%.com`'))
), ),
'formatter' => function ($field, $row) use ($typeMap, $formTypeMap) { 'formatter' => function ($field, $row) use ($typeMap, $formTypeMap, $filtersConfig) {
$fieldName = $row['fieldname']; $fieldName = $row['fieldname'];
$formType = $formTypeMap[$typeMap[$fieldName]] ?? 'text'; $formType = $formTypeMap[$typeMap[$fieldName]] ?? 'text';
$fieldData = [
'field' => $fieldName,
'type' => $formType,
'label' => '',
'class' => 'fieldValue form-control-sm'
];
if (!empty($filtersConfig[$fieldName]['multiple'])) {
$fieldData['type'] = 'dropdown';
$fieldData['multiple'] = true;
$fieldData['select2'] = [
'tags' => true,
'tokenSeparators' => [',', ' '],
];
}
$this->Form->setTemplates([ $this->Form->setTemplates([
'formGroup' => '<div class="col-sm-10">{{input}}</div>', 'formGroup' => '<div class="col-sm-10">{{input}}</div>',
]); ]);
return $this->element('genericElements/Form/fieldScaffold', [ return $this->element('genericElements/Form/fieldScaffold', [
'fieldData' => [ 'fieldData' => $fieldData,
'field' => $fieldName,
'type' => $formType,
'label' => '',
'class' => 'fieldValue form-control-sm'
],
'params' => [] 'params' => []
]); ]);
} }
@ -169,6 +178,36 @@ echo $this->Bootstrap->modal([
} }
setFilteringValues($filteringTable, field, value, operator) setFilteringValues($filteringTable, field, value, operator)
} }
if (tags.length > 0) {
setFilteringTags($filteringTable, tags)
}
}
function setFilteringValues($filteringTable, field, value, operator) {
$row = $filteringTable.find('td > span.fieldName').filter(function() {
return $(this).data('fieldname') == field
}).closest('tr')
$row.find('.fieldOperator').val(operator)
const $formElement = $row.find('.fieldValue');
if ($formElement.attr('type') === 'datetime-local') {
$formElement.val(moment(value).format('yyyy-MM-DDThh:mm:ss'))
} else if ($formElement.is('select') && Array.isArray(value)) {
let newOptions = [];
value.forEach(aValue => {
const existingOption = $formElement.find('option').filter(function() {
return $(this).val() === aValue
})
if (existingOption.length == 0) {
newOptions.push(new Option(aValue, aValue, true, true))
}
})
$formElement.append(newOptions).trigger('change');
} else {
$formElement.val(value)
}
}
function setFilteringTags($filteringTable, tags) {
$select = $filteringTable.closest('.modal-body').find('select.select2-input') $select = $filteringTable.closest('.modal-body').find('select.select2-input')
let passedTags = [] let passedTags = []
tags.forEach(tagname => { tags.forEach(tagname => {
@ -185,19 +224,6 @@ echo $this->Bootstrap->modal([
.trigger('change') .trigger('change')
} }
function setFilteringValues($filteringTable, field, value, operator) {
$row = $filteringTable.find('td > span.fieldName').filter(function() {
return $(this).data('fieldname') == field
}).closest('tr')
$row.find('.fieldOperator').val(operator)
const $formElement = $row.find('.fieldValue');
if ($formElement.attr('type') === 'datetime-local') {
$formElement.val(moment(value).format('yyyy-MM-DDThh:mm:ss'))
} else {
$formElement.val(value)
}
}
function getDataFromRow($row) { function getDataFromRow($row) {
const rowData = {}; const rowData = {};
rowData['name'] = $row.find('td > span.fieldName').data('fieldname') rowData['name'] = $row.find('td > span.fieldName').data('fieldname')