diff --git a/README.md b/README.md index f86235d6..526839bf 100644 --- a/README.md +++ b/README.md @@ -147,6 +147,15 @@ notBetween('column', $value, $value2) // $value will protected by either using escape or prepare statement ``` +```php +// To allow simple grouping of basic $whereConditions, +// wrap the following around a group of the above comparison +// expressions within the where( ...$whereConditions) clause +grouping( eq(key, value, combiner ), eq(key, value, combiner ) ) +// The above will wrap beginning and end grouping in a where statement +// where required to break down your where clause. +``` + ```php // Supply the the whole query string, and placing '?' within // With the same number of arguments in an array. diff --git a/lib/ezFunctions.php b/lib/ezFunctions.php index 3c335ded..b99fc9a7 100644 --- a/lib/ezFunctions.php +++ b/lib/ezFunctions.php @@ -349,6 +349,14 @@ function where(...$args) : false; } + function grouping(...$args) + { + $ezQuery = \getInstance(); + return ($ezQuery instanceof DatabaseInterface) + ? $ezQuery->grouping(...$args) + : false; + } + function groupBy($groupBy) { $ezQuery = \getInstance(); @@ -374,7 +382,7 @@ function innerJoin( $condition = \EQ ) { $ezQuery = \getInstance(); - return ($ezQuery instanceOf DatabaseInterface) + return ($ezQuery instanceof DatabaseInterface) ? $ezQuery->innerJoin($leftTable, $rightTable, $leftColumn, $rightColumn, $tableAs, $condition) : false; } @@ -388,7 +396,7 @@ function leftJoin( $condition = \EQ ) { $ezQuery = \getInstance(); - return ($ezQuery instanceOf DatabaseInterface) + return ($ezQuery instanceof DatabaseInterface) ? $ezQuery->leftJoin($leftTable, $rightTable, $leftColumn, $rightColumn, $tableAs, $condition) : false; } @@ -402,7 +410,7 @@ function rightJoin( $condition = \EQ ) { $ezQuery = \getInstance(); - return ($ezQuery instanceOf DatabaseInterface) + return ($ezQuery instanceof DatabaseInterface) ? $ezQuery->rightJoin($leftTable, $rightTable, $leftColumn, $rightColumn, $tableAs, $condition) : false; } @@ -416,7 +424,7 @@ function fullJoin( $condition = \EQ ) { $ezQuery = \getInstance(); - return ($ezQuery instanceOf DatabaseInterface) + return ($ezQuery instanceof DatabaseInterface) ? $ezQuery->fullJoin($leftTable, $rightTable, $leftColumn, $rightColumn, $tableAs, $condition) : false; } diff --git a/lib/ezQuery.php b/lib/ezQuery.php index 842b297d..7c02ac73 100644 --- a/lib/ezQuery.php +++ b/lib/ezQuery.php @@ -342,13 +342,16 @@ public function limit($numberOf, $offset = null) return 'LIMIT ' . $rows . $value; } - private function conditions($key, $condition, $value, $combine) + private function conditions($key, $condition, $value, $combine, $extra) { + $groupStart = (!empty($extra) && $extra === '(') ? $extra : ''; + $groupEnd = (!empty($extra) && $extra === ')') ? $extra : ''; + if ($this->isPrepareOn()) { - $this->whereSQL .= "$key $condition " . \_TAG . " $combine "; + $this->whereSQL .= "$groupStart $key $condition " . \_TAG . " $groupEnd $combine "; $this->addPrepare($value); } else - $this->whereSQL .= "$key $condition '" . $this->escape($value) . "' $combine "; + $this->whereSQL .= "$groupStart $key $condition '" . $this->escape($value) . "' $groupEnd $combine "; } private function conditionBetween($key, $condition, $valueOne, $valueTwo, $combine) @@ -388,8 +391,22 @@ private function conditionIs($key, $condition, $combine) $this->whereSQL .= "$key $isCondition NULL $combine "; } + private function flattenWhereConditions($whereConditions) + { + $whereConditionsReturn = []; + foreach ($whereConditions as $whereCondition) { + if (!empty($whereCondition[0]) && is_array($whereCondition[0])) { + $whereConditionsReturn = array_merge($whereConditionsReturn, $this->flattenWhereConditions($whereCondition)); + } else { + $whereConditionsReturn[] = $whereCondition; + } + } + return $whereConditionsReturn; + } + private function retrieveConditions($whereConditions) { + $whereConditions = $this->flattenWhereConditions($whereConditions); $whereKey = []; $whereValue = []; $operator = []; @@ -408,7 +425,7 @@ private function retrieveConditions($whereConditions) $combiner[] = \_AND; $extra[] = null; } else { - if (isset($checkFields[0])) { + if (!empty($checkFields[0])) { $whereKey[] = $checkFields[0]; $whereValue[] = (isset($checkFields[2])) ? $checkFields[2] : ''; $combiner[] = (isset($checkFields[3])) ? $checkFields[3] : \_AND; @@ -434,12 +451,37 @@ private function processConditions($column, $condition, $value, $valueOrCombine, } elseif ((($condition == \_LIKE) || ($condition == \_notLIKE)) && !\preg_match('/[_%?]/', $value)) { return $this->clearPrepare(); } else { - $this->conditions($column, $condition, $value, $valueOrCombine); + $this->conditions($column, $condition, $value, $valueOrCombine, $extraCombine); + } + } + + public function grouping(...$whereConditions) + { + if (empty($whereConditions)) + return false; + + $whereOrHaving = ($this->isWhere) ? 'WHERE' : 'HAVING'; + + if (\is_string($whereConditions[0]) && \strpos($whereConditions[0], $whereOrHaving) !== false) + return $whereConditions[0]; + + $totalConditions = count($whereConditions) - 1; + + if ($totalConditions > 0) { + + if (!in_array('(', $whereConditions[0])) + $whereConditions[0][count($whereConditions[0])] = '('; + + if (!in_array(')', $whereConditions[$totalConditions])) + $whereConditions[$totalConditions][count($whereConditions[$totalConditions])] = ')'; } + + return $whereConditions; } public function where(...$whereConditions) { + if (empty($whereConditions)) return false; diff --git a/lib/ezQueryInterface.php b/lib/ezQueryInterface.php index 08bfe5f0..5007d1d2 100644 --- a/lib/ezQueryInterface.php +++ b/lib/ezQueryInterface.php @@ -294,6 +294,33 @@ public function orderBy($orderBy, $order); */ public function limit($numberOf, $offset = null); + /** + * Helper adds WHERE grouping to the conditions + * + * format: + * `grouping( comparison(x, y, and) )` + * + * example: + * `grouping( eq(key, value, combiner ), eq(key, value, combiner ) );` + * + * @param array $whereConditions - In the following format: + * + * eq('key/Field/Column', $value, _AND), // combine next expression + * neq('key/Field/Column', $value, _OR), // will combine next expression again + * ne('key/Field/Column', $value), // the default is _AND so will combine next expression + * lt('key/Field/Column', $value) + * lte('key/Field/Column', $value) + * gt('key/Field/Column', $value) + * gte('key/Field/Column', $value) + * isNull('key/Field/Column') + * isNotNull('key/Field/Column') + * like('key/Field/Column', '_%') + * notLike('key/Field/Column', '_%') + * + * @return array modified conditions + */ + public function grouping(...$whereConditions); + /** * Helper returns an WHERE sql clause string. * diff --git a/tests/pdo/pdo_mysqlTest.php b/tests/pdo/pdo_mysqlTest.php index dc36492c..a46f2a2a 100644 --- a/tests/pdo/pdo_mysqlTest.php +++ b/tests/pdo/pdo_mysqlTest.php @@ -265,6 +265,26 @@ public function testSelecting() $this->assertEquals(0, $this->object->query('DROP TABLE unit_test')); } + public function testWhereGrouping() + { + $this->assertTrue($this->object->connect('mysql:host=' . self::TEST_DB_HOST . ';dbname=' . self::TEST_DB_NAME . ';port=' . self::TEST_DB_PORT, self::TEST_DB_USER, self::TEST_DB_PASSWORD)); + $this->object->query('CREATE TABLE unit_test(id integer, test_key varchar(50), active tinyint(1), PRIMARY KEY (ID))'); + $this->object->insert('unit_test', array('id' => '1', 'test_key' => 'testing 1', 'active' => 1)); + $this->object->insert('unit_test', array('id' => '2', 'test_key' => 'testing 2', 'active' => 0)); + $this->object->insert('unit_test', array('id' => '3', 'test_key' => 'testing 3', 'active' => 1)); + $this->object->insert('unit_test', array('id' => '4', 'test_key' => 'testing 4', 'active' => 1)); + + $result = $this->object->selecting('unit_test', '*', where(eq('active', '1'), grouping(like('test_key', '%1%', _OR), like('test_key', '%3%')))); + $i = 1; + foreach ($result as $row) { + $this->assertEquals($i, $row->id); + $this->assertEquals('testing ' . $i, $row->test_key); + $i = $i + 2; + } + + $this->assertEquals(0, $this->object->query('DROP TABLE unit_test')); + } + public function testJoins() { $this->assertTrue($this->object->connect('mysql:host=' . self::TEST_DB_HOST . ';dbname=' . self::TEST_DB_NAME . ';port=' . self::TEST_DB_PORT, self::TEST_DB_USER, self::TEST_DB_PASSWORD)); diff --git a/tests/pdo/pdo_pgsqlTest.php b/tests/pdo/pdo_pgsqlTest.php index 414b6f06..58a91eb1 100644 --- a/tests/pdo/pdo_pgsqlTest.php +++ b/tests/pdo/pdo_pgsqlTest.php @@ -189,6 +189,24 @@ public function testSelecting() } } + public function testWhereGrouping() + { + $this->assertTrue($this->object->connect('pgsql:host=' . self::TEST_DB_HOST . ';dbname=' . self::TEST_DB_NAME . ';port=' . self::TEST_DB_PORT, self::TEST_DB_USER, self::TEST_DB_PASSWORD)); + $this->object->query('CREATE TABLE unit_test(id integer, test_key varchar(50), active tinyint(1), PRIMARY KEY (ID))'); + $this->object->insert('unit_test', array('id' => '1', 'test_key' => 'testing 1', 'active' => 1)); + $this->object->insert('unit_test', array('id' => '2', 'test_key' => 'testing 2', 'active' => 0)); + $this->object->insert('unit_test', array('id' => '3', 'test_key' => 'testing 3', 'active' => 1)); + $this->object->insert('unit_test', array('id' => '4', 'test_key' => 'testing 4', 'active' => 1)); + + $result = $this->object->selecting('unit_test', '*', where(eq('active', '1'), grouping(like('test_key', '%1%', _OR), like('test_key', '%3%')))); + $i = 1; + foreach ($result as $row) { + $this->assertEquals($i, $row->id); + $this->assertEquals('testing ' . $i, $row->test_key); + $i = $i + 2; + } + } + public function testJoins() { $this->assertTrue($this->object->connect('pgsql:host=' . self::TEST_DB_HOST . ';dbname=' . self::TEST_DB_NAME . ';port=' . self::TEST_DB_PORT, self::TEST_DB_USER, self::TEST_DB_PASSWORD)); diff --git a/tests/pdo/pdo_sqliteTest.php b/tests/pdo/pdo_sqliteTest.php index ea639af2..2cc13485 100644 --- a/tests/pdo/pdo_sqliteTest.php +++ b/tests/pdo/pdo_sqliteTest.php @@ -216,6 +216,26 @@ public function testSelecting() $this->assertEquals(1, $this->object->query('DROP TABLE unit_test')); } + public function testWhereGrouping() + { + $this->assertTrue($this->object->connect('sqlite:' . self::TEST_SQLITE_DB, '', '', array(), true)); + $this->object->query('CREATE TABLE unit_test(id integer, test_key varchar(50), active tinyint(1), PRIMARY KEY (ID))'); + $this->object->insert('unit_test', array('id' => '1', 'test_key' => 'testing 1', 'active' => 1)); + $this->object->insert('unit_test', array('id' => '2', 'test_key' => 'testing 2', 'active' => 0)); + $this->object->insert('unit_test', array('id' => '3', 'test_key' => 'testing 3', 'active' => 1)); + $this->object->insert('unit_test', array('id' => '4', 'test_key' => 'testing 4', 'active' => 1)); + + $result = $this->object->selecting('unit_test', '*', where(eq('active', '1'), grouping(like('test_key', '%1%', _OR), like('test_key', '%3%')))); + $i = 1; + foreach ($result as $row) { + $this->assertEquals($i, $row->id); + $this->assertEquals('testing ' . $i, $row->test_key); + $i = $i + 2; + } + + $this->assertEquals(1, $this->object->query('DROP TABLE unit_test')); + } + public function testJoins() { $this->assertTrue($this->object->connect('sqlite:' . self::TEST_SQLITE_DB, '', '', array(), true)); diff --git a/tests/pdo/pdo_sqlsrvTest.php b/tests/pdo/pdo_sqlsrvTest.php index f3ed02b5..de8e82bd 100644 --- a/tests/pdo/pdo_sqlsrvTest.php +++ b/tests/pdo/pdo_sqlsrvTest.php @@ -193,6 +193,24 @@ public function testSelecting() } } + public function testWhereGrouping() + { + $this->assertTrue($this->object->connect('sqlsrv:Server=' . self::TEST_DB_HOST . ';Database=' . self::TEST_DB_NAME, self::TEST_DB_USER, self::TEST_DB_PASSWORD)); + $this->object->query('CREATE TABLE unit_test(id integer, test_key varchar(50), active tinyint(1), PRIMARY KEY (ID))'); + $this->object->insert('unit_test', array('id' => '1', 'test_key' => 'testing 1', 'active' => 1)); + $this->object->insert('unit_test', array('id' => '2', 'test_key' => 'testing 2', 'active' => 0)); + $this->object->insert('unit_test', array('id' => '3', 'test_key' => 'testing 3', 'active' => 1)); + $this->object->insert('unit_test', array('id' => '4', 'test_key' => 'testing 4', 'active' => 1)); + + $result = $this->object->selecting('unit_test', '*', where(eq('active', '1'), grouping(like('test_key', '%1%', _OR), like('test_key', '%3%')))); + $i = 1; + foreach ($result as $row) { + $this->assertEquals($i, $row->id); + $this->assertEquals('testing ' . $i, $row->test_key); + $i = $i + 2; + } + } + public function testJoins() { $this->assertTrue($this->object->connect('sqlsrv:Server=' . self::TEST_DB_HOST . ';Database=' . self::TEST_DB_NAME, self::TEST_DB_USER, self::TEST_DB_PASSWORD));