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 {
$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();
$associatedtypeMap = !empty($this->Controller->filterFields) ? $this->_getAssociatedTypeMap() : [];
$associatedtypeMap = !empty($filtersName) ? $this->_getAssociatedTypeMap() : [];
$typeMap = array_merge(
$this->Table->getSchema()->typeMap(),
$associatedtypeMap
);
$typeMap = array_filter($typeMap, function ($field) use ($filters) {
return in_array($field, $filters);
$typeMap = array_filter($typeMap, function ($field) use ($filtersName) {
return in_array($field, $filtersName);
}, ARRAY_FILTER_USE_KEY);
$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->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
*
@ -1613,7 +1632,7 @@ class CRUDComponent extends Component
protected function _getAssociatedTypeMap(): array
{
$typeMap = [];
foreach ($this->Controller->filterFields as $filter) {
foreach ($this->getFilterFieldsName() as $filter) {
$exploded = explode('.', $filter);
if (count($exploded) > 1) {
$model = $exploded[0];

View File

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

View File

@ -47,19 +47,28 @@ $filteringForm = $this->Bootstrap->table(
__('Value'),
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'];
$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([
'formGroup' => '<div class="col-sm-10">{{input}}</div>',
]);
return $this->element('genericElements/Form/fieldScaffold', [
'fieldData' => [
'field' => $fieldName,
'type' => $formType,
'label' => '',
'class' => 'fieldValue form-control-sm'
],
'fieldData' => $fieldData,
'params' => []
]);
}
@ -169,6 +178,36 @@ echo $this->Bootstrap->modal([
}
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')
let passedTags = []
tags.forEach(tagname => {
@ -185,19 +224,6 @@ echo $this->Bootstrap->modal([
.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) {
const rowData = {};
rowData['name'] = $row.find('td > span.fieldName').data('fieldname')