From 592df024977b3136b0eaaf3f29dc8f723c28b2da Mon Sep 17 00:00:00 2001 From: cbornhoft Date: Fri, 28 Sep 2018 08:20:42 -0400 Subject: [PATCH 1/4] Move where clause building to its own method --- src/Queries/Base.php | 31 ++++++++----------------------- src/Queries/Common.php | 19 +++++++++++++++++++ 2 files changed, 27 insertions(+), 23 deletions(-) diff --git a/src/Queries/Base.php b/src/Queries/Base.php index 393c0ce..30cb4d2 100644 --- a/src/Queries/Base.php +++ b/src/Queries/Base.php @@ -326,29 +326,14 @@ protected function buildQuery() foreach ($this->clauses as $clause => $separator) { if ($this->clauseNotEmpty($clause)) { - if ($clause === 'WHERE') { - $firstStatement = array_shift($this->statements[$clause]); - $query .= " {$clause} {$firstStatement[1]}"; // append first statement to WHERE without condition - - if (!empty($this->statements[$clause])) { - foreach ($this->statements[$clause] as $statement) { - $query .= " {$statement[0]} {$statement[1]}"; - } - } - - // put the first statement back onto the beginning of the array in case we want to run this again - array_unshift($this->statements[$clause], $firstStatement); - } - else { - if (is_string($separator)) { - $query .= " {$clause} " . implode($separator, $this->statements[$clause]); - } elseif ($separator === null) { - $query .= " {$clause} {$this->statements[$clause]}"; - } elseif (is_callable($separator)) { - $query .= call_user_func($separator); - } else { - throw new \Exception("Clause '$clause' is incorrectly set to '$separator'."); - } + if (is_string($separator)) { + $query .= " {$clause} " . implode($separator, $this->statements[$clause]); + } elseif ($separator === null) { + $query .= " {$clause} {$this->statements[$clause]}"; + } elseif (is_callable($separator)) { + $query .= call_user_func($separator); + } else { + throw new \Exception("Clause '$clause' is incorrectly set to '$separator'."); } } } diff --git a/src/Queries/Common.php b/src/Queries/Common.php index c43e130..6a74f8c 100644 --- a/src/Queries/Common.php +++ b/src/Queries/Common.php @@ -206,6 +206,25 @@ protected function getClauseJoin() return implode(' ', $this->statements['JOIN']); } + /** + * @return string + */ + protected function getClauseWhere() { + $firstStatement = array_shift($this->statements['WHERE']); + $query = " WHERE {$firstStatement[1]}"; // append first statement to WHERE without condition + + if (!empty($this->statements['WHERE'])) { + foreach ($this->statements['WHERE'] as $statement) { + $query .= " {$statement[0]} {$statement[1]}"; // [0] -> AND/OR [1] -> field = ? + } + } + + // put the first statement back onto the beginning of the array in case we want to run this again + array_unshift($this->statements['WHERE'], $firstStatement); + + return $query; + } + /** * Statement can contain more tables (e.g. "table1.table2:table3:") * From e49b7fd2704a0ec5f81026aeecfb56c57821ca71 Mon Sep 17 00:00:00 2001 From: cbornhoft Date: Fri, 28 Sep 2018 08:21:59 -0400 Subject: [PATCH 2/4] Update where clauses to use new method getClauseWhere() --- src/Queries/Delete.php | 2 +- src/Queries/Select.php | 2 +- src/Queries/Update.php | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Queries/Delete.php b/src/Queries/Delete.php index 939f2d5..293faf6 100644 --- a/src/Queries/Delete.php +++ b/src/Queries/Delete.php @@ -33,7 +33,7 @@ public function __construct(Query $fluent, string $table) 'DELETE' => [$this, 'getClauseDelete'], 'FROM' => null, 'JOIN' => [$this, 'getClauseJoin'], - 'WHERE' => ' AND ', + 'WHERE' => [$this, 'getClauseWhere'], 'ORDER BY' => ', ', 'LIMIT' => null, ]; diff --git a/src/Queries/Select.php b/src/Queries/Select.php index 278650d..ddb432d 100644 --- a/src/Queries/Select.php +++ b/src/Queries/Select.php @@ -41,7 +41,7 @@ function __construct(Query $fluent, $from) 'SELECT' => ', ', 'FROM' => null, 'JOIN' => [$this, 'getClauseJoin'], - 'WHERE' => ' AND ', + 'WHERE' => [$this, 'getClauseWhere'], 'GROUP BY' => ',', 'HAVING' => ' AND ', 'ORDER BY' => ', ', diff --git a/src/Queries/Update.php b/src/Queries/Update.php index 17250a7..337633b 100644 --- a/src/Queries/Update.php +++ b/src/Queries/Update.php @@ -29,7 +29,7 @@ public function __construct(Query $fluent, string $table) 'UPDATE' => [$this, 'getClauseUpdate'], 'JOIN' => [$this, 'getClauseJoin'], 'SET' => [$this, 'getClauseSet'], - 'WHERE' => ' AND ', + 'WHERE' => [$this, 'getClauseWhere'], 'ORDER BY' => ', ', 'LIMIT' => null, ]; From d2f61cf1494b35c9237fb9c3ca27f295d879eb6e Mon Sep 17 00:00:00 2001 From: cbornhoft Date: Fri, 28 Sep 2018 08:22:31 -0400 Subject: [PATCH 3/4] Add support for named params in Update::set() --- src/Queries/Update.php | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/src/Queries/Update.php b/src/Queries/Update.php index 337633b..ad38733 100644 --- a/src/Queries/Update.php +++ b/src/Queries/Update.php @@ -42,11 +42,14 @@ public function __construct(Query $fluent, string $table) } /** + * In Update's case, parameters are not assigned until the query is built, since this method + * * @param string|array $fieldOrArray * @param bool|string $value * - * @return $this * @throws \Exception + * + * @return $this */ public function set($fieldOrArray, $value = false) { @@ -73,6 +76,8 @@ public function set($fieldOrArray, $value = false) * * @param boolean $getResultAsPdoStatement true to return the pdo statement instead of row count * + * @throws \Exception + * * @return int|boolean|\PDOStatement */ public function execute($getResultAsPdoStatement = false) @@ -103,7 +108,13 @@ protected function getClauseSet() { $setArray = []; foreach ($this->statements['SET'] as $field => $value) { - if ($value instanceof Literal) { + // named params are being used here + if (is_array($value) && strpos(key($value), ':') === 0) { + $key = key($value); + $setArray[] = $field . ' = ' . $key; + $this->parameters['SET'][$key] = $value[$key]; + } + elseif ($value instanceof Literal) { $setArray[] = $field . ' = ' . $value; } else { $setArray[] = $field . ' = ?'; From 16988c42840ab78f30e296f02aab9ade92ff921e Mon Sep 17 00:00:00 2001 From: cbornhoft Date: Fri, 28 Sep 2018 08:23:58 -0400 Subject: [PATCH 4/4] Add unit tests - testWherePreparedArray() - testWhereOr - testUpdateNamedParameters() --- tests/Queries/SelectTest.php | 27 +++++++++++++++++++++++++-- tests/Queries/UpdateTest.php | 32 ++++++++++++++++++++++---------- 2 files changed, 47 insertions(+), 12 deletions(-) diff --git a/tests/Queries/SelectTest.php b/tests/Queries/SelectTest.php index c742c45..bc48a0a 100644 --- a/tests/Queries/SelectTest.php +++ b/tests/Queries/SelectTest.php @@ -86,8 +86,8 @@ public function testWhereArrayParameter() 'type' => 'author' ]); - self::assertEquals([0 => 2, 1 => 'author'], $query->getParameters()); self::assertEquals('SELECT user.* FROM user WHERE id = ? AND type = ?', $query->getQuery(false)); + self::assertEquals([0 => 2, 1 => 'author'], $query->getParameters()); } public function testWhereColumnValue() @@ -95,8 +95,8 @@ public function testWhereColumnValue() $query = $this->fluent->from('user') ->where('type', 'author'); - self::assertEquals([0 => 'author'], $query->getParameters()); self::assertEquals('SELECT user.* FROM user WHERE type = ?', $query->getQuery(false)); + self::assertEquals([0 => 'author'], $query->getParameters()); } public function testWhereColumnNull() @@ -118,6 +118,16 @@ public function testWhereColumnArray() self::assertEquals([], $query->getParameters()); } + public function testWherePreparedArray() + { + $query = $this->fluent + ->from('user') + ->where('id IN (?, ?, ?)', [1, 2, 3]); + + self::assertEquals('SELECT user.* FROM user WHERE id IN (?, ?, ?)', $query->getQuery(false)); + self::assertEquals([0 => 1, 1 => 2, 2 => 3], $query->getParameters()); + } + public function testWhereColumnName() { $query = $this->fluent->from('user') @@ -134,6 +144,17 @@ public function testWhereColumnName() self::assertEquals('Robert', $returnValue); } + public function testWhereOr() + { + $query = $this->fluent->from('comment') + ->where('comment.id = :id', [':id' => 1]) + ->whereOr('user.id = :userId', [':userId' => 2]); + + self::assertEquals('SELECT comment.* FROM comment LEFT JOIN user ON user.id = comment.user_id WHERE comment.id = :id OR user.id = :userId', + $query->getQuery(false)); + self::assertEquals([':id' => '1', ':userId' => '2'], $query->getParameters()); + } + public function testWhereReset() { $query = $this->fluent->from('user')->where('id > ?', 0)->orderBy('name'); @@ -144,6 +165,8 @@ public function testWhereReset() self::assertEquals(['id' => '1', 'country_id' => '1', 'type' => 'admin', 'name' => 'Marek'], $query->fetch()); } + + public function testSelectArrayParam() { $query = $this->fluent diff --git a/tests/Queries/UpdateTest.php b/tests/Queries/UpdateTest.php index f6b439e..b8d88b2 100644 --- a/tests/Queries/UpdateTest.php +++ b/tests/Queries/UpdateTest.php @@ -55,7 +55,7 @@ public function testUpdateFromArray() $query = $this->fluent->update('user')->set(['name' => 'keraM', '`type`' => 'author'])->where('id', 1); self::assertEquals('UPDATE user SET name = ?, `type` = ? WHERE id = ?', $query->getQuery(false)); - self::assertEquals(['0' => 'keraM', '1' => 'author', '2' => '1'], $query->getParameters()); + self::assertEquals([0 => 'keraM', 1 => 'author', 2 => '1'], $query->getParameters()); } public function testUpdateLeftJoin() @@ -67,7 +67,7 @@ public function testUpdateLeftJoin() self::assertEquals('UPDATE user OUTER JOIN country ON country.id = user.country_id SET name = ?, `type` = ? WHERE id = ?', $query->getQuery(false)); - self::assertEquals(['0' => 'keraM', '1' => 'author', '2' => '1'], $query->getParameters()); + self::assertEquals([0 => 'keraM', 1 => 'author', 2 => '1'], $query->getParameters()); } public function testUpdateSmartJoin() @@ -78,7 +78,7 @@ public function testUpdateSmartJoin() self::assertEquals('UPDATE user LEFT JOIN country ON country.id = user.country_id SET type = ? WHERE country.id = ?', $query->getQuery(false)); - self::assertEquals(['0' => 'author', '1' => '1'], $query->getParameters()); + self::assertEquals([0 => 'author', 1 => '1'], $query->getParameters()); } public function testUpdateOrderLimit() @@ -90,7 +90,7 @@ public function testUpdateOrderLimit() ->limit(1); self::assertEquals('UPDATE user SET type = ? WHERE id = ? ORDER BY name LIMIT 1', $query->getQuery(false)); - self::assertEquals(['0' => 'author', '1' => '2'], $query->getParameters()); + self::assertEquals([0 => 'author', 1 => '2'], $query->getParameters()); } public function testUpdateShortCut() @@ -98,7 +98,7 @@ public function testUpdateShortCut() $query = $this->fluent->update('user', ['type' => 'admin'], 1); self::assertEquals('UPDATE user SET type = ? WHERE id = ?', $query->getQuery(false)); - self::assertEquals(['0' => 'admin', '1' => '1'], $query->getParameters()); + self::assertEquals([0 => 'admin', 1 => '1'], $query->getParameters()); } public function testUpdateZero() @@ -106,13 +106,13 @@ public function testUpdateZero() $this->fluent->update('article')->set('content', '')->where('id', 1)->execute(); $user = $this->fluent->from('article')->where('id', 1)->fetch(); - $printQuery = 'ID: ' . $user['id'] . ' - content: ' . $user['content']; + $printQuery = "ID: {$user['id']} - content: {$user['content']}"; $this->fluent->update('article')->set('content', 'content 1')->where('id', 1)->execute(); $user2 = $this->fluent->from('article')->where('id', 1)->fetch(); - $printQuery2 = 'ID: ' . $user2['id'] . ' - content: ' . $user2['content']; + $printQuery2 = "ID: {$user2['id']} - content: {$user2['content']}"; self::assertEquals('ID: 1 - content: ', $printQuery); self::assertEquals('ID: 1 - content: content 1', $printQuery2); @@ -132,9 +132,21 @@ public function testUpdateWhere() self::assertEquals('UPDATE users LEFT JOIN country ON country.id = users.country_id SET `users`.`active` = ? WHERE `country`.`name` = ? AND `users`.`name` = ?', $query->getQuery(false)); - self::assertEquals(['0' => '1', '1' => 'Slovakia', '2' => 'Marek'], $query->getParameters()); + self::assertEquals([0 => '1', 1 => 'Slovakia', 2 => 'Marek'], $query->getParameters()); self::assertEquals('UPDATE users LEFT JOIN country ON country.id = users.country_id SET [users].[active] = ? WHERE [country].[name] = ? AND [users].[name] = ?', $query2->getQuery(false)); - self::assertEquals(['0' => '1', '1' => 'Slovakia', '2' => 'Marek'], $query2->getParameters()); + self::assertEquals([0 => '1', 1 => 'Slovakia', 2 => 'Marek'], $query2->getParameters()); } -} \ No newline at end of file + + public function testUpdateNamedParameters() + { + $query = $this->fluent->update('users') + ->set("`users`.`active`", [':active' => 1]) + ->where("`country`.`name` = :country", [':country' => 'Slovakia']); + + self::assertEquals('UPDATE users LEFT JOIN country ON country.id = users.country_id SET `users`.`active` = :active WHERE `country`.`name` = :country', + $query->getQuery(false)); + self::assertEquals([':active' => '1', ':country' => 'Slovakia'], $query->getParameters()); + } + +}