From 4e5f334467fe9b349cbd5dc2954a6a1efbb441c6 Mon Sep 17 00:00:00 2001 From: Benn Oshrin Date: Fri, 17 Sep 2021 11:51:01 -0400 Subject: [PATCH] Initial commits for CO-1871 (filter Matchgrid) and CO-2137 (configurable columns) --- app/config/schema/schema.json | 1 + .../Controller/MatchgridRecordsController.php | 7 +- app/src/Controller/StandardController.php | 15 ++- app/src/Lib/Traits/SearchFilterTrait.php | 111 ++++++++++++++++++ app/src/Locale/en_US/default.po | 3 + app/src/Model/Table/AttributesTable.php | 7 ++ app/src/Model/Table/MatchgridRecordsTable.php | 19 +++ app/src/Template/Attributes/fields.inc | 2 + app/src/Template/MatchgridRecords/columns.inc | 12 +- 9 files changed, 173 insertions(+), 4 deletions(-) create mode 100644 app/src/Lib/Traits/SearchFilterTrait.php diff --git a/app/config/schema/schema.json b/app/config/schema/schema.json index 8f7f6b3b2..3308eae4b 100644 --- a/app/config/schema/schema.json +++ b/app/config/schema/schema.json @@ -138,6 +138,7 @@ "name": {}, "description": {}, "api_name": { "type": "string", "size": 128 }, + "index_display": { "type": "boolean" }, "alphanumeric": { "type": "boolean" }, "case_sensitive": { "type": "boolean" }, "invalidates": { "type": "boolean" }, diff --git a/app/src/Controller/MatchgridRecordsController.php b/app/src/Controller/MatchgridRecordsController.php index 023c2d9f1..ab0c72b9b 100644 --- a/app/src/Controller/MatchgridRecordsController.php +++ b/app/src/Controller/MatchgridRecordsController.php @@ -71,8 +71,11 @@ public function initialize() { throw new \RuntimeException(__('match.er.mgid')); } - // Set the table name for MatchgridRecordsTable - TableRegistry::getTableLocator()->setConfig('MatchgridRecords', ['table' => $obj->prefixed_table_name]); + // Set the table name and Matchgrid ID for MatchgridRecordsTable + TableRegistry::getTableLocator()->setConfig('MatchgridRecords', [ + 'table' => $obj->prefixed_table_name, + 'matchgrid_id' => $this->request->getQuery('matchgrid_id') + ]); } /** diff --git a/app/src/Controller/StandardController.php b/app/src/Controller/StandardController.php index bf4dbea11..b4f2a550d 100644 --- a/app/src/Controller/StandardController.php +++ b/app/src/Controller/StandardController.php @@ -347,7 +347,20 @@ public function index() { && $table->getIndexContains()) { $query->contain($table->getIndexContains()); } - + + // SearchFilterTrait + if(method_exists($table, "getSearchableAttributes")) { + $searchableAttributes = $table->getSearchableAttributes(); + + if(!empty($searchableAttributes)) { + foreach($searchableAttributes as $attribute) { + if(!empty($this->request->getQuery($attribute))) { + $query = $table->whereFilter($query, $attribute, $this->request->getQuery($attribute)); + } + } + } + } + // The Cake documents describe $this->paginate (which worked in Cake 2), // but it doesn't seem to work in Cake 3/4. So we just use $this->pagination // ourselves here. diff --git a/app/src/Lib/Traits/SearchFilterTrait.php b/app/src/Lib/Traits/SearchFilterTrait.php new file mode 100644 index 000000000..70b35cfd3 --- /dev/null +++ b/app/src/Lib/Traits/SearchFilterTrait.php @@ -0,0 +1,111 @@ +searchFilters); + } + + /** + * Add a permitted search filters. + * + * @since COmanage Match v1.0.0 + * @param string $attribute Attribute that filtering is permitted on (database name) + * @param bool $caseSensitive Whether this attribute is case sensitive + * @param bool $substring Whether substring searching is permitted for this attrimute + */ + + public function setSearchFilter(string $attribute, bool $caseSensitive=false, bool $substring=true) { + $this->searchFilters[$attribute] = [ + 'caseSensitive' => $caseSensitive, + 'substring' => $substring + ]; + } + + /** + * Build a query where() clause for the configured attribute. + * + * @since COmanage Match v1.0.0 + * @param \Cake\ORM\Query $query Cake ORM Query object + * @param string $attribute Attribute to filter on (database name) + * @param string $q Value to filter on + * @return \Cake\ORM\Query Cake ORM Query object + */ + + public function whereFilter(\Cake\ORM\Query $query, string $attribute, string $q) { + if(!empty($this->searchFilters[$attribute])) { + $cs = (isset($this->searchFilters[$attribute]['caseSensitive']) + && $this->searchFilters[$attribute]['caseSensitive']); + + $sub = (isset($this->searchFilters[$attribute]['substring']) + && $this->searchFilters[$attribute]['substring']); + + $search = $q; + + if($sub) { + // Substring + // note, for now at least, a user may infix their own % + $search .= "%"; + } + + if($cs) { + // Case sensitive + $query->where([$attribute => $search]); + } else { + // Case insensitive + $query->where(function (\Cake\Database\Expression\QueryExpression $exp, \Cake\ORM\Query $query) use ($attribute, $search, $sub) { + $lower = $query->func()->lower([ + // https://book.cakephp.org/3/en/orm/query-builder.html#function-arguments + $attribute => 'identifier' + ]); + if($sub) { + return $exp->like($lower, strtolower($search)); + } else { + return $exp->eq($lower, strtolower($search)); + } + }); + } + } + // else not a permitted attribute + + return $query; + } +} diff --git a/app/src/Locale/en_US/default.po b/app/src/Locale/en_US/default.po index e379014d8..37cdc9f85 100644 --- a/app/src/Locale/en_US/default.po +++ b/app/src/Locale/en_US/default.po @@ -366,6 +366,9 @@ msgstr "Description" msgid "match.fd.id" msgstr "ID" +msgid "match.fd.index_display" +msgstr "Display Field in Matchgrid Index" + msgid "match.fd.invalidates" msgstr "Invalidates" diff --git a/app/src/Model/Table/AttributesTable.php b/app/src/Model/Table/AttributesTable.php index 534962bd0..2e1e27d11 100644 --- a/app/src/Model/Table/AttributesTable.php +++ b/app/src/Model/Table/AttributesTable.php @@ -124,6 +124,13 @@ public function validationDefault(Validator $validator) { ); $validator->notEmpty('api_name'); + $validator->add( + 'index_display', + 'toggle', + [ 'rule' => [ 'boolean' ] ] + ); + $validator->notEmpty('index_display'); + $validator->add( 'attribute_group_id', 'content', diff --git a/app/src/Model/Table/MatchgridRecordsTable.php b/app/src/Model/Table/MatchgridRecordsTable.php index 052f15324..52150ba12 100644 --- a/app/src/Model/Table/MatchgridRecordsTable.php +++ b/app/src/Model/Table/MatchgridRecordsTable.php @@ -30,11 +30,13 @@ namespace App\Model\Table; use Cake\ORM\Table; +use Cake\ORM\TableRegistry; class MatchgridRecordsTable extends Table { use \App\Lib\Traits\AutoViewVarsTrait; use \App\Lib\Traits\MatchgridLinkTrait; use \App\Lib\Traits\PrimaryLinkTrait; + use \App\Lib\Traits\SearchFilterTrait; /** * Perform Cake Model initialization. @@ -61,6 +63,23 @@ public function initialize(array $config) { 'fields' => ['keyField' => 'label', 'valueField' => 'label'] ] ]); + + // Use the matchgrid ID to figure out the attribute configuration for + // search filter purposes + + $Matchgrid = TableRegistry::get('Matchgrids'); + + $mgconfig = $Matchgrid->getMatchgridConfig($config['matchgrid_id']); + + foreach($mgconfig->attributes as $attr) { + // XXX for now we permit substring on all fields since we don't have a better way to distinguish + $this->setSearchFilter($attr->name, $attr->case_sensitive, true); + } + + // Also permit search on SOR, SORID, and Reference ID + $this->setSearchFilter('sor', true, false); + $this->setSearchFilter('sorid', true, false); + $this->setSearchFilter('referenceid', true, false); } /** diff --git a/app/src/Template/Attributes/fields.inc b/app/src/Template/Attributes/fields.inc index f10424672..e412c5c62 100644 --- a/app/src/Template/Attributes/fields.inc +++ b/app/src/Template/Attributes/fields.inc @@ -33,6 +33,8 @@ if($action == 'add' || $action == 'edit') { print $this->Field->control('api_name'); + print $this->Field->control('index_display', [], false); + print $this->Field->control('alphanumeric', [], false); print $this->Field->control('case_sensitive', [], false); // CO-1762 diff --git a/app/src/Template/MatchgridRecords/columns.inc b/app/src/Template/MatchgridRecords/columns.inc index 0b572b29e..3c7b42253 100644 --- a/app/src/Template/MatchgridRecords/columns.inc +++ b/app/src/Template/MatchgridRecords/columns.inc @@ -44,4 +44,14 @@ $indexColumns = [ 'referenceid' => [ 'type' => 'echo' ] -]; \ No newline at end of file +]; + +// Add any configured attributes to the display set +foreach($attributes as $attr) { + if($attr->index_display === true) { + $indexColumns[$attr->name] = [ + 'label' => $attr->name, + 'type' => 'echo' + ]; + } +} \ No newline at end of file