Skip to content

Commit 8dcc5aa

Browse files
cbltaylorotwell
andauthored
[8.x] Add beforeQuery to base query builder (#37431)
* add preserve to query builder * Apply fixes from StyleCI * formatting' Co-authored-by: Taylor Otwell <[email protected]>
1 parent 13e5627 commit 8dcc5aa

File tree

2 files changed

+165
-0
lines changed

2 files changed

+165
-0
lines changed

src/Illuminate/Database/Query/Builder.php

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,13 @@ class Builder
182182
*/
183183
public $lock;
184184

185+
/**
186+
* The callbacks that should be invoked before the query is executed.
187+
*
188+
* @var array
189+
*/
190+
public $beforeQueryCallbacks = [];
191+
185192
/**
186193
* All of the available clause operators.
187194
*
@@ -2256,13 +2263,42 @@ public function sharedLock()
22562263
return $this->lock(false);
22572264
}
22582265

2266+
/**
2267+
* Register a closure to be invoked before the query is executed.
2268+
*
2269+
* @param callable $callback
2270+
* @return $this
2271+
*/
2272+
public function beforeQuery(callable $callback)
2273+
{
2274+
$this->beforeQueryCallbacks[] = $callback;
2275+
2276+
return $this;
2277+
}
2278+
2279+
/**
2280+
* Invoke the "before query" modification callbacks.
2281+
*
2282+
* @return void
2283+
*/
2284+
public function applyBeforeQueryCallbacks()
2285+
{
2286+
foreach ($this->beforeQueryCallbacks as $callback) {
2287+
$callback($this);
2288+
}
2289+
2290+
$this->beforeQueryCallbacks = [];
2291+
}
2292+
22592293
/**
22602294
* Get the SQL representation of the query.
22612295
*
22622296
* @return string
22632297
*/
22642298
public function toSql()
22652299
{
2300+
$this->applyBeforeQueryCallbacks();
2301+
22662302
return $this->grammar->compileSelect($this);
22672303
}
22682304

@@ -2663,6 +2699,8 @@ public function implode($column, $glue = '')
26632699
*/
26642700
public function exists()
26652701
{
2702+
$this->applyBeforeQueryCallbacks();
2703+
26662704
$results = $this->connection->select(
26672705
$this->grammar->compileExists($this), $this->getBindings(), ! $this->useWritePdo
26682706
);
@@ -2901,6 +2939,8 @@ public function insert(array $values)
29012939
}
29022940
}
29032941

2942+
$this->applyBeforeQueryCallbacks();
2943+
29042944
// Finally, we will run this query against the database connection and return
29052945
// the results. We will need to also flatten these bindings before running
29062946
// the query so they are all in one huge, flattened array for execution.
@@ -2931,6 +2971,8 @@ public function insertOrIgnore(array $values)
29312971
}
29322972
}
29332973

2974+
$this->applyBeforeQueryCallbacks();
2975+
29342976
return $this->connection->affectingStatement(
29352977
$this->grammar->compileInsertOrIgnore($this, $values),
29362978
$this->cleanBindings(Arr::flatten($values, 1))
@@ -2946,6 +2988,8 @@ public function insertOrIgnore(array $values)
29462988
*/
29472989
public function insertGetId(array $values, $sequence = null)
29482990
{
2991+
$this->applyBeforeQueryCallbacks();
2992+
29492993
$sql = $this->grammar->compileInsertGetId($this, $values, $sequence);
29502994

29512995
$values = $this->cleanBindings($values);
@@ -2962,6 +3006,8 @@ public function insertGetId(array $values, $sequence = null)
29623006
*/
29633007
public function insertUsing(array $columns, $query)
29643008
{
3009+
$this->applyBeforeQueryCallbacks();
3010+
29653011
[$sql, $bindings] = $this->createSub($query);
29663012

29673013
return $this->connection->affectingStatement(
@@ -2978,6 +3024,8 @@ public function insertUsing(array $columns, $query)
29783024
*/
29793025
public function update(array $values)
29803026
{
3027+
$this->applyBeforeQueryCallbacks();
3028+
29813029
$sql = $this->grammar->compileUpdate($this, $values);
29823030

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

3086+
$this->applyBeforeQueryCallbacks();
3087+
30383088
$bindings = $this->cleanBindings(array_merge(
30393089
Arr::flatten($values, 1),
30403090
collect($update)->reject(function ($value, $key) {
@@ -3109,6 +3159,8 @@ public function delete($id = null)
31093159
$this->where($this->from.'.id', '=', $id);
31103160
}
31113161

3162+
$this->applyBeforeQueryCallbacks();
3163+
31123164
return $this->connection->delete(
31133165
$this->grammar->compileDelete($this), $this->cleanBindings(
31143166
$this->grammar->prepareBindingsForDelete($this->bindings)
@@ -3123,6 +3175,8 @@ public function delete($id = null)
31233175
*/
31243176
public function truncate()
31253177
{
3178+
$this->applyBeforeQueryCallbacks();
3179+
31263180
foreach ($this->grammar->compileTruncate($this) as $sql => $bindings) {
31273181
$this->connection->statement($sql, $bindings);
31283182
}

tests/Database/DatabaseQueryBuilderTest.php

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
namespace Illuminate\Tests\Database;
44

55
use BadMethodCallException;
6+
use Closure;
67
use DateTime;
78
use Illuminate\Database\ConnectionInterface;
89
use Illuminate\Database\Eloquent\Builder as EloquentBuilder;
@@ -2563,6 +2564,116 @@ public function testTruncateMethod()
25632564
], $sqlite->compileTruncate($builder));
25642565
}
25652566

2567+
public function testPreserveAddsClosureToArray()
2568+
{
2569+
$builder = $this->getBuilder();
2570+
$builder->beforeQuery(function () {
2571+
});
2572+
$this->assertCount(1, $builder->beforeQueryCallbacks);
2573+
$this->assertInstanceOf(Closure::class, $builder->beforeQueryCallbacks[0]);
2574+
}
2575+
2576+
public function testApplyPreserveCleansArray()
2577+
{
2578+
$builder = $this->getBuilder();
2579+
$builder->beforeQuery(function () {
2580+
});
2581+
$this->assertCount(1, $builder->beforeQueryCallbacks);
2582+
$builder->applyBeforeQueryCallbacks();
2583+
$this->assertCount(0, $builder->beforeQueryCallbacks);
2584+
}
2585+
2586+
public function testPreservedAreAppliedByToSql()
2587+
{
2588+
$builder = $this->getBuilder();
2589+
$builder->beforeQuery(function ($builder) {
2590+
$builder->where('foo', 'bar');
2591+
});
2592+
$this->assertSame('select * where "foo" = ?', $builder->toSql());
2593+
$this->assertEquals(['bar'], $builder->getBindings());
2594+
}
2595+
2596+
public function testPreservedAreAppliedByInsert()
2597+
{
2598+
$builder = $this->getBuilder();
2599+
$builder->getConnection()->shouldReceive('insert')->once()->with('insert into "users" ("email") values (?)', ['foo']);
2600+
$builder->beforeQuery(function ($builder) {
2601+
$builder->from('users');
2602+
});
2603+
$builder->insert(['email' => 'foo']);
2604+
}
2605+
2606+
public function testPreservedAreAppliedByInsertGetId()
2607+
{
2608+
$this->called = false;
2609+
$builder = $this->getBuilder();
2610+
$builder->getProcessor()->shouldReceive('processInsertGetId')->once()->with($builder, 'insert into "users" ("email") values (?)', ['foo'], 'id');
2611+
$builder->beforeQuery(function ($builder) {
2612+
$builder->from('users');
2613+
});
2614+
$builder->insertGetId(['email' => 'foo'], 'id');
2615+
}
2616+
2617+
public function testPreservedAreAppliedByInsertUsing()
2618+
{
2619+
$builder = $this->getBuilder();
2620+
$builder->getConnection()->shouldReceive('affectingStatement')->once()->with('insert into "users" () select *', []);
2621+
$builder->beforeQuery(function ($builder) {
2622+
$builder->from('users');
2623+
});
2624+
$builder->insertUsing([], $this->getBuilder());
2625+
}
2626+
2627+
public function testPreservedAreAppliedByUpsert()
2628+
{
2629+
$builder = $this->getMySqlBuilder();
2630+
$builder->getConnection()->shouldReceive('affectingStatement')->once()->with('insert into `users` (`email`) values (?) on duplicate key update `email` = values(`email`)', ['foo']);
2631+
$builder->beforeQuery(function ($builder) {
2632+
$builder->from('users');
2633+
});
2634+
$builder->upsert(['email' => 'foo'], 'id');
2635+
}
2636+
2637+
public function testPreservedAreAppliedByUpdate()
2638+
{
2639+
$builder = $this->getBuilder();
2640+
$builder->getConnection()->shouldReceive('update')->once()->with('update "users" set "email" = ? where "id" = ?', ['foo', 1]);
2641+
$builder->from('users')->beforeQuery(function ($builder) {
2642+
$builder->where('id', 1);
2643+
});
2644+
$builder->update(['email' => 'foo']);
2645+
}
2646+
2647+
public function testPreservedAreAppliedByDelete()
2648+
{
2649+
$builder = $this->getBuilder();
2650+
$builder->getConnection()->shouldReceive('delete')->once()->with('delete from "users"', []);
2651+
$builder->beforeQuery(function ($builder) {
2652+
$builder->from('users');
2653+
});
2654+
$builder->delete();
2655+
}
2656+
2657+
public function testPreservedAreAppliedByTruncate()
2658+
{
2659+
$builder = $this->getBuilder();
2660+
$builder->getConnection()->shouldReceive('statement')->once()->with('truncate table "users"', []);
2661+
$builder->beforeQuery(function ($builder) {
2662+
$builder->from('users');
2663+
});
2664+
$builder->truncate();
2665+
}
2666+
2667+
public function testPreservedAreAppliedByExists()
2668+
{
2669+
$builder = $this->getBuilder();
2670+
$builder->getConnection()->shouldReceive('select')->once()->with('select exists(select * from "users") as "exists"', [], true);
2671+
$builder->beforeQuery(function ($builder) {
2672+
$builder->from('users');
2673+
});
2674+
$builder->exists();
2675+
}
2676+
25662677
public function testPostgresInsertGetId()
25672678
{
25682679
$builder = $this->getPostgresBuilder();

0 commit comments

Comments
 (0)