From d6d592ff8c918cdd66316f0b3e592d325b526959 Mon Sep 17 00:00:00 2001 From: Sami Mokaddem Date: Wed, 10 Nov 2021 12:07:27 +0100 Subject: [PATCH] new: [genericElement:index_table] Added support of meta_fields searches --- src/Controller/Component/CRUDComponent.php | 42 +++- .../IndexTable/metafield_filtering.php | 189 ++++++++++++++++++ .../ListTopBar/group_search.php | 8 +- templates/genericTemplates/filters.php | 37 +++- 4 files changed, 265 insertions(+), 11 deletions(-) create mode 100644 templates/element/genericElements/IndexTable/metafield_filtering.php diff --git a/src/Controller/Component/CRUDComponent.php b/src/Controller/Component/CRUDComponent.php index eb0c601..29739b9 100644 --- a/src/Controller/Component/CRUDComponent.php +++ b/src/Controller/Component/CRUDComponent.php @@ -106,6 +106,11 @@ class CRUDComponent extends Component $this->Controller->set('taggingEnabled', true); $this->setAllTags(); } + if ($this->metaFieldsSupported()) { + $metaTemplates = $this->getMetaTemplates()->toArray(); + $this->Controller->set('metaFieldsEnabled', true); + $this->Controller->set('metaTemplates', $metaTemplates); + } $filters = !empty($this->Controller->filterFields) ? $this->Controller->filterFields : []; $this->Controller->set('filters', $filters); $this->Controller->viewBuilder()->setLayout('ajax'); @@ -954,10 +959,10 @@ class CRUDComponent extends Component } if (!empty($params['simpleFilters'])) { foreach ($params['simpleFilters'] as $filter => $filterValue) { - $activeFilters[$filter] = $filterValue; if ($filter === 'quickFilter') { continue; } + $activeFilters[$filter] = $filterValue; if (is_array($filterValue)) { $query->where([($filter . ' IN') => $filterValue]); } else { @@ -979,10 +984,26 @@ class CRUDComponent extends Component $query = $this->setTagFilters($query, $filteringTags); } + if ($this->metaFieldsSupported()) { + $filteringMetaFields = $this->getMetaFieldFiltersFromQuery(); + if (!empty($filteringMetaFields)) { + $activeFilters['filteringMetaFields'] = $filteringMetaFields; + } + $query = $this->setMetaFieldFilters($query, $filteringMetaFields); + } + $this->Controller->set('activeFilters', $activeFilters); return $query; } + protected function setMetaFieldFilters($query, $metaFieldFilters) + { + $modelAlias = $this->Table->getAlias(); + $subQuery = $this->Table->find('metaFieldValue', $metaFieldFilters) + ->select($modelAlias . '.id'); + return $query->where([$modelAlias . '.id IN' => $subQuery]); + } + protected function setTagFilters($query, $tags) { $modelAlias = $this->Table->getAlias(); @@ -1241,6 +1262,25 @@ class CRUDComponent extends Component ->toList(); } + private function getMetaFieldFiltersFromQuery(): array + { + $filters = []; + foreach ($this->request->getQueryParams() as $filterName => $value) { + $prefix = '_metafield'; + if (substr($filterName, 0, strlen($prefix)) === $prefix) { + $dissected = explode('_', substr($filterName, strlen($prefix))); + if (count($dissected) == 3) { // Skip if template_id or template_field_id not provided + $filters[] = [ + 'meta_template_id' => intval($dissected[1]), + 'meta_template_field_id' => intval($dissected[2]), + 'value' => $value, + ]; + } + } + } + return $filters; + } + private function renderViewInVariable($templateRelativeName, $data) { $builder = new ViewBuilder(); diff --git a/templates/element/genericElements/IndexTable/metafield_filtering.php b/templates/element/genericElements/IndexTable/metafield_filtering.php new file mode 100644 index 0000000..0bd85f8 --- /dev/null +++ b/templates/element/genericElements/IndexTable/metafield_filtering.php @@ -0,0 +1,189 @@ + $metaTemplate) { + foreach ($metaTemplate['meta_template_fields'] as $metaTemplateField) { + $filteringItems[h($metaTemplate->name)][] = ['id' => $metaTemplateField->id, 'name' => h($metaTemplateField->field), 'template_id' => $template_id]; + } +} + +$filteringForm = $this->Bootstrap->table( + [ + 'small' => true, + 'striped' => false, + 'hover' => false, + 'tableClass' => ['indexMetaFieldsFilteringTable'], + ], + [ + 'fields' => [ + __('Meta Field'), + __('Operator'), + [ + 'labelHtml' => sprintf( + '%s %s', + __('Value'), + sprintf('', __('Supports strict matches and LIKE matches with the `%` character. Example: `%.com`')) + ) + ], + __('Action') + ], + 'items' => [] + ] +); +?> + + + \ No newline at end of file diff --git a/templates/element/genericElements/ListTopBar/group_search.php b/templates/element/genericElements/ListTopBar/group_search.php index 46cef31..4a21baa 100644 --- a/templates/element/genericElements/ListTopBar/group_search.php +++ b/templates/element/genericElements/ListTopBar/group_search.php @@ -19,6 +19,10 @@ $filteringButton = ''; if (!empty($data['allowFilering'])) { $activeFilters = !empty($activeFilters) ? $activeFilters : []; + $numberActiveFilters = count($activeFilters); + if (!empty($activeFilters['filteringMetaFields'])) { + $numberActiveFilters += count($activeFilters['filteringMetaFields']) - 1; + } $buttonConfig = [ 'icon' => 'filter', 'params' => [ @@ -29,8 +33,8 @@ if (count($activeFilters) > 0) { $buttonConfig['badge'] = [ 'variant' => 'light', - 'text' => count($activeFilters), - 'title' => __n('There is {0} active filter', 'There are {0} active filters', count($activeFilters), count($activeFilters)) + 'text' => $numberActiveFilters, + 'title' => __n('There is {0} active filter', 'There are {0} active filters', $numberActiveFilters, $numberActiveFilters) ]; } $filteringButton = $this->Bootstrap->button($buttonConfig); diff --git a/templates/genericTemplates/filters.php b/templates/genericTemplates/filters.php index 97602ec..33fb379 100644 --- a/templates/genericTemplates/filters.php +++ b/templates/genericTemplates/filters.php @@ -47,26 +47,39 @@ $filteringForm = $this->Bootstrap->table( ] ); +$filteringMetafields = ''; +if ($metaFieldsEnabled) { + $helpText = $this->Bootstrap->genNode('sup', [ + 'class' => ['ms-1 fa fa-info'], + 'title' => __('Include help'), + 'data-bs-toggle' => 'tooltip', + ]); + $filteringMetafields = $this->Bootstrap->genNode('h5', [], __('Meta Fields') . $helpText); + $filteringMetafields .= $this->element('genericElements/IndexTable/metafield_filtering', $metaTemplates); +} + +$filteringTags = ''; if ($taggingEnabled) { $helpText = $this->Bootstrap->genNode('sup', [ 'class' => ['ms-1 fa fa-info'], 'title' => __('Supports negation matches (with the `!` character) and LIKE matches (with the `%` character). Example: `!exportable`, `%able`'), 'data-bs-toggle' => 'tooltip', ]); - $filteringTags = $this->Bootstrap->genNode('h5', [], __('Tags') . $helpText); + $filteringTags = $this->Bootstrap->genNode('h5', [ + 'class' => 'mt-2' + ], __('Tags') . $helpText); $filteringTags .= $this->Tag->tags([], [ 'allTags' => $allTags, 'picker' => true, 'editable' => false, ]); -} else { - $filteringTags = ''; } -$modalBody = sprintf('%s%s', $filteringForm, $filteringTags); + +$modalBody = implode('', [$filteringForm, $filteringMetafields, $filteringTags]); echo $this->Bootstrap->modal([ 'title' => __('Filtering options for {0}', Inflector::singularize($this->request->getParam('controller'))), - 'size' => 'lg', + 'size' => !empty($metaFieldsEnabled) ? 'xl' : 'lg', 'type' => 'confirm', 'bodyHtml' => $modalBody, 'confirmText' => __('Filter'), @@ -84,7 +97,7 @@ echo $this->Bootstrap->modal([ const controller = 'request->getParam('controller') ?>'; const action = 'index'; const $tbody = modalObject.$modal.find('table.indexFilteringTable tbody') - const $rows = $tbody.find('tr:not(#controlRow)') + const $rows = $tbody.find('tr') const activeFilters = {} $rows.each(function() { const rowData = getDataFromRow($(this)) @@ -96,8 +109,16 @@ echo $this->Bootstrap->modal([ activeFilters[fullFilter] = rowData['value'] } }) - $select = modalObject.$modal.find('select.select2-input') - activeFilters['filteringTags'] = $select.select2('data').map(tag => tag.text) + 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 + } + } + $selectTag = modalObject.$modal.find('.tag-container select.select2-input') + activeFilters['filteringTags'] = $selectTag.select2('data').map(tag => tag.text) const searchParam = jQuery.param(activeFilters); const url = `/${controller}/${action}?${searchParam}`