Skip to content

Commit

Permalink
[8.x] Add beforeQuery to base query builder (#37431)
Browse files Browse the repository at this point in the history
* add preserve to query builder

* Apply fixes from StyleCI

* formatting'

Co-authored-by: Taylor Otwell <taylorotwell@gmail.com>
  • Loading branch information
cbl and taylorotwell authored May 21, 2021
1 parent 13e5627 commit 8dcc5aa
Show file tree
Hide file tree
Showing 2 changed files with 165 additions and 0 deletions.
54 changes: 54 additions & 0 deletions src/Illuminate/Database/Query/Builder.php
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,13 @@ class Builder
*/
public $lock;

/**
* The callbacks that should be invoked before the query is executed.
*
* @var array
*/
public $beforeQueryCallbacks = [];

/**
* All of the available clause operators.
*
Expand Down Expand Up @@ -2256,13 +2263,42 @@ public function sharedLock()
return $this->lock(false);
}

/**
* Register a closure to be invoked before the query is executed.
*
* @param callable $callback
* @return $this
*/
public function beforeQuery(callable $callback)
{
$this->beforeQueryCallbacks[] = $callback;

return $this;
}

/**
* Invoke the "before query" modification callbacks.
*
* @return void
*/
public function applyBeforeQueryCallbacks()
{
foreach ($this->beforeQueryCallbacks as $callback) {
$callback($this);
}

$this->beforeQueryCallbacks = [];
}

/**
* Get the SQL representation of the query.
*
* @return string
*/
public function toSql()
{
$this->applyBeforeQueryCallbacks();

return $this->grammar->compileSelect($this);
}

Expand Down Expand Up @@ -2663,6 +2699,8 @@ public function implode($column, $glue = '')
*/
public function exists()
{
$this->applyBeforeQueryCallbacks();

$results = $this->connection->select(
$this->grammar->compileExists($this), $this->getBindings(), ! $this->useWritePdo
);
Expand Down Expand Up @@ -2901,6 +2939,8 @@ public function insert(array $values)
}
}

$this->applyBeforeQueryCallbacks();

// Finally, we will run this query against the database connection and return
// the results. We will need to also flatten these bindings before running
// the query so they are all in one huge, flattened array for execution.
Expand Down Expand Up @@ -2931,6 +2971,8 @@ public function insertOrIgnore(array $values)
}
}

$this->applyBeforeQueryCallbacks();

return $this->connection->affectingStatement(
$this->grammar->compileInsertOrIgnore($this, $values),
$this->cleanBindings(Arr::flatten($values, 1))
Expand All @@ -2946,6 +2988,8 @@ public function insertOrIgnore(array $values)
*/
public function insertGetId(array $values, $sequence = null)
{
$this->applyBeforeQueryCallbacks();

$sql = $this->grammar->compileInsertGetId($this, $values, $sequence);

$values = $this->cleanBindings($values);
Expand All @@ -2962,6 +3006,8 @@ public function insertGetId(array $values, $sequence = null)
*/
public function insertUsing(array $columns, $query)
{
$this->applyBeforeQueryCallbacks();

[$sql, $bindings] = $this->createSub($query);

return $this->connection->affectingStatement(
Expand All @@ -2978,6 +3024,8 @@ public function insertUsing(array $columns, $query)
*/
public function update(array $values)
{
$this->applyBeforeQueryCallbacks();

$sql = $this->grammar->compileUpdate($this, $values);

return $this->connection->update($sql, $this->cleanBindings(
Expand Down Expand Up @@ -3035,6 +3083,8 @@ public function upsert(array $values, $uniqueBy, $update = null)
$update = array_keys(reset($values));
}

$this->applyBeforeQueryCallbacks();

$bindings = $this->cleanBindings(array_merge(
Arr::flatten($values, 1),
collect($update)->reject(function ($value, $key) {
Expand Down Expand Up @@ -3109,6 +3159,8 @@ public function delete($id = null)
$this->where($this->from.'.id', '=', $id);
}

$this->applyBeforeQueryCallbacks();

return $this->connection->delete(
$this->grammar->compileDelete($this), $this->cleanBindings(
$this->grammar->prepareBindingsForDelete($this->bindings)
Expand All @@ -3123,6 +3175,8 @@ public function delete($id = null)
*/
public function truncate()
{
$this->applyBeforeQueryCallbacks();

foreach ($this->grammar->compileTruncate($this) as $sql => $bindings) {
$this->connection->statement($sql, $bindings);
}
Expand Down
111 changes: 111 additions & 0 deletions tests/Database/DatabaseQueryBuilderTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
namespace Illuminate\Tests\Database;

use BadMethodCallException;
use Closure;
use DateTime;
use Illuminate\Database\ConnectionInterface;
use Illuminate\Database\Eloquent\Builder as EloquentBuilder;
Expand Down Expand Up @@ -2563,6 +2564,116 @@ public function testTruncateMethod()
], $sqlite->compileTruncate($builder));
}

public function testPreserveAddsClosureToArray()
{
$builder = $this->getBuilder();
$builder->beforeQuery(function () {
});
$this->assertCount(1, $builder->beforeQueryCallbacks);
$this->assertInstanceOf(Closure::class, $builder->beforeQueryCallbacks[0]);
}

public function testApplyPreserveCleansArray()
{
$builder = $this->getBuilder();
$builder->beforeQuery(function () {
});
$this->assertCount(1, $builder->beforeQueryCallbacks);
$builder->applyBeforeQueryCallbacks();
$this->assertCount(0, $builder->beforeQueryCallbacks);
}

public function testPreservedAreAppliedByToSql()
{
$builder = $this->getBuilder();
$builder->beforeQuery(function ($builder) {
$builder->where('foo', 'bar');
});
$this->assertSame('select * where "foo" = ?', $builder->toSql());
$this->assertEquals(['bar'], $builder->getBindings());
}

public function testPreservedAreAppliedByInsert()
{
$builder = $this->getBuilder();
$builder->getConnection()->shouldReceive('insert')->once()->with('insert into "users" ("email") values (?)', ['foo']);
$builder->beforeQuery(function ($builder) {
$builder->from('users');
});
$builder->insert(['email' => 'foo']);
}

public function testPreservedAreAppliedByInsertGetId()
{
$this->called = false;
$builder = $this->getBuilder();
$builder->getProcessor()->shouldReceive('processInsertGetId')->once()->with($builder, 'insert into "users" ("email") values (?)', ['foo'], 'id');
$builder->beforeQuery(function ($builder) {
$builder->from('users');
});
$builder->insertGetId(['email' => 'foo'], 'id');
}

public function testPreservedAreAppliedByInsertUsing()
{
$builder = $this->getBuilder();
$builder->getConnection()->shouldReceive('affectingStatement')->once()->with('insert into "users" () select *', []);
$builder->beforeQuery(function ($builder) {
$builder->from('users');
});
$builder->insertUsing([], $this->getBuilder());
}

public function testPreservedAreAppliedByUpsert()
{
$builder = $this->getMySqlBuilder();
$builder->getConnection()->shouldReceive('affectingStatement')->once()->with('insert into `users` (`email`) values (?) on duplicate key update `email` = values(`email`)', ['foo']);
$builder->beforeQuery(function ($builder) {
$builder->from('users');
});
$builder->upsert(['email' => 'foo'], 'id');
}

public function testPreservedAreAppliedByUpdate()
{
$builder = $this->getBuilder();
$builder->getConnection()->shouldReceive('update')->once()->with('update "users" set "email" = ? where "id" = ?', ['foo', 1]);
$builder->from('users')->beforeQuery(function ($builder) {
$builder->where('id', 1);
});
$builder->update(['email' => 'foo']);
}

public function testPreservedAreAppliedByDelete()
{
$builder = $this->getBuilder();
$builder->getConnection()->shouldReceive('delete')->once()->with('delete from "users"', []);
$builder->beforeQuery(function ($builder) {
$builder->from('users');
});
$builder->delete();
}

public function testPreservedAreAppliedByTruncate()
{
$builder = $this->getBuilder();
$builder->getConnection()->shouldReceive('statement')->once()->with('truncate table "users"', []);
$builder->beforeQuery(function ($builder) {
$builder->from('users');
});
$builder->truncate();
}

public function testPreservedAreAppliedByExists()
{
$builder = $this->getBuilder();
$builder->getConnection()->shouldReceive('select')->once()->with('select exists(select * from "users") as "exists"', [], true);
$builder->beforeQuery(function ($builder) {
$builder->from('users');
});
$builder->exists();
}

public function testPostgresInsertGetId()
{
$builder = $this->getPostgresBuilder();
Expand Down

0 comments on commit 8dcc5aa

Please sign in to comment.