From aeda393bbab5b6fb0ab21493ea5eb9ce39b094b4 Mon Sep 17 00:00:00 2001 From: Sami Mokaddem Date: Thu, 27 Oct 2022 15:56:39 +0200 Subject: [PATCH] chg: [component:CRUD] Improved filtering to support form type based on database column type --- src/Controller/Component/CRUDComponent.php | 32 ++++++++++++++ templates/genericTemplates/filters.php | 51 +++++++++++++++++----- 2 files changed, 73 insertions(+), 10 deletions(-) diff --git a/src/Controller/Component/CRUDComponent.php b/src/Controller/Component/CRUDComponent.php index 7d7e612..a11937e 100644 --- a/src/Controller/Component/CRUDComponent.php +++ b/src/Controller/Component/CRUDComponent.php @@ -48,6 +48,8 @@ class CRUDComponent extends Component $optionFilters = empty($options['filters']) ? [] : $options['filters']; foreach ($optionFilters as $i => $filter) { $optionFilters[] = "{$filter} !="; + $optionFilters[] = "{$filter} >="; + $optionFilters[] = "{$filter} <="; } $params = $this->Controller->ParamHandler->harvestParams($optionFilters); $params = $this->fakeContextFilter($options, $params); @@ -196,6 +198,16 @@ class CRUDComponent extends Component $this->Controller->set('metaFieldsEnabled', false); } $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->viewBuilder()->setLayout('ajax'); $this->Controller->render('/genericTemplates/filters'); @@ -1522,4 +1534,24 @@ class CRUDComponent extends Component $view = $builder->build($data); 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; + } } diff --git a/templates/genericTemplates/filters.php b/templates/genericTemplates/filters.php index b101e09..d900104 100644 --- a/templates/genericTemplates/filters.php +++ b/templates/genericTemplates/filters.php @@ -7,6 +7,7 @@ $tableItems = array_map(function ($fieldName) { 'fieldname' => $fieldName, ]; }, $filters); +$formTypeMap = $this->Form->getConfig('typeMap'); $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 = [ sprintf('', '=', '='), sprintf('', '!=', '!='), ]; + if ($type === 'datetime') { + $options = [ + sprintf('', '>=', '>='), + sprintf('', '<=', '<='), + ]; + } return sprintf('', implode('', $options)); } ], @@ -38,8 +47,21 @@ $filteringForm = $this->Bootstrap->table( __('Value'), sprintf('', __('Supports strict matches and LIKE matches with the `%` character. Example: `%.com`')) ), - 'formatter' => function ($field, $row) { - return sprintf(''); + 'formatter' => function ($field, $row) use ($typeMap, $formTypeMap) { + $fieldName = $row['fieldname']; + $formType = $formTypeMap[$typeMap[$fieldName]] ?? 'text'; + $this->Form->setTemplates([ + 'formGroup' => '
{{input}}
', + ]); + 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() { const rowData = getDataFromRow($(this)) let fullFilter = rowData['name'] - if (rowData['operator'] == '!=') { - fullFilter += ' !=' + if (rowData['operator'] != '=') { + fullFilter += ` ${rowData['operator']}` } if (rowData['value'].length > 0) { activeFilters[fullFilter] = rowData['value'] @@ -111,7 +133,6 @@ echo $this->Bootstrap->modal([ }) if (modalObject.$modal.find('table.indexMetaFieldsFilteringTable').length > 0) { let metaFieldFilters = modalObject.$modal.find('table.indexMetaFieldsFilteringTable')[0].getFiltersFunction() - // activeFilters['filteringMetaFields'] = metaFieldFilters !== undefined ? metaFieldFilters : []; metaFieldFilters = metaFieldFilters !== undefined ? metaFieldFilters : [] for (let [metaFieldPath, metaFieldValue] of Object.entries(metaFieldFilters)) { activeFilters[metaFieldPath] = metaFieldValue @@ -138,8 +159,8 @@ echo $this->Bootstrap->modal([ for (let [field, value] of Object.entries(activeFilters)) { const fieldParts = field.split(' ') let operator = '=' - if (fieldParts.length == 2 && fieldParts[1] == '!=') { - operator = '!=' + if (fieldParts.length == 2) { + operator = fieldParts[1] field = fieldParts[0] } else if (fieldParts.length > 2) { console.error('Field contains multiple spaces. ' + field) @@ -167,14 +188,24 @@ echo $this->Bootstrap->modal([ return $(this).data('fieldname') == field }).closest('tr') $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) { const rowData = {}; rowData['name'] = $row.find('td > span.fieldName').data('fieldname') 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 }