From aa73e232b7e6d4f2a9c3120d758c83c17305c6d6 Mon Sep 17 00:00:00 2001 From: Ioannis Igoumenos Date: Sun, 10 Mar 2024 20:50:57 +0200 Subject: [PATCH] fix broken mveaType query.Improve Changelog. --- app/src/Controller/ApiV2Controller.php | 7 +++++- app/src/Lib/Traits/IndexQueryTrait.php | 25 +++++++++++++++----- app/src/Lib/Traits/SearchFilterTrait.php | 5 ++-- app/src/Model/Behavior/ChangelogBehavior.php | 21 ++++++++++++---- app/templates/element/autocompletePeople.php | 20 +++++++++------- app/templates/element/filter.php | 2 +- app/templates/element/mveaJs.php | 2 +- 7 files changed, 58 insertions(+), 24 deletions(-) diff --git a/app/src/Controller/ApiV2Controller.php b/app/src/Controller/ApiV2Controller.php index 8bb133898..6e3114c9f 100644 --- a/app/src/Controller/ApiV2Controller.php +++ b/app/src/Controller/ApiV2Controller.php @@ -305,8 +305,13 @@ public function index() { // $table = the actual table object $table = $this->$modelsName; + $pickerMode = false; + if($this->request->getQuery('picker') !== null + && $this->request->is('restful')) { + $pickerMode = filter_var($this->request->getQuery('picker'), FILTER_VALIDATE_BOOLEAN); + } // Construct the Query - $query = $this->getIndexQuery(); + $query = $this->getIndexQuery($pickerMode); // This magically makes REST calls paginated... can use eg direction=, // sort=, limit=, page= diff --git a/app/src/Lib/Traits/IndexQueryTrait.php b/app/src/Lib/Traits/IndexQueryTrait.php index 3c6d73927..eb8ec2f45 100644 --- a/app/src/Lib/Traits/IndexQueryTrait.php +++ b/app/src/Lib/Traits/IndexQueryTrait.php @@ -57,6 +57,7 @@ public function constructGetIndexContains(Query $query): object { // Examples: // 1. GET https://example.com/registry-pe/api/v2/people?co_id=2&limit=10&extended=PrimaryName,EmailAddresses,Identifiers // 2. GET https://example.com/registry-pe/api/v2/people?co_id=2&limit=10&extended=on + // 2. GET https://example.com/registry-pe/api/v2/people?co_id=2&limit=10&extended=all // 1. GET https://example.com/registry-pe/api/v2/people?co_id=2&limit=10 if($this->request->is('restful')|| $this->request->is('ajax')) { // Restfull and ajax do not include the IndexContains by default. @@ -67,9 +68,20 @@ public function constructGetIndexContains(Query $query): object { filter_var($this->request->getQuery('extended'), FILTER_VALIDATE_BOOLEAN) ) { $containClause = $table->getIndexContains(); - } elseif($this->request->getQuery('extended') - && \is_string($this->request->getQuery('extended'))) { - // This is a string. We will parse the csv and continue + } elseif( + $this->request->getQuery('extended') && + $this->request->getQuery('extended') === 'all' + ) { + // Get all the associated models + $associations = $table->associations(); + foreach($associations->getIterator() as $a) { + $containClause[] = $a->getName(); + } + } elseif ( + $this->request->getQuery('extended') + && \is_string($this->request->getQuery('extended')) + ) { + // Get ONLY the associated models requested $associations = $table->associations(); $containQueryList = str_getcsv($this->request->getQuery('extended')); foreach($associations->getIterator() as $a) { @@ -86,11 +98,12 @@ public function constructGetIndexContains(Query $query): object { /** * Build the Index Query * + * @params boolean $mode True for OR and False for AND. AND is the default behavior * * @return object Cake ORM Query object * @since COmanage Registry v5.0.0 */ - public function getIndexQuery(): object { + public function getIndexQuery(bool $mode = false): object { // $this->name = Models $modelsName = $this->name; // $table = the actual table object @@ -101,9 +114,9 @@ public function getIndexQuery(): object { $query = $table->find($this->paginate['finder'] ?? 'all'); // Get a pointer to my expressions list $newexp = $query->newExpr(); - // The searchable attributes can have an AND or an OR conjuction. The first one is used from the filtering block + // The searchable attributes can have an AND or an OR conjunction. The first one is used from the filtering block // while the second one from the picker vue module. - $newexp = $newexp->setConjunction(true ? 'AND' : 'OR'); + $newexp = $newexp->setConjunction($mode ? 'OR' : 'AND'); if(!empty($link->attr) && !empty($link->value)) { // If a link attribute is defined but no value is provided, then query diff --git a/app/src/Lib/Traits/SearchFilterTrait.php b/app/src/Lib/Traits/SearchFilterTrait.php index 1e0210952..39a2e6c93 100644 --- a/app/src/Lib/Traits/SearchFilterTrait.php +++ b/app/src/Lib/Traits/SearchFilterTrait.php @@ -68,8 +68,9 @@ public function addJoins(Query $query, string $attribute, ServerRequest $request 'table' => $mtable_name, 'conditions' => [ $mtable_alias . '.' . $fk . '=' . $this->_alias . '.id', - $mtable_alias . '.' . 'deleted IS NOT TRUE', - $mtable_alias . '.' . $changelog_fk . ' IS NULL' +// XXX Moved to changelong Behavior +// $mtable_alias . '.' . 'deleted IS NOT TRUE', +// $mtable_alias . '.' . $changelog_fk . ' IS NULL' ], 'type' => 'INNER' ]]); diff --git a/app/src/Model/Behavior/ChangelogBehavior.php b/app/src/Model/Behavior/ChangelogBehavior.php index be2afabee..4ac8f3ff1 100644 --- a/app/src/Model/Behavior/ChangelogBehavior.php +++ b/app/src/Model/Behavior/ChangelogBehavior.php @@ -29,7 +29,10 @@ namespace App\Model\Behavior; +use Cake\Datasource\EntityInterface; +use Cake\Event\Event; use Cake\ORM\Behavior; +use Cake\ORM\Query; use Cake\Utility\Inflector; class ChangelogBehavior extends Behavior @@ -44,7 +47,7 @@ class ChangelogBehavior extends Behavior * @return boolean True on success */ - public function beforeDelete(\Cake\Event\Event $event, $entity, \ArrayObject $options) { + public function beforeDelete(Event $event, $entity, \ArrayObject $options) { if(isset($options['useHardDelete']) && $options['useHardDelete']) { // Hard delete requested, so just return return true; @@ -74,7 +77,7 @@ public function beforeDelete(\Cake\Event\Event $event, $entity, \ArrayObject $op // But return success return true; } - + /** * Adjust find query conditions for changelog. * @@ -85,7 +88,7 @@ public function beforeDelete(\Cake\Event\Event $event, $entity, \ArrayObject $op * @param boolean $primary Whether or not this is the root query (vs an associated query) */ - public function beforeFind(\Cake\Event\Event $event, \Cake\ORM\Query $query, \ArrayObject $options, bool $primary) { + public function beforeFind(Event $event, Query $query, \ArrayObject $options, bool $primary) { if(isset($options['archived']) && $options['archived']) { // Archived records requested (including possiblf expunge), so just return return true; @@ -100,7 +103,15 @@ public function beforeFind(\Cake\Event\Event $event, \Cake\ORM\Query $query, \Ar // XXX add support for archived, revision, etc // XXX if specific id is requested, do not modify query - + + if(!empty($query->clause('join'))) { + foreach($query->clause('join') as $mdl => $opts) { + $ascParentfk = Inflector::singularize($opts['table']) . "_id"; + + $query->where([$opts['alias'] . '.deleted IS NOT true']) + ->where([$opts['alias'] . '.' . $ascParentfk . ' IS NULL']); + } + } // We use IS NOT TRUE to check for null || false, since pre-Changelog data // may have null instead of false. // (Alternately we could join two clauses for false || IS NULL.) @@ -121,7 +132,7 @@ public function beforeFind(\Cake\Event\Event $event, \Cake\ORM\Query $query, \Ar * @param ArrayObject $options Options */ - public function beforeSave(\Cake\Event\Event $event, \Cake\Datasource\EntityInterface $entity, \ArrayObject $options) { + public function beforeSave(Event $event, EntityInterface $entity, \ArrayObject $options) { // XXX prevent updates to deleted and archived records // Cake Book suggests doing this with Application Rules... can we define those in the Behavior? // or perhaps in beforeMarshal? https://book.cakephp.org/3.0/en/orm/saving-data.html#modifying-request-data-before-building-entities diff --git a/app/templates/element/autocompletePeople.php b/app/templates/element/autocompletePeople.php index e80c984cf..0f735c068 100644 --- a/app/templates/element/autocompletePeople.php +++ b/app/templates/element/autocompletePeople.php @@ -26,9 +26,9 @@ */ // Get parameters - $fieldName = $fieldName ?? ""; - $type = $type ?? ""; - $htmlId = $htmlId ?? ""; + $fieldName = $fieldName ?? ''; + $type = $type ?? ''; + $htmlId = $htmlId ?? ''; // Get the CSRF Token in JavaScript $token = $this->request->getAttribute('csrfToken'); @@ -54,13 +54,17 @@ minLength: 2, // XXX probably should be set by config and default to 3 htmlId: '' }, - error: '', - core: { - webroot: 'request->getAttribute('webroot') ?>' - }, - txt: JSON.parse('locales()) ?>') + error: '' + coId: } }, + provide: { + txt: JSON.parse('locales()) ?>') + api: { + webroot: 'request->getAttribute('webroot') ?>', + searchPeople: `request->getAttribute('webroot') ?>/api/ajax/v2/people?co_id=?PrimaryName,Identifiers,EmailAddresses&picker=on`, + } + } components: { AutocompletePeople }, diff --git a/app/templates/element/filter.php b/app/templates/element/filter.php index 513555371..f01116d52 100644 --- a/app/templates/element/filter.php +++ b/app/templates/element/filter.php @@ -246,7 +246,7 @@
Form->label(!empty($columns[$key]['label']) ? $columns[$key]['label'] : $key); + print $this->Form->label($options['label'] ?? $key); print $this->Form->checkbox($key, [ 'id' => str_replace("_", "-", $key), 'class' => 'form-check-input', diff --git a/app/templates/element/mveaJs.php b/app/templates/element/mveaJs.php index bb8511dcd..3ae2e54b5 100644 --- a/app/templates/element/mveaJs.php +++ b/app/templates/element/mveaJs.php @@ -78,7 +78,7 @@ var entityTypeIdRef = entityType + '_id'; let url = 'Url->build(['controller' => 'api/ajax', 'action' => 'v2']) - ?>/' + mveaType + '?' + entityTypeIdRef + '=&extended'; + ?>/' + mveaType + '?' + entityTypeIdRef + '=&extended=all'; let xhr = callRegistryAPI( url, 'GET',