diff --git a/app/src/Controller/StandardController.php b/app/src/Controller/StandardController.php index b4883090d..91f145147 100644 --- a/app/src/Controller/StandardController.php +++ b/app/src/Controller/StandardController.php @@ -572,27 +572,24 @@ public function index() { // AutoViewVarsTrait $this->populateAutoViewVars(); - + + $query = $table->find($this->paginate['finder'] ?? 'all'); if(!empty($link->attr)) { // If a link attribute is defined but no value is provided, then query // where the link attribute is NULL // "all" is the default finder. But since we are utilizing the paginator here, we will check the configuration // for any custom finder. - $query = $table->find( - $this->paginate['finder'] ?? "all" - )->where([$table->getAlias().'.'.$link->attr => $link->value]); - } else { - $query = $table->find($this->paginate['finder'] ?? "all"); + $query = $query->where([$table->getAlias().'.'.$link->attr => $link->value]); } // QueryModificationTrait - if(method_exists($table, "getIndexContains") + if(method_exists($table, 'getIndexContains') && $table->getIndexContains()) { $query->contain($table->getIndexContains()); } // SearchFilterTrait - if(method_exists($table, "getSearchableAttributes")) { + if(method_exists($table, 'getSearchableAttributes')) { $searchableAttributes = $table->getSearchableAttributes($this->name, $this->viewBuilder()->getVar('vv_tz')); if(!empty($searchableAttributes)) { @@ -600,12 +597,12 @@ public function index() { foreach($searchableAttributes as $attribute => $options) { if(!empty($this->request->getQuery($attribute))) { $query = $table->whereFilter($query, $attribute, $this->request->getQuery($attribute)); - } elseif (!empty($this->request->getQuery($attribute . "_starts_at")) - || !empty($this->request->getQuery($attribute . "_ends_at"))) { + } elseif (!empty($this->request->getQuery($attribute . '_starts_at')) + || !empty($this->request->getQuery($attribute . '_ends_at'))) { $search_date = []; // We allow empty for dates since we might refer to infinity (from whenever or to always) - $search_date[] = $this->request->getQuery($attribute . "_starts_at") ?? ""; - $search_date[] = $this->request->getQuery($attribute . "_ends_at") ?? ""; + $search_date[] = $this->request->getQuery($attribute . '_starts_at') ?? ''; + $search_date[] = $this->request->getQuery($attribute . '_ends_at') ?? ''; $query = $table->whereFilter($query, $attribute, $search_date); } } diff --git a/app/src/Lib/Traits/SearchFilterTrait.php b/app/src/Lib/Traits/SearchFilterTrait.php index d90440646..6b6ad17f6 100644 --- a/app/src/Lib/Traits/SearchFilterTrait.php +++ b/app/src/Lib/Traits/SearchFilterTrait.php @@ -29,6 +29,8 @@ namespace App\Lib\Traits; +use Cake\Database\Expression\QueryExpression; +use Cake\ORM\Query; use Cake\Utility\Inflector; use Cake\I18n\FrozenTime; @@ -136,15 +138,15 @@ public function setFilterConfig(array $filterConfig): void { /** * Build a query where() clause for the configured attribute. * - * @param \Cake\ORM\Query $query Cake ORM Query object + * @param Query $query Cake ORM Query object * @param string $attribute Attribute to filter on (database name) * @param string|array $q Value to filter on * - * @return \Cake\ORM\Query Cake ORM Query object + * @return Query Cake ORM Query object * @since COmanage Registry v5.0.0 */ - public function whereFilter(\Cake\ORM\Query $query, string $attribute, string|array $q): object { + public function whereFilter(Query $query, string $attribute, string|array $q): object { // not a permitted attribute if(empty($this->searchFilters[$attribute])) { return $query; @@ -175,48 +177,47 @@ public function whereFilter(\Cake\ORM\Query $query, string $attribute, string|ar $sub = false; // Primitive types $search_types = ['integer', 'boolean']; - if( $this->searchFilters[$attribute]['type'] == "string") { - $search = "%" . $search . "%"; + if( $this->searchFilters[$attribute]['type'] === 'string') { + $search = '%' . $search . '%'; $sub = true; // Search type } elseif(in_array($this->searchFilters[$attribute]['type'], $search_types, true)) { return $query->where([$attributeWithModelPrefix => $search]); // Date - } elseif($this->searchFilters[$attribute]['type'] == "date") { + } elseif($this->searchFilters[$attribute]['type'] === 'date') { // Parse the date string with FrozenTime to improve error handling return $query->where([$attributeWithModelPrefix => FrozenTime::parseDate($search, 'y-M-d')]); // Timestamp - } elseif( $this->searchFilters[$attribute]['type'] == "timestamp") { + } elseif( $this->searchFilters[$attribute]['type'] === 'timestamp') { // Date between dates if(!empty($search[0]) && !empty($search[1])) { - return $query->where(function (\Cake\Database\Expression\QueryExpression $exp, \Cake\ORM\Query $query) use ($attributeWithModelPrefix, $search) { - return $exp->between($attributeWithModelPrefix, "'" . $search[0] . "'", "'" . $search[1] . "'"); - }); - // The starts at is non-empty. So the data should be greater than the starts_at date + return $query->where(fn(QueryExpression $exp, Query $query) => $exp->between($attributeWithModelPrefix, "'" . $search[0] . "'", "'" . $search[1] . "'")); + // The starts_at is non-empty. So the data should be greater than the starts_at date } elseif(!empty($search[0]) && empty($search[1])) { - return $query->where(function (\Cake\Database\Expression\QueryExpression $exp, \Cake\ORM\Query $query) use ($attributeWithModelPrefix, $search) { - return $exp->gte("'" . FrozenTime::parse($search[0]) . "'", $attributeWithModelPrefix); - }); - // The ends at is non-empty. So the data should be less than the ends at date + return $query->where(fn(QueryExpression $exp, Query $query) => $exp->gte("'" . FrozenTime::parse($search[0]) . "'", $attributeWithModelPrefix)); + // The ends_at is non-empty. So the data should be less than the ends_at date } elseif(!empty($search[1]) && empty($search[0])) { - return $query->where(function (\Cake\Database\Expression\QueryExpression $exp, \Cake\ORM\Query $query) use ($attributeWithModelPrefix, $search) { - return $exp->lte("'" . FrozenTime::parse($search[1]) . "'", $attributeWithModelPrefix); - }); + return $query->where(fn(QueryExpression $exp, Query $query) => $exp->lte("'" . FrozenTime::parse($search[1]) . "'", $attributeWithModelPrefix)); } else { // We return everything return $query; } - } + // Use the `lower` function to apply uniformity for the search globally + $lower = $query->func()->lower([$attributeWithModelPrefix => 'identifier']); + + // This is the case of the filtering block. We are `and`-ing the conditions + // The like function applies to all string searches + // The eq function applies to everything else + $conditionsCallback = static fn(QueryExpression $exp, Query $query) => + ($sub) ? $exp->like($lower, strtolower($search)) + : $exp->eq($lower, strtolower($search)); + // String values - return $query->where(function (\Cake\Database\Expression\QueryExpression $exp, \Cake\ORM\Query $query) use ($attributeWithModelPrefix, $search, $sub) { - $lower = $query->func()->lower([$attributeWithModelPrefix => 'identifier']); - return ($sub) ? $exp->like($lower, strtolower($search)) - : $exp->eq($lower, strtolower($search)); - }); + return $query->where($conditionsCallback); } }