From 178c54915180cd51c201bb5dd2584e3923b1c514 Mon Sep 17 00:00:00 2001 From: noelma Date: Sat, 4 Dec 2021 20:53:37 +0100 Subject: [PATCH] feat: adding group methods to the where object --- src/RequestHandler.php | 54 ++++++++++++++++--------- src/RequestInterface.php | 12 +++--- src/Where.php | 6 +-- src/WhereHandler.php | 82 +++++++++++++++++++++++--------------- tests/unit/RequestTest.php | 60 +++++++++++++++++++++++++--- 5 files changed, 148 insertions(+), 66 deletions(-) diff --git a/src/RequestHandler.php b/src/RequestHandler.php index fffa18e..62d345d 100644 --- a/src/RequestHandler.php +++ b/src/RequestHandler.php @@ -17,10 +17,10 @@ * * @author Mathieu NOËL * - * @method Request where(\Closure|string $column, null|string $operator = null, null|numeric|string $value = null) Alias de la fonction de l'objet Queryflatfile\Where - * @method Request notWhere(\Closure|string $column, null|string $operator = null, null|numeric|string $value = null) Alias de la fonction de l'objet Queryflatfile\Where - * @method Request orWhere(\Closure|string $column, null|string $operator = null, null|numeric|string $value = null) Alias de la fonction de l'objet Queryflatfile\Where - * @method Request orNotWhere(\Closure|string $column, null|string $operator = null, null|numeric|string $value = null) Alias de la fonction de l'objet Queryflatfile\Where + * @method Request where(string $column, string $operator, null|scalar $value) Alias de la fonction de l'objet Queryflatfile\Where + * @method Request notWhere(string $column, string $operator, null|scalar $value) Alias de la fonction de l'objet Queryflatfile\Where + * @method Request orWhere(string $column, string $operator, null|scalar $value) Alias de la fonction de l'objet Queryflatfile\Where + * @method Request orNotWhere(string $column, string $operator, null|scalar $value) Alias de la fonction de l'objet Queryflatfile\Where * * @method Request between(string $column, numeric|string $min, numeric|string $max) Alias de la fonction de l'objet Queryflatfile\Where * @method Request orBetween(string $column, numeric|string $min, numeric|string $max) Alias de la fonction de l'objet Queryflatfile\Where @@ -41,6 +41,11 @@ * @method Request orRegex(string $column, string $pattern) Alias de la fonction de l'objet Queryflatfile\Where * @method Request notRegex(string $column, string $pattern) Alias de la fonction de l'objet Queryflatfile\Where * @method Request orNotRegex(string $column, string $pattern) Alias de la fonction de l'objet Queryflatfile\Where + * + * @method Request whereGroup(\Closure $callable) Alias de la fonction de l'objet Queryflatfile\Where + * @method Request notWhereGroup(\Closure $callable) Alias de la fonction de l'objet Queryflatfile\Where + * @method Request orWhereGroup(\Closure $callable) Alias de la fonction de l'objet Queryflatfile\Where + * @method Request orNotWhereGroup(\Closure $callable) Alias de la fonction de l'objet Queryflatfile\Where */ abstract class RequestHandler implements RequestInterface { @@ -187,8 +192,13 @@ public function insertInto(string $table, array $columns) /** * {@inheritdoc} */ - public function leftJoin(string $table, $column, ?string $operator = null, ?string $value = null) + public function leftJoin(string $table, $column, string $operator = '', $value = null) { + if ($column instanceof \Closure) { + $this->joinGroup(self::JOIN_LEFT, $table, $column); + + return $this; + } $this->join(self::JOIN_LEFT, $table, $column, $operator, $value); return $this; @@ -219,8 +229,13 @@ public function orderBy(string $columns, int $order = SORT_ASC) /** * {@inheritdoc} */ - public function rightJoin(string $table, $column, ?string $operator = null, ?string $value = null) + public function rightJoin(string $table, $column, string $operator = '', $value = null) { + if ($column instanceof \Closure) { + $this->joinGroup(self::JOIN_RIGHT, $table, $column); + + return $this; + } $this->join(self::JOIN_RIGHT, $table, $column, $operator, $value); return $this; @@ -302,22 +317,23 @@ protected function init() /** * Enregistre une jointure. * - * @param string $type Type de la jointure. - * @param string $table Nom de la table à joindre - * @param string|\Closure $column Nom de la colonne d'une des tables précédentes - * ou une closure pour affiner les conditions. - * @param string|null $operator Opérateur logique ou null pour une closure. - * @param string|null $value Valeur - * ou une colonne de la table jointe (au format nom_table.colonne) - * ou null pour une closure. + * @param string $type Type de la jointure. + * @param string $table Nom de la table à joindre + * @param string $column Nom de la colonne d'une des tables précédentes. + * @param string $operator Opérateur logique ou null pour une closure. + * @param null|scalar $value Valeur ou une colonne de la table jointe (au format nom_table.colonne) */ - private function join(string $type, string $table, $column, ?string $operator = null, ?string $value = null): void + private function join(string $type, string $table, string $column, string $operator, $value): void { - $where = new Where(); + $where = (new Where())->where($column, $operator, $value); - $column instanceof \Closure - ? call_user_func_array($column, [ &$where ]) - : $where->where($column, $operator, $value); + $this->joins[] = compact('type', 'table', 'where'); + } + + private function joinGroup(string $type, string $table, \Closure $callable): void + { + $where = new Where(); + call_user_func_array($callable, [ &$where ]); $this->joins[] = compact('type', 'table', 'where'); } diff --git a/src/RequestInterface.php b/src/RequestInterface.php index 2e47425..279d91d 100644 --- a/src/RequestInterface.php +++ b/src/RequestInterface.php @@ -70,14 +70,14 @@ public function from(string $table); * @param string $table Nom de la table à joindre. * @param string|\Closure $column Nom de la colonne d'une des tables précédentes * ou une closure pour affiner les conditions. - * @param string|null $operator Opérateur logique ou null pour une closure. - * @param string|null $value Valeur + * @param string $operator Opérateur logique ou null pour une closure. + * @param scalar|null $value Valeur * ou une colonne de la table jointe (au format nom_table.colonne) * ou null pour une closure. * * @return $this */ - public function leftJoin(string $table, $column, ?string $operator = null, ?string $value = null); + public function leftJoin(string $table, $column, string $operator = '', $value = null); /** * Enregistre une jointure droite. @@ -85,14 +85,14 @@ public function leftJoin(string $table, $column, ?string $operator = null, ?stri * @param string $table Nom de la table à joindre * @param string|\Closure $column Nom de la colonne d'une des tables précédentes * ou une closure pour affiner les conditions. - * @param string|null $operator Opérateur logique ou null pour une closure. - * @param string|null $value Valeur + * @param string $operator Opérateur logique ou null pour une closure. + * @param scalar|null $value Valeur * ou une colonne de la table jointe (au format nom_table.colonne) * ou null pour une closure. * * @return $this */ - public function rightJoin(string $table, $column, ?string $operator = null, ?string $value = null); + public function rightJoin(string $table, $column, string $operator = '', $value = null); /** * Enregistre une limitation et un décalage au retour de la requête. diff --git a/src/Where.php b/src/Where.php index d68ad08..1bdfc03 100644 --- a/src/Where.php +++ b/src/Where.php @@ -60,7 +60,7 @@ public function __toString(): string ); break; - case 'whereCallback': + case 'whereGroup': $output .= sprintf('%s(%s) ', $not, $where[ 'value' ]); break; @@ -121,7 +121,7 @@ public function execute(array $row): bool $output = true; foreach ($this->where as $key => $value) { /* Si la clause est standard ou une sous clause. */ - $predicate = $value[ 'type' ] === 'whereCallback' + $predicate = $value[ 'type' ] === 'whereGroup' ? $value[ 'value' ]->execute($row) : self::predicate($row[ $value[ 'column' ] ], $value[ 'condition' ], $value[ 'value' ]); /* Si la clause est inversé. */ @@ -161,7 +161,7 @@ public function executeJoin(array $row, array &$rowTable): bool foreach ($this->where as $key => $value) { $predicate = true; - if ($value[ 'type' ] === 'whereCallback') { + if ($value[ 'type' ] === 'whereGroup') { $predicate = $value[ 'value' ]->executeJoin($row, $rowTable); } else { $val = $rowTable[ self::getColumn($value[ 'value' ]) ]; diff --git a/src/WhereHandler.php b/src/WhereHandler.php index c05d67b..65071ae 100644 --- a/src/WhereHandler.php +++ b/src/WhereHandler.php @@ -49,27 +49,21 @@ class WhereHandler * Ajoute une condition simple pour la requête. * Si la valeur du champ est égal (non égale, supérieur à, ...) par rapport à une valeur. * - * @param \Closure|string $column Sous condition ou une colonne. - * @param null|string $operator Type de condition. - * @param null|numeric|string $value Valeur de teste. - * @param string $bool Porte logique de la condition (and|or). - * @param bool $not Inverse la condition. + * @param string $column Nom d'une colonne. + * @param string $operator Type de condition. + * @param null|scalar $value Valeur de teste. + * @param string $bool Porte logique de la condition (and|or). + * @param bool $not Inverse la condition. * * @throws OperatorNotFound The condition is not exist. */ public function where( - $column, - ?string $operator = null, - $value = null, + string $column, + string $operator, + $value, string $bool = self::EXP_AND, bool $not = false ): self { - if ($column instanceof \Closure) { - $this->whereCallback($column, $bool, $not); - - return $this; - } - $condition = $this->filterOperator($operator); if (in_array($condition, [ 'like', 'ilike', 'not like', 'not ilike' ])) { @@ -97,10 +91,9 @@ public function where( /** * Alias inverse de la fonction where(). * - * @param \Closure|string $column Sous condition ou une colonne. - * @param null|numeric|string $value Valeur de teste. + * @param null|scalar $value Valeur de teste. */ - public function notWhere($column, ?string $operator = null, $value = null): self + public function notWhere(string $column, string $operator, $value): self { $this->where($column, $operator, $value, self::EXP_AND, true); @@ -110,10 +103,9 @@ public function notWhere($column, ?string $operator = null, $value = null): self /** * Alias avec la porte logique 'OR' de la fonction where(). * - * @param \Closure|string $column Sous condition ou une colonne. - * @param null|numeric|string $value Valeur de teste. + * @param null|scalar $value Valeur de teste. */ - public function orWhere($column, ?string $operator = null, $value = null): self + public function orWhere(string $column, string $operator, $value): self { $this->where($column, $operator, $value, self::EXP_OR); @@ -123,10 +115,9 @@ public function orWhere($column, ?string $operator = null, $value = null): self /** * Alias inverse avec la porte logique 'OR' de la fonction where(). * - * @param \Closure|string $column Sous condition ou une colonne. - * @param null|numeric|string $value Valeur de teste. + * @param null|scalar $value Valeur de teste. */ - public function orNotWhere($column, ?string $operator = null, $value = null): self + public function orNotWhere(string $column, string $operator, $value): self { $this->where($column, $operator, $value, self::EXP_OR, true); @@ -358,13 +349,13 @@ public function orNotRegex(string $column, string $pattern): self /** * Ajoute une sous-condition pour la requête. */ - protected function whereCallback( - callable $column, + public function whereGroup( + \Closure $callable, string $bool = self::EXP_AND, bool $not = false ): void { $where = new Where(); - call_user_func_array($column, [ &$where ]); + call_user_func_array($callable, [ &$where ]); $this->where[] = [ 'type' => __FUNCTION__, @@ -375,6 +366,36 @@ protected function whereCallback( ]; } + /** + * Alias inverse de la fonction whereGroup(). + */ + public function notWhereGroup(\Closure $callable): self + { + $this->whereGroup($callable, self::EXP_AND, true); + + return $this; + } + + /** + * Alias avec la porte logique 'OR' de la fonction whereGroup(). + */ + public function orWhereGroup(\Closure $callable): self + { + $this->whereGroup($callable, self::EXP_OR); + + return $this; + } + + /** + * Alias inverse avec la porte logique 'OR' de la fonction whereGroup(). + */ + public function orNotWhereGroup(\Closure $callable): self + { + $this->whereGroup($callable, self::EXP_OR, true); + + return $this; + } + /** * Ajoute une condition like pour la requête. * @@ -413,19 +434,14 @@ protected function like( /** * Filtre l'opérateur. * - * @param string|null $operator + * @param string $operator * - * @throws QueryException * @throws OperatorNotFound * * @return string */ - private function filterOperator(?string $operator): string + private function filterOperator(string $operator): string { - if ($operator === null) { - throw new QueryException(); - } - $condition = strtolower($operator); if (!in_array($condition, self::CONDITION)) { diff --git a/tests/unit/RequestTest.php b/tests/unit/RequestTest.php index 5e888d3..6037b83 100644 --- a/tests/unit/RequestTest.php +++ b/tests/unit/RequestTest.php @@ -946,12 +946,12 @@ public function testOrNotWhere(): void ); } - public function testWhereAndGroup(): void + public function testWhereGroup(): void { $data = $this->request ->from('user') ->where('id', '>=', 2) - ->where(static function (WhereHandler $query): void { + ->whereGroup(static function (WhereHandler $query): void { $query->where('name', '=', 'DUPOND') ->orWhere('firstname', '=', 'Eva'); }); @@ -969,12 +969,36 @@ public function testWhereAndGroup(): void ); } - public function testWhereOrGroup(): void + public function testNotWhereGroup(): void + { + $data = $this->request + ->from('user') + ->where('id', '>=', 2) + ->notWhereGroup(static function (WhereHandler $query): void { + $query->where('name', '=', 'DUPOND') + ->orWhere('firstname', '=', 'Eva'); + }); + + self::assertEquals( + 'SELECT * FROM user WHERE id >= 2 AND NOT (name = \'DUPOND\' OR firstname = \'Eva\');', + (string) $data + ); + self::assertEquals( + [ + [ 'id' => 2, 'name' => 'MARTIN', 'firstname' => 'Manon' ], + [ 'id' => 3, 'name' => null, 'firstname' => 'Marie' ], + [ 'id' => 6, 'name' => 'ROBERT', 'firstname' => null ] + ], + $data->fetchAll() + ); + } + + public function testOrWhereGroup(): void { $data = $this->request ->from('user') ->where('name', '=', 'DUPOND') - ->orWhere(static function (WhereHandler $query): void { + ->orWhereGroup(static function (WhereHandler $query): void { $query->where('firstname', '=', 'Eva') ->orWhere('firstname', '=', 'Mathieu'); }); @@ -994,6 +1018,32 @@ public function testWhereOrGroup(): void ); } + public function testOrNotWhereGroup(): void + { + $data = $this->request + ->from('user') + ->where('name', '=', 'DUPOND') + ->orNotWhereGroup(static function (WhereHandler $query): void { + $query->where('firstname', '=', 'Eva') + ->orWhere('firstname', '=', 'Mathieu'); + }); + + self::assertEquals( + 'SELECT * FROM user WHERE name = \'DUPOND\' OR NOT (firstname = \'Eva\' OR firstname = \'Mathieu\');', + (string) $data + ); + self::assertEquals( + [ + [ 'id' => 1, 'name' => 'DUPOND', 'firstname' => 'Jean' ], + [ 'id' => 2, 'name' => 'MARTIN', 'firstname' => 'Manon' ], + [ 'id' => 3, 'name' => null, 'firstname' => 'Marie' ], + [ 'id' => 4, 'name' => 'DUPOND', 'firstname' => 'Pierre' ], + [ 'id' => 6, 'name' => 'ROBERT', 'firstname' => null ] + ], + $data->fetchAll() + ); + } + public function testLimit(): void { $data = $this->request @@ -1322,7 +1372,7 @@ public function testLeftJoinGroupMultiple(): void ->from('user') ->leftJoin('user_role', 'id', '=', 'user_role.id_user') ->leftJoin('role', static function (WhereHandler $query): void { - $query->where(static function (WhereHandler $query): void { + $query->whereGroup(static function (WhereHandler $query): void { $query->where('id_role', '=', 'role.id_role'); }); })