diff --git a/src/Common/WhereInterface.php b/src/Common/WhereInterface.php index 99f03bd..48e774d 100644 --- a/src/Common/WhereInterface.php +++ b/src/Common/WhereInterface.php @@ -8,6 +8,8 @@ */ namespace Aura\SqlQuery\Common; +use Aura\SqlQuery\Exception; + /** * * An interface for WHERE clauses. @@ -48,4 +50,40 @@ public function where($cond, array $bind = []); * */ public function orWhere($cond, array $bind = []); + + /** + * + * Adds a WHERE condition with a prepared statement placeholder and value to the query by AND. + * + * @param string $cond the first part of the WHERE condition without the placeholder. e.g. "name = " or "name IN" + * + * @param string $placeholder the placeholder as a string. e.g. ":NAME" or "(:NAMES)" + * + * @param (string|int|float|array) $value the value to be bound to the placeholder. e.g. "John" or ["John", "Eric", "Michael", "Terry"] + * + * @return $this + * + * @throws Exception + * + */ + public function whereBoundValue($cond, $placeholder, $value); + + /** + * + * Adds a WHERE condition with a prepared statement placeholder and value to the query by OR. + * + * @param string $cond the first part of the WHERE condition without the placeholder. e.g. "name = " or "name IN" + * + * @param string $placeholder the placeholder as a string. e.g. ":NAME" or "(:NAMES)" + * + * @param (string|int|float|array) $value the value to be bound to the placeholder. e.g. "John" or ["John", "Eric", "Michael", "Terry"] + * + * @return $this + * + * @throws Exception + * + * @see whereBoundValue() + * + */ + public function orWhereBoundValue($cond, $placeholder, $value); } diff --git a/src/Common/WhereTrait.php b/src/Common/WhereTrait.php index e54df7b..b30f686 100644 --- a/src/Common/WhereTrait.php +++ b/src/Common/WhereTrait.php @@ -8,6 +8,8 @@ */ namespace Aura\SqlQuery\Common; +use Aura\SqlQuery\Exception; + /** * * Common code for WHERE clauses. @@ -54,4 +56,69 @@ public function orWhere($cond, array $bind = []) $this->addClauseCondWithBind('where', 'OR', $cond, $bind); return $this; } + + /** + * + * Adds a WHERE condition with a prepared statement placeholder and value to the query by AND. + * + * @param string $cond the first part of the WHERE condition without the placeholder. e.g. "name = " or "name IN" + * + * @param string $placeholder the placeholder as a string. e.g. ":NAME" or "(:NAMES)" + * + * @param (string|int|float|array) $value the value to be bound to the placeholder. e.g. "John" or ["John", "Eric", "Michael", "Terry"] + * + * @return $this + * + * @throws Exception + * + */ + public function whereBoundValue($cond, $placeholder, $value) + { + $name = $this->extractNameOrThrow($placeholder); + $this->addClauseCondWithBind('where', 'AND', $cond.$placeholder, [ $name => $value ] ); + return $this; + } + + /** + * + * Adds a WHERE condition with a prepared statement placeholder and value to the query by OR. + * + * @param string $cond the first part of the WHERE condition without the placeholder. e.g. "name = " or "name IN" + * + * @param string $placeholder the placeholder as a string. e.g. ":NAME" or "(:NAMES)" + * + * @param (string|int|float|array) $value the value to be bound to the placeholder. e.g. "John" or ["John", "Eric", "Michael", "Terry"] + * + * @return $this + * + * @throws Exception + * + * @see whereBoundValue() + * + */ + public function orWhereBoundValue($cond, $placeholder, $value) + { + $name = $this->extractNameOrThrow($placeholder); + $this->addClauseCondWithBind('where', 'OR', $cond.$placeholder, [ $name => $value ] ); + return $this; + } + + /** + * Extract the name of PDO placeholders (e.g. "P") of the form ":P" for simple values and "(:P)" for array values. + * + * @param string $placeholder the placeholder specification + * + * @return the placeholder name as string + * + * @throws Exception + * + */ + protected static function extractNameOrThrow($placeholder) { + $name = preg_replace( '/^\(?:([^\)]+)\)?$/', '\1', $placeholder); + // XXX add type checks + if (strlen($name)===strlen($placeholder)) { + throw new Exception("Bad placeholder \"$name\""); + } + return $name; + } } diff --git a/tests/Common/SelectTest.php b/tests/Common/SelectTest.php index da4cefa..b4632d8 100644 --- a/tests/Common/SelectTest.php +++ b/tests/Common/SelectTest.php @@ -531,6 +531,30 @@ public function testOrWhere() $this->assertSame($expect, $actual); } + + public function testWhereBoundValue() + { + $this->query->cols(array('*')); + $this->query->where('c1 = c2') + ->whereBoundValue('c2 IN ', '(:c2)', ['foo']) + ->whereBoundValue('c3 = ', ':c3', 'foo'); + $expect = ' + SELECT + * + WHERE + c1 = c2 + AND c2 IN (:c2) + AND c3 = :c3 + '; + + $actual = $this->query->__toString(); + $this->assertSameSql($expect, $actual); + + $actual = $this->query->getBindValues(); + $expect = ['c2' => ['foo'], 'c3' => 'foo']; + $this->assertSame($expect, $actual); + } + public function testGroupBy() { $this->query->cols(array('*'));