Skip to content

Commit

Permalink
Add Expression functionality
Browse files Browse the repository at this point in the history
  • Loading branch information
peteraba committed Dec 28, 2019
1 parent 451dca5 commit bb647e2
Show file tree
Hide file tree
Showing 10 changed files with 209 additions and 30 deletions.
72 changes: 72 additions & 0 deletions src/Opulence/QueryBuilders/Expression/Expression.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
<?php

/*
* Opulence
*
* @link https://www.opulencephp.com
* @copyright Copyright (C) 2017 David Young
* @license https://github.com/opulencephp/Opulence/blob/master/LICENSE.md
*/

namespace Opulence\QueryBuilders\Expression;

use Opulence\QueryBuilders\InvalidQueryException;

/**
* Expression is designed to be used for setting values in INSERT and UPDATE statements
* It is not intended to be used in WHERE clauses or as columns in SELECT queries
*/
class Expression
{
/** @var string The expression to use */
protected $expression = '';

/** @var array[] */
protected $values = [];

/**
* Expression constructor.
*
* @param string $expression
* @param mixed ...$values
*
* @throws InvalidQueryException
*/
public function __construct(string $expression, ...$values)
{
$this->expression = $expression;

foreach ($values as $value) {
if (is_scalar($value)) {
$value = [$value, \PDO::PARAM_STR];
}

if (!is_array($value) || count($value) !== 2) {
throw new InvalidQueryException('Incorrect number of items in expression value array');
}

if (!array_key_exists(0, $value) || !array_key_exists(1, $value)) {
throw new InvalidQueryException('Incorrect keys in expression value array');
}

if (!is_scalar($value[0]) || !is_numeric($value[1]) || $value[1] < 0) {
throw new InvalidQueryException('Incorrect expression values');
}

$this->values[] = $value;
}
}

/**
* @return array
*/
public function getParameters() : array
{
return $this->values;
}

public function getSql() : string
{
return $this->expression;
}
}
20 changes: 15 additions & 5 deletions src/Opulence/QueryBuilders/InsertQuery.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@

namespace Opulence\QueryBuilders;

use Opulence\QueryBuilders\Expression\Expression;

/**
* Builds an insert query
*/
Expand Down Expand Up @@ -64,11 +66,19 @@ public function addColumnValues(array $columnNamesToValues) : self
*/
public function getSql() : string
{
$sql = "INSERT INTO {$this->tableName}"
. ' (' . implode(', ', array_keys($this->augmentingQueryBuilder->getColumnNamesToValues())) . ') VALUES ('
. implode(', ',
array_fill(0, count(array_values($this->augmentingQueryBuilder->getColumnNamesToValues())), '?'))
. ')';
$namesToValues = $this->augmentingQueryBuilder->getColumnNamesToValues();

$sql = 'INSERT INTO ' . $this->tableName . ' (' . implode(', ', array_keys($namesToValues)) . ') VALUES (';

$values = [];
foreach ($namesToValues as $value) {
if ($value instanceof Expression) {
$values[] = $value->getSql();
} else {
$values[] = '?';
}
}
$sql .= implode(', ', $values) . ')';

return $sql;
}
Expand Down
6 changes: 6 additions & 0 deletions src/Opulence/QueryBuilders/Query.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

namespace Opulence\QueryBuilders;

use Opulence\QueryBuilders\Expression\Expression;
use PDO;

/**
Expand Down Expand Up @@ -124,6 +125,11 @@ public function addUnnamedPlaceholderValue($value, int $dataType = PDO::PARAM_ST
public function addUnnamedPlaceholderValues(array $placeholderValues) : self
{
foreach ($placeholderValues as $value) {
if ($value instanceof Expression) {
$this->addUnnamedPlaceholderValues($value->getParameters());
continue;
}

if (is_array($value)) {
if (count($value) !== 2) {
throw new InvalidQueryException('Incorrect number of items in value array');
Expand Down
50 changes: 45 additions & 5 deletions src/Opulence/QueryBuilders/Tests/InsertQueryTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

namespace Opulence\QueryBuilders\Tests;

use Opulence\QueryBuilders\Expression\Expression;
use Opulence\QueryBuilders\InsertQuery;

/**
Expand All @@ -27,7 +28,7 @@ public function testAddingMoreColumns()
$this->assertEquals('INSERT INTO users (name, email) VALUES (?, ?)', $query->getSql());
$this->assertEquals([
['dave', \PDO::PARAM_STR],
['foo@bar.com', \PDO::PARAM_STR]
['foo@bar.com', \PDO::PARAM_STR],
], $query->getParameters());
}

Expand All @@ -44,17 +45,56 @@ public function testBasicQuery()
], $query->getParameters());
}

/**
* Tests a query with a simple UpsertExpression
*/
public function testSimpleExpression()
{
$query = new InsertQuery('users',
['name' => 'dave', 'email' => 'foo@bar.com', 'valid_until' => new Expression('NOW()')]);
$this->assertEquals('INSERT INTO users (name, email, valid_until) VALUES (?, ?, NOW())', $query->getSql());
$this->assertEquals([
['dave', \PDO::PARAM_STR],
['foo@bar.com', \PDO::PARAM_STR],
], $query->getParameters());
}

/**
* Tests a query with a complex UpsertExpression
*/
public function testComplexExpression()
{
$query = new InsertQuery('users',
[
'name' => 'dave',
'email' => 'foo@bar.com',
'is_val_even' => new Expression('(val + ?) % ?', ['1', \PDO::PARAM_INT], [2, \PDO::PARAM_INT])
]);
$this->assertEquals('INSERT INTO users (name, email, is_val_even) VALUES (?, ?, (val + ?) % ?)',
$query->getSql());
$this->assertSame([
['dave', \PDO::PARAM_STR],
['foo@bar.com', \PDO::PARAM_STR],
['1', \PDO::PARAM_INT],
[2, \PDO::PARAM_INT],
], $query->getParameters());
}

/**
* Tests all the methods in a single, complicated query
*/
public function testEverything()
{
$expr = new Expression("(val + ?) % ?", ['1', \PDO::PARAM_INT]);
$query = new InsertQuery('users', ['name' => 'dave']);
$query->addColumnValues(['email' => 'foo@bar.com']);
$this->assertEquals('INSERT INTO users (name, email) VALUES (?, ?)', $query->getSql());
$this->assertEquals([
$query->addColumnValues(['email' => 'foo@bar.com', 'is_val_even' => $expr])
->addUnnamedPlaceholderValues([[2, \PDO::PARAM_INT]]);
$this->assertEquals('INSERT INTO users (name, email, is_val_even) VALUES (?, ?, (val + ?) % ?)', $query->getSql());
$this->assertSame([
['dave', \PDO::PARAM_STR],
['foo@bar.com', \PDO::PARAM_STR]
['foo@bar.com', \PDO::PARAM_STR],
['1', \PDO::PARAM_INT],
[2, \PDO::PARAM_INT],
], $query->getParameters());
}
}
16 changes: 12 additions & 4 deletions src/Opulence/QueryBuilders/Tests/MySql/InsertQueryTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

namespace Opulence\QueryBuilders\Tests\MySql;

use Opulence\QueryBuilders\Expression\Expression;
use Opulence\QueryBuilders\MySql\InsertQuery;
use PDO;

Expand Down Expand Up @@ -52,14 +53,21 @@ public function testBasicQuery()
*/
public function testEverything()
{
$query = new InsertQuery('users', ['name' => 'dave', 'email' => 'foo@bar.com']);
$query = new InsertQuery('users',
[
'name' => 'dave',
'email' => 'foo@bar.com',
'is_val_even' => new Expression('(val + ?) % ?', ['1', \PDO::PARAM_INT], [2, \PDO::PARAM_INT])
]);
$query->update(['name' => 'dave'])
->addUpdateColumnValues(['email' => 'foo@bar.com']);
$this->assertEquals('INSERT INTO users (name, email) VALUES (?, ?) ON DUPLICATE KEY UPDATE name = ?, email = ?',
$this->assertEquals('INSERT INTO users (name, email, is_val_even) VALUES (?, ?, (val + ?) % ?) ON DUPLICATE KEY UPDATE name = ?, email = ?',
$query->getSql());
$this->assertEquals([
$this->assertSame([
['dave', PDO::PARAM_STR],
['foo@bar.com', PDO::PARAM_STR]
['foo@bar.com', PDO::PARAM_STR],
['1', \PDO::PARAM_INT],
[2, \PDO::PARAM_INT],
], $query->getParameters());
}

Expand Down
16 changes: 11 additions & 5 deletions src/Opulence/QueryBuilders/Tests/PostgreSql/InsertQueryTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

namespace Opulence\QueryBuilders\Tests\PostgreSql;

use Opulence\QueryBuilders\Expression\Expression;
use Opulence\QueryBuilders\PostgreSql\InsertQuery;
use PDO;

Expand Down Expand Up @@ -37,14 +38,19 @@ public function testAddReturning()
*/
public function testEverything()
{
$expr = new Expression("(val + ?) % ?", ['1', \PDO::PARAM_INT]);
$query = new InsertQuery('users', ['name' => 'dave']);
$query->addColumnValues(['email' => 'foo@bar.com'])
$query->addColumnValues(['email' => 'foo@bar.com', 'is_val_even' => $expr])
->returning('id')
->addReturning('name');
$this->assertEquals('INSERT INTO users (name, email) VALUES (?, ?) RETURNING id, name', $query->getSql());
$this->assertEquals([
->addReturning('name')
->addUnnamedPlaceholderValues([[2, \PDO::PARAM_INT]]);
$this->assertEquals('INSERT INTO users (name, email, is_val_even) VALUES (?, ?, (val + ?) % ?) RETURNING id, name',
$query->getSql());
$this->assertSame([
['dave', PDO::PARAM_STR],
['foo@bar.com', PDO::PARAM_STR]
['foo@bar.com', PDO::PARAM_STR],
['1', \PDO::PARAM_INT],
[2, \PDO::PARAM_INT],
], $query->getParameters());
}

Expand Down
10 changes: 7 additions & 3 deletions src/Opulence/QueryBuilders/Tests/PostgreSql/UpdateQueryTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

namespace Opulence\QueryBuilders\Tests\PostgreSql;

use Opulence\QueryBuilders\Expression\Expression;
use Opulence\QueryBuilders\PostgreSql\UpdateQuery;
use PDO;

Expand Down Expand Up @@ -37,19 +38,22 @@ public function testAddReturning()
*/
public function testEverything()
{
$expr = new Expression("(val + ?) % ?", ['1', PDO::PARAM_INT]);
$query = new UpdateQuery('users', 'u', ['name' => 'david']);
$query->addColumnValues(['email' => 'bar@foo.com'])
$query->addColumnValues(['email' => 'bar@foo.com', 'is_val_even' => $expr])
->where('u.id = ?', 'emails.userid = u.id', 'emails.email = ?')
->orWhere('u.name = ?')
->andWhere('subscriptions.userid = u.id', "subscriptions.type = 'customer'")
->returning('u.id')
->addReturning('u.name')
->addUnnamedPlaceholderValues([[18175, PDO::PARAM_INT], 'foo@bar.com', 'dave']);
$this->assertEquals("UPDATE users AS u SET name = ?, email = ? WHERE (u.id = ?) AND (emails.userid = u.id) AND (emails.email = ?) OR (u.name = ?) AND (subscriptions.userid = u.id) AND (subscriptions.type = 'customer') RETURNING u.id, u.name",
->addUnnamedPlaceholderValues([[2, PDO::PARAM_INT], [18175, PDO::PARAM_INT], 'foo@bar.com', 'dave']);
$this->assertEquals("UPDATE users AS u SET name = ?, email = ?, is_val_even = (val + ?) % ? WHERE (u.id = ?) AND (emails.userid = u.id) AND (emails.email = ?) OR (u.name = ?) AND (subscriptions.userid = u.id) AND (subscriptions.type = 'customer') RETURNING u.id, u.name",
$query->getSql());
$this->assertEquals([
['david', PDO::PARAM_STR],
['bar@foo.com', PDO::PARAM_STR],
['1', PDO::PARAM_INT],
[2, PDO::PARAM_INT],
[18175, PDO::PARAM_INT],
['foo@bar.com', PDO::PARAM_STR],
['dave', PDO::PARAM_STR]
Expand Down
4 changes: 2 additions & 2 deletions src/Opulence/QueryBuilders/Tests/SelectQueryTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,7 @@ public function testBasicQueryWithAlias()
*/
public function testEverything()
{
$query = new SelectQuery('u.id', 'u.name', 'e.email');
$query = new SelectQuery('u.id', 'u.name', 'e.email', 'CHARACTER_LENGTH(e.email) AS email_length');
$query->addSelectExpression('p.password')
->from('users', 'u')
->innerJoin('log', 'l', 'l.userid = u.id')
Expand All @@ -181,7 +181,7 @@ public function testEverything()
->addOrderBy('u.name ASC')
->limit(2)
->offset(1);
$this->assertEquals('SELECT u.id, u.name, e.email, p.password FROM users AS u INNER JOIN log AS l ON l.userid = u.id LEFT JOIN emails AS e ON e.userid = u.id RIGHT JOIN password AS p ON p.userid = u.id WHERE (u.id <> 10) AND (u.name <> :notAllowedName) AND (u.id <> 9) OR (u.name = :allowedName) GROUP BY u.id, u.name, e.email, p.password HAVING (count(*) > :minCount) AND (count(*) < 5) OR (count(*) = 2) ORDER BY u.id DESC, u.name ASC LIMIT 2 OFFSET 1',
$this->assertEquals('SELECT u.id, u.name, e.email, CHARACTER_LENGTH(e.email) AS email_length, p.password FROM users AS u INNER JOIN log AS l ON l.userid = u.id LEFT JOIN emails AS e ON e.userid = u.id RIGHT JOIN password AS p ON p.userid = u.id WHERE (u.id <> 10) AND (u.name <> :notAllowedName) AND (u.id <> 9) OR (u.name = :allowedName) GROUP BY u.id, u.name, e.email, p.password HAVING (count(*) > :minCount) AND (count(*) < 5) OR (count(*) = 2) ORDER BY u.id DESC, u.name ASC LIMIT 2 OFFSET 1',
$query->getSql());
$this->assertEquals([
'notAllowedName' => ['dave', PDO::PARAM_STR],
Expand Down
36 changes: 32 additions & 4 deletions src/Opulence/QueryBuilders/Tests/UpdateQueryTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
namespace Opulence\QueryBuilders\Tests;

use Opulence\QueryBuilders\Conditions\ICondition;
use Opulence\QueryBuilders\Expression\Expression;
use Opulence\QueryBuilders\UpdateQuery;
use PDO;

Expand Down Expand Up @@ -62,22 +63,49 @@ public function testBasicQuery()
], $query->getParameters());
}

/**
* Tests a query with a simple UpsertExpression
*/
public function testSimpleExpression()
{
$query = new UpdateQuery('users', '', ['valid_until' => new Expression('NOW()')]);
$this->assertEquals('UPDATE users SET valid_until = NOW()', $query->getSql());
$this->assertEquals([], $query->getParameters());
}

/**
* Tests a query with a complex UpsertExpression
*/
public function testComplexExpression()
{
$query = new UpdateQuery('users', '',
['is_val_even' => new Expression('(val + ?) % ?', ['1', PDO::PARAM_INT], [2, PDO::PARAM_INT])]);
$this->assertEquals('UPDATE users SET is_val_even = (val + ?) % ?', $query->getSql());
$this->assertEquals([
['1', PDO::PARAM_INT],
[2, PDO::PARAM_INT],
], $query->getParameters());
}

/**
* Tests all the methods in a single, complicated query
*/
public function testEverything()
{
$expr = new Expression("(val + ?) % ?", ['1', PDO::PARAM_INT]);
$query = new UpdateQuery('users', 'u', ['name' => 'david']);
$query->addColumnValues(['email' => 'bar@foo.com'])
$query->addColumnValues(['email' => 'bar@foo.com', 'is_val_even' => $expr])
->where('u.id = ?', 'emails.userid = u.id', 'emails.email = ?')
->orWhere('u.name = ?')
->andWhere('subscriptions.userid = u.id', "subscriptions.type = 'customer'")
->addUnnamedPlaceholderValues([[18175, PDO::PARAM_INT], 'foo@bar.com', 'dave']);
$this->assertEquals("UPDATE users AS u SET name = ?, email = ? WHERE (u.id = ?) AND (emails.userid = u.id) AND (emails.email = ?) OR (u.name = ?) AND (subscriptions.userid = u.id) AND (subscriptions.type = 'customer')",
->addUnnamedPlaceholderValues([[2, PDO::PARAM_INT], [18175, PDO::PARAM_INT], 'foo@bar.com', 'dave']);
$this->assertEquals("UPDATE users AS u SET name = ?, email = ?, is_val_even = (val + ?) % ? WHERE (u.id = ?) AND (emails.userid = u.id) AND (emails.email = ?) OR (u.name = ?) AND (subscriptions.userid = u.id) AND (subscriptions.type = 'customer')",
$query->getSql());
$this->assertEquals([
$this->assertSame([
['david', PDO::PARAM_STR],
['bar@foo.com', PDO::PARAM_STR],
['1', PDO::PARAM_INT],
[2, PDO::PARAM_INT],
[18175, PDO::PARAM_INT],
['foo@bar.com', PDO::PARAM_STR],
['dave', PDO::PARAM_STR]
Expand Down
Loading

0 comments on commit bb647e2

Please sign in to comment.