From 99565b4bb7c632064c094347ebd36646f48d29e0 Mon Sep 17 00:00:00 2001 From: robiinho Date: Tue, 14 May 2024 14:37:39 +0100 Subject: [PATCH 1/5] Create SearchBuilderCriteriaProvider.php Allow the usage of SearchBuilder plugin, and deal with DateTimeInterface Format . # Use: 'criteria' => [ new SearchBuilderCriteriaProvider($request), new SearchCriteriaProvider(), ], Doctrine extension are needed: https://github.com/beberlei/DoctrineExtensions/tree/master # To enable extensions orm: dql: datetime_functions: date: DoctrineExtensions\Query\Mysql\Date year: DoctrineExtensions\Query\Mysql\Year yearmonth: DoctrineExtensions\Query\Mysql\YearMonth --- .../ORM/SearchBuilderCriteriaProvider.php | 218 ++++++++++++++++++ 1 file changed, 218 insertions(+) create mode 100644 src/Adapter/Doctrine/ORM/SearchBuilderCriteriaProvider.php diff --git a/src/Adapter/Doctrine/ORM/SearchBuilderCriteriaProvider.php b/src/Adapter/Doctrine/ORM/SearchBuilderCriteriaProvider.php new file mode 100644 index 0000000..5165621 --- /dev/null +++ b/src/Adapter/Doctrine/ORM/SearchBuilderCriteriaProvider.php @@ -0,0 +1,218 @@ + '=', + '>' => '>', + '>=' => '>=', + '<' => '<', + '<=' => '<=', + '!=' => '!=', + 'starts' => 'LIKE', + '!starts' => 'NOT LIKE', + 'contains' => 'LIKE', + '!contains' => 'NOT LIKE', + 'ends' => 'LIKE', + '!ends' => 'NOT LIKE', + 'null' => 'IS NULL', + '!null' => 'IS NOT NULL', + 'between' => 'between', + '!between' => 'not between', + ]; + + public function __construct($request) { + $this->request = $request; + } + + public function process(QueryBuilder $queryBuilder, DataTableState $state): void { + $this->processSearchBuilder($queryBuilder, $state); + } + + public function processSearchBuilder(QueryBuilder $queryBuilder, DataTableState $state): void { + + if ($this->request->get('searchBuilder')) { + + $searchBuilder = $this->request->get('searchBuilder'); + if ($searchBuilder) { + $comparisons = $this->searchBuilder($queryBuilder, $state, $searchBuilder); + if ($comparisons !== null) { + $queryBuilder->andWhere($comparisons); + } + } + } + } + + public function searchBuilder(QueryBuilder $queryBuilder, DataTableState $state, $searchBuilder) { + + + $expr = $queryBuilder->expr(); + + $sbLogic = []; + $logic = $searchBuilder['logic'] ?? "AND"; + if ($logic == "AND") { + $comparisons = $expr->andX(); + } else { + $comparisons = $expr->orX(); + } + $logicValid = in_array($logic, ['AND', "OR"]); + + if ($logicValid && isset($searchBuilder['criteria'])) { + foreach ($searchBuilder['criteria'] as $rule) { + + $col = $rule['origData'] ?? null; + + $searchTerm = (!in_array($rule['condition'] ?? null, ['null', '!null'])) ? $rule['value1'] ?? false : true; + + // If criteria is defined then this must be a group + if (isset($rule['criteria'])) { + + $comparisons->add($this->searchBuilder($queryBuilder, $state, $rule)); + } elseif ($col && $searchTerm && array_key_exists($rule['condition'] ?? null, $this->sbRules) && (null !== $state->getDataTable()->getColumnByName($col))) { + + + + $column = $state->getDataTable()->getColumnByName($col); + + if ($rule['condition'] === 'starts' || $rule['condition'] === '!starts') { + $searchTerm = $searchTerm . '%'; + } elseif ($rule['condition'] === 'ends' || $rule['condition'] === '!ends') { + $searchTerm = '%' . $searchTerm; + } elseif ($rule['condition'] === 'contains' || $rule['condition'] === '!contains') { + $searchTerm = '%' . $searchTerm . '%'; + } elseif ($rule['condition'] === 'between' || $rule['condition'] === '!between') { + if (preg_match("/^[0-9]{4}-[0-9]{1,2}-[0-9]{1,2}$/", $rule['value1'])) { + $date2 = $rule['value2'] ?? $rule['value1']; + $searchTerm = [$rule['value1'] . " 00:00:00", $date2 . " 23:59:59"]; + } else { + $searchTerm = [$rule['value1'], $rule['value2'] ?? null]; + } + } + $col = (!empty($state->getDataTable()->getColumnByName($col)->getField())) ? $state->getDataTable()->getColumnByName($col)->getField() ?? $col : $col; + $sbLogic[] = [$col, $this->sbRules[$rule['condition'] ?? null], $searchTerm, $column]; + } + } + if ($sbLogic) { + + foreach ($sbLogic as $r) { + $column = $r[3]; + $operator = $r[1]; + $search = $r[2]; + + if ($column instanceof DateTimeColumn) { + if (is_array($search)) { + foreach ($search as $s) { + $paramsDate = $this->validateDateDatatable($s); + } + } else { + $paramsDate = $this->validateDateDatatable($search); + } + if ($paramsDate !== null) { + $column->setOption('leftExpr', $paramsDate["leftExpr"]); + $column->setOption('rightExpr', $paramsDate['rightExpr']); + } + } + + if ($r[1] == 'between') { + + $comparisons_between = $expr->andX(); + $comparisons_between->add(new Comparison($column->getLeftExpr(), '>=', + $expr->literal($search[0]))); + $comparisons_between->add(new Comparison($column->getLeftExpr(), '<=', + $expr->literal($search[1]))); + + $comparisons->add($comparisons_between); + } elseif ($r[1] == 'not between') { + $comparisons_between = $expr->orX(); + $comparisons_between->add(new Comparison($column->getLeftExpr(), '<', + $expr->literal($search[0]))); + $comparisons_between->add(new Comparison($column->getLeftExpr(), '>', + $expr->literal($search[1]))); + + $comparisons->add($comparisons_between); + } elseif ($r[1] == 'IS NULL') { + $comparisons->add(new Comparison($column->getLeftExpr(), 'IS', 'NULL')); + } elseif ($r[1] == 'IS NOT NULL') { + $comparisons->add(new Comparison($column->getLeftExpr(), 'IS NOT', 'NULL')); + } else { + if ($column instanceof DateTimeColumn) { + $comparisons->add(new Comparison($column->getLeftExpr(), $operator, + $expr->literal($column->getRightExpr($search)))); + } else { + $comparisons->add(new Comparison($column->getLeftExpr(), $operator, + $expr->literal($search))); + } + } + } + return $comparisons; + } + } + } + + function isValidDate($date, $format = 'Y-m-d') { + $dateTime = \DateTime::createFromFormat($format, $date); + return $dateTime && $dateTime->format($format) === $date; + } + + public function validateDateDatatable($value) { + + if (is_string($value)) { + $value = trim($value); + + if ($this->isValidDate($value, "Y")) { + return [ + 'leftExpr' => function ($arg) { + return 'YEAR(' . $arg . ')'; + }, + 'rightExpr' => $value + ]; + } elseif ($this->isValidDate($value, "d-m-Y")) { + $dateString = date_format(\DateTime::createFromFormat('d-m-Y', $value), 'Y-m-d'); + return [ + 'leftExpr' => function ($arg) { + return 'DATE(' . $arg . ')'; + }, + 'rightExpr' => $dateString]; + } elseif ($this->isValidDate($value, "Y-m-d")) { + $dateString = date_format(\DateTime::createFromFormat('Y-m-d', $value), 'Y-m-d'); + return [ + 'leftExpr' => function ($arg) { + return 'DATE(' . $arg . ')'; + }, + 'rightExpr' => $dateString]; + } elseif ($this->isValidDate($value, "Y-m")) { + $dateString = date_format(\DateTime::createFromFormat('Y-m', $value), 'Ym'); + return [ + 'leftExpr' => function ($arg) { + return 'YEARMONTH(' . $arg . ')'; + }, + 'rightExpr' => $dateString + ]; + } elseif ($this->isValidDate($value, "m-Y")) { + $dateString = date_format(\DateTime::createFromFormat('m-Y', $value), 'Ym'); + return [ + 'leftExpr' => function ($arg) { + return 'YEARMONTH(' . $arg . ')'; + }, + 'rightExpr' => $dateString + ]; + } else + return null; + } else + return null; + } +} From 77a20f320fab46d2f13f076aee170b0f8e60b159 Mon Sep 17 00:00:00 2001 From: robiinho Date: Tue, 14 May 2024 14:42:45 +0100 Subject: [PATCH 2/5] Update index.html.md --- docs/source/index.html.md | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/docs/source/index.html.md b/docs/source/index.html.md index 6d45f2f..936a336 100644 --- a/docs/source/index.html.md +++ b/docs/source/index.html.md @@ -296,6 +296,25 @@ Note that implementing your own criteria overrides the default, meaning searchin longer work automatically. Add the `SearchCriteriaProvider` manually to combine the default behavior with your own implementation. +### SearchBuilder criteria +``` + 'criteria' => [ + new SearchBuilderCriteriaProvider($request), + new SearchCriteriaProvider(), + ], +``` +Doctrine extension are needed: https://github.com/beberlei/DoctrineExtensions/tree/master + +To enable extensions : config/packages/doctrines.yaml +``` + orm: + dql: + datetime_functions: + date: DoctrineExtensions\Query\Mysql\Date + year: DoctrineExtensions\Query\Mysql\Year + yearmonth: DoctrineExtensions\Query\Mysql\YearMonth +``` + ### Events ```php?start_inline=1 From 2c2a1c8bf2e65fc628015481945826f354590804 Mon Sep 17 00:00:00 2001 From: robiinho Date: Tue, 14 May 2024 14:43:26 +0100 Subject: [PATCH 3/5] Update index.html.md --- docs/source/index.html.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/source/index.html.md b/docs/source/index.html.md index 936a336..19a7b32 100644 --- a/docs/source/index.html.md +++ b/docs/source/index.html.md @@ -297,7 +297,9 @@ longer work automatically. Add the `SearchCriteriaProvider` manually to combine with your own implementation. ### SearchBuilder criteria -``` +``` +use App\Datatable\SearchBuilderCriteriaProvider; + 'criteria' => [ new SearchBuilderCriteriaProvider($request), new SearchCriteriaProvider(), From e604d8eb50a5191c8ae373d1f2705d78541a75cf Mon Sep 17 00:00:00 2001 From: robiinho Date: Wed, 15 May 2024 10:08:37 +0100 Subject: [PATCH 4/5] Update SearchBuilderCriteriaProvider.php --- src/Adapter/Doctrine/ORM/SearchBuilderCriteriaProvider.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Adapter/Doctrine/ORM/SearchBuilderCriteriaProvider.php b/src/Adapter/Doctrine/ORM/SearchBuilderCriteriaProvider.php index 5165621..bf9e3d4 100644 --- a/src/Adapter/Doctrine/ORM/SearchBuilderCriteriaProvider.php +++ b/src/Adapter/Doctrine/ORM/SearchBuilderCriteriaProvider.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace App\Datatable; +namespace Omines\DataTablesBundle\Adapter\Doctrine\ORM; use Doctrine\ORM\Query\Expr\Comparison; use Doctrine\ORM\QueryBuilder; From f5262414100286c5750a6bee61d60a0c6604c903 Mon Sep 17 00:00:00 2001 From: robiinho Date: Wed, 15 May 2024 10:11:11 +0100 Subject: [PATCH 5/5] Update index.html.md use Omines\DataTablesBundle\Adapter\Doctrine\ORM\SearchbuilderCriteriaProvider.php --- docs/source/index.html.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/source/index.html.md b/docs/source/index.html.md index 19a7b32..be5f91b 100644 --- a/docs/source/index.html.md +++ b/docs/source/index.html.md @@ -298,7 +298,7 @@ with your own implementation. ### SearchBuilder criteria ``` -use App\Datatable\SearchBuilderCriteriaProvider; +use Omines\DataTablesBundle\Adapter\Doctrine\ORM\SearchBuilderCriteriaProvider; 'criteria' => [ new SearchBuilderCriteriaProvider($request),