chg: [component:CRUD] Improved filtering to support form type based on database column type
parent
e1499fb705
commit
aeda393bba
|
@ -48,6 +48,8 @@ class CRUDComponent extends Component
|
||||||
$optionFilters = empty($options['filters']) ? [] : $options['filters'];
|
$optionFilters = empty($options['filters']) ? [] : $options['filters'];
|
||||||
foreach ($optionFilters as $i => $filter) {
|
foreach ($optionFilters as $i => $filter) {
|
||||||
$optionFilters[] = "{$filter} !=";
|
$optionFilters[] = "{$filter} !=";
|
||||||
|
$optionFilters[] = "{$filter} >=";
|
||||||
|
$optionFilters[] = "{$filter} <=";
|
||||||
}
|
}
|
||||||
$params = $this->Controller->ParamHandler->harvestParams($optionFilters);
|
$params = $this->Controller->ParamHandler->harvestParams($optionFilters);
|
||||||
$params = $this->fakeContextFilter($options, $params);
|
$params = $this->fakeContextFilter($options, $params);
|
||||||
|
@ -196,6 +198,16 @@ class CRUDComponent extends Component
|
||||||
$this->Controller->set('metaFieldsEnabled', false);
|
$this->Controller->set('metaFieldsEnabled', false);
|
||||||
}
|
}
|
||||||
$filters = !empty($this->Controller->filterFields) ? $this->Controller->filterFields : [];
|
$filters = !empty($this->Controller->filterFields) ? $this->Controller->filterFields : [];
|
||||||
|
$typeMap = $this->Table->getSchema()->typeMap();
|
||||||
|
$associatedtypeMap = !empty($this->Controller->filterFields) ? $this->_getAssociatedTypeMap() : [];
|
||||||
|
$typeMap = array_merge(
|
||||||
|
$this->Table->getSchema()->typeMap(),
|
||||||
|
$associatedtypeMap
|
||||||
|
);
|
||||||
|
$typeMap = array_filter($typeMap, function ($field) use ($filters) {
|
||||||
|
return in_array($field, $filters);
|
||||||
|
}, ARRAY_FILTER_USE_KEY);
|
||||||
|
$this->Controller->set('typeMap', $typeMap);
|
||||||
$this->Controller->set('filters', $filters);
|
$this->Controller->set('filters', $filters);
|
||||||
$this->Controller->viewBuilder()->setLayout('ajax');
|
$this->Controller->viewBuilder()->setLayout('ajax');
|
||||||
$this->Controller->render('/genericTemplates/filters');
|
$this->Controller->render('/genericTemplates/filters');
|
||||||
|
@ -1522,4 +1534,24 @@ class CRUDComponent extends Component
|
||||||
$view = $builder->build($data);
|
$view = $builder->build($data);
|
||||||
return $view->render();
|
return $view->render();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected function _getAssociatedTypeMap(): array
|
||||||
|
{
|
||||||
|
$typeMap = [];
|
||||||
|
foreach ($this->Controller->filterFields as $filter) {
|
||||||
|
$exploded = explode('.', $filter);
|
||||||
|
if (count($exploded) > 1) {
|
||||||
|
$model = $exploded[0];
|
||||||
|
$subField = $exploded[1];
|
||||||
|
if ($model == $this->Table->getAlias()) {
|
||||||
|
$typeMap[$filter] = $this->Table->getSchema()->typeMap()[$subField] ?? 'text';
|
||||||
|
} else {
|
||||||
|
$association = $this->Table->associations()->get($model);
|
||||||
|
$associatedTable = $association->getTarget();
|
||||||
|
$typeMap[$filter] = $associatedTable->getSchema()->typeMap()[$subField] ?? 'text';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $typeMap;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,7 @@ $tableItems = array_map(function ($fieldName) {
|
||||||
'fieldname' => $fieldName,
|
'fieldname' => $fieldName,
|
||||||
];
|
];
|
||||||
}, $filters);
|
}, $filters);
|
||||||
|
$formTypeMap = $this->Form->getConfig('typeMap');
|
||||||
|
|
||||||
$filteringForm = $this->Bootstrap->table(
|
$filteringForm = $this->Bootstrap->table(
|
||||||
[
|
[
|
||||||
|
@ -23,11 +24,19 @@ $filteringForm = $this->Bootstrap->table(
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
'key' => 'operator', 'label' => __('Operator'), 'formatter' => function ($field, $row) {
|
'key' => 'operator', 'label' => __('Operator'), 'formatter' => function ($field, $row) use ($typeMap) {
|
||||||
|
$fieldName = $row['fieldname'];
|
||||||
|
$type = $typeMap[$fieldName] ?? 'text';
|
||||||
$options = [
|
$options = [
|
||||||
sprintf('<option value="%s">%s</option>', '=', '='),
|
sprintf('<option value="%s">%s</option>', '=', '='),
|
||||||
sprintf('<option value="%s">%s</option>', '!=', '!='),
|
sprintf('<option value="%s">%s</option>', '!=', '!='),
|
||||||
];
|
];
|
||||||
|
if ($type === 'datetime') {
|
||||||
|
$options = [
|
||||||
|
sprintf('<option value="%s">%s</option>', '>=', '>='),
|
||||||
|
sprintf('<option value="%s">%s</option>', '<=', '<='),
|
||||||
|
];
|
||||||
|
}
|
||||||
return sprintf('<select class="fieldOperator form-select form-select-sm">%s</select>', implode('', $options));
|
return sprintf('<select class="fieldOperator form-select form-select-sm">%s</select>', implode('', $options));
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
@ -38,8 +47,21 @@ $filteringForm = $this->Bootstrap->table(
|
||||||
__('Value'),
|
__('Value'),
|
||||||
sprintf('<sup class="fa fa-info" title="%s"><sup>', __('Supports strict matches and LIKE matches with the `%` character. Example: `%.com`'))
|
sprintf('<sup class="fa fa-info" title="%s"><sup>', __('Supports strict matches and LIKE matches with the `%` character. Example: `%.com`'))
|
||||||
),
|
),
|
||||||
'formatter' => function ($field, $row) {
|
'formatter' => function ($field, $row) use ($typeMap, $formTypeMap) {
|
||||||
return sprintf('<input type="text" class="fieldValue form-control form-control-sm">');
|
$fieldName = $row['fieldname'];
|
||||||
|
$formType = $formTypeMap[$typeMap[$fieldName]] ?? 'text';
|
||||||
|
$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'
|
||||||
|
],
|
||||||
|
'params' => []
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
|
@ -102,8 +124,8 @@ echo $this->Bootstrap->modal([
|
||||||
$rows.each(function() {
|
$rows.each(function() {
|
||||||
const rowData = getDataFromRow($(this))
|
const rowData = getDataFromRow($(this))
|
||||||
let fullFilter = rowData['name']
|
let fullFilter = rowData['name']
|
||||||
if (rowData['operator'] == '!=') {
|
if (rowData['operator'] != '=') {
|
||||||
fullFilter += ' !='
|
fullFilter += ` ${rowData['operator']}`
|
||||||
}
|
}
|
||||||
if (rowData['value'].length > 0) {
|
if (rowData['value'].length > 0) {
|
||||||
activeFilters[fullFilter] = rowData['value']
|
activeFilters[fullFilter] = rowData['value']
|
||||||
|
@ -111,7 +133,6 @@ echo $this->Bootstrap->modal([
|
||||||
})
|
})
|
||||||
if (modalObject.$modal.find('table.indexMetaFieldsFilteringTable').length > 0) {
|
if (modalObject.$modal.find('table.indexMetaFieldsFilteringTable').length > 0) {
|
||||||
let metaFieldFilters = modalObject.$modal.find('table.indexMetaFieldsFilteringTable')[0].getFiltersFunction()
|
let metaFieldFilters = modalObject.$modal.find('table.indexMetaFieldsFilteringTable')[0].getFiltersFunction()
|
||||||
// activeFilters['filteringMetaFields'] = metaFieldFilters !== undefined ? metaFieldFilters : [];
|
|
||||||
metaFieldFilters = metaFieldFilters !== undefined ? metaFieldFilters : []
|
metaFieldFilters = metaFieldFilters !== undefined ? metaFieldFilters : []
|
||||||
for (let [metaFieldPath, metaFieldValue] of Object.entries(metaFieldFilters)) {
|
for (let [metaFieldPath, metaFieldValue] of Object.entries(metaFieldFilters)) {
|
||||||
activeFilters[metaFieldPath] = metaFieldValue
|
activeFilters[metaFieldPath] = metaFieldValue
|
||||||
|
@ -138,8 +159,8 @@ echo $this->Bootstrap->modal([
|
||||||
for (let [field, value] of Object.entries(activeFilters)) {
|
for (let [field, value] of Object.entries(activeFilters)) {
|
||||||
const fieldParts = field.split(' ')
|
const fieldParts = field.split(' ')
|
||||||
let operator = '='
|
let operator = '='
|
||||||
if (fieldParts.length == 2 && fieldParts[1] == '!=') {
|
if (fieldParts.length == 2) {
|
||||||
operator = '!='
|
operator = fieldParts[1]
|
||||||
field = fieldParts[0]
|
field = fieldParts[0]
|
||||||
} else if (fieldParts.length > 2) {
|
} else if (fieldParts.length > 2) {
|
||||||
console.error('Field contains multiple spaces. ' + field)
|
console.error('Field contains multiple spaces. ' + field)
|
||||||
|
@ -167,14 +188,24 @@ echo $this->Bootstrap->modal([
|
||||||
return $(this).data('fieldname') == field
|
return $(this).data('fieldname') == field
|
||||||
}).closest('tr')
|
}).closest('tr')
|
||||||
$row.find('.fieldOperator').val(operator)
|
$row.find('.fieldOperator').val(operator)
|
||||||
$row.find('.fieldValue').val(value)
|
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')
|
||||||
rowData['operator'] = $row.find('select.fieldOperator').val()
|
rowData['operator'] = $row.find('select.fieldOperator').val()
|
||||||
rowData['value'] = $row.find('input.fieldValue').val()
|
const $formElement = $row.find('.fieldValue');
|
||||||
|
if ($formElement.attr('type') === 'datetime-local') {
|
||||||
|
rowData['value'] = moment($formElement.val()).toISOString()
|
||||||
|
} else {
|
||||||
|
rowData['value'] = $formElement.val()
|
||||||
|
}
|
||||||
return rowData
|
return rowData
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue