diff --git a/src/Io/Query.php b/src/Io/Query.php index 71ab90d..0ccb352 100644 --- a/src/Io/Query.php +++ b/src/Io/Query.php @@ -125,16 +125,34 @@ protected function resolveValueForSql($value) protected function buildSql() { + $tooManyParametersError = 'Params not enough to build sql'; $sql = $this->sql; $offset = strpos($sql, '?'); - foreach ($this->params as $param) { + $offsetNamed = 0; + + foreach ($this->params as $key => $param) { $replacement = $this->resolveValueForSql($param); - $sql = substr_replace($sql, $replacement, $offset, 1); - $offset = strpos($sql, '?', $offset + strlen($replacement)); + + if (is_string($key)) { + $prefix = $key[0] === ':' ? '' : ':'; + $offsetNamed = strpos($sql, $prefix . $key, $offsetNamed); + + if ($offsetNamed === false) { + throw new \LogicException($tooManyParametersError); + } + + $sql = substr_replace($sql, $replacement, $offsetNamed, strlen($prefix) + strlen($key)); + $offset = strpos($sql, '?', strlen($replacement)); + $offsetNamed += strlen($replacement); + } else { + $sql = substr_replace($sql, $replacement, $offset, 1); + $offset = strpos($sql, '?', $offset + strlen($replacement)); + } } + if ($offset !== false) { - throw new \LogicException('Params not enough to build sql'); + throw new \LogicException($tooManyParametersError); } return $sql; diff --git a/tests/Io/QueryTest.php b/tests/Io/QueryTest.php index 3a5831f..788bffe 100644 --- a/tests/Io/QueryTest.php +++ b/tests/Io/QueryTest.php @@ -27,6 +27,56 @@ public function testBindParams() */ } + public function testNamedParams() + { + $query = new Query('select * from test where id = :id and name = :name'); + $sql = $query->bindParamsFromArray([ + 'id' => 100, + 'name' => 'test' + ])->getSql(); + $this->assertEquals("select * from test where id = 100 and name = 'test'", $sql); + } + + public function testNamedParamsWithPrefix() + { + $query = new Query('select * from test where id = :id and name = :name'); + $sql = $query->bindParamsFromArray([ + ':id' => 100, + ':name' => 'test' + ])->getSql(); + $this->assertEquals("select * from test where id = 100 and name = 'test'", $sql); + } + + public function testNamedParamsWithInClause() + { + $query = new Query('select * from test where id in (:in) and name = :name'); + $sql = $query->bindParamsFromArray([ + 'in' => [1, 2], + 'name' => 'test' + ])->getSql(); + $this->assertEquals("select * from test where id in (1,2) and name = 'test'", $sql); + } + + public function testMixedNamedParams() + { + $query = new Query('select * from test where id in (?) and name = :name'); + $sql = $query->bindParamsFromArray([ + [1, 2], + 'name' => 'test' + ])->getSql(); + $this->assertEquals("select * from test where id in (1,2) and name = 'test'", $sql); + } + + public function testMixedNamedParamsWithInClause() + { + $query = new Query('select * from test where id in (:in) and name = ?'); + $sql = $query->bindParamsFromArray([ + 'in' => [1, 2], + 'test' + ])->getSql(); + $this->assertEquals("select * from test where id in (1,2) and name = 'test'", $sql); + } + public function testGetSqlReturnsQuestionMarkReplacedWhenBound() { $query = new Query('select ?'); @@ -55,6 +105,16 @@ public function testGetSqlReturnsQuestionMarkReplacedFromBoundWhenBound() $this->assertEquals("select CONCAT('hello??', 'world??')", $sql); } + public function testGetSqlReturnsNamedParamsReplacedFromBoundWhenBound() + { + $query = new Query('select CONCAT(:param1, :param2)'); + $sql = $query->bindParamsFromArray([ + 'param1' => ':param2', + 'param2' => 'world' + ])->getSql(); + $this->assertEquals("select CONCAT(':param2', 'world')", $sql); + } + public function testGetSqlReturnsQuestionMarksAsIsWhenNotBound() { $query = new Query('select "hello?"');