From 33e9125c11f67d0de5f51b27b80c17c2af2b07cc Mon Sep 17 00:00:00 2001 From: Enzo Innocenzi Date: Sat, 2 Aug 2025 15:02:11 +0200 Subject: [PATCH] feat(database): add `tap` to query builders --- .../QueryBuilders/CountQueryBuilder.php | 7 +++- .../QueryBuilders/DeleteQueryBuilder.php | 7 +++- .../QueryBuilders/InsertQueryBuilder.php | 2 +- .../QueryBuilders/SelectQueryBuilder.php | 12 +++--- .../QueryBuilders/TapsQueryBuilder.php | 21 ++++++++++ .../QueryBuilders/UpdateQueryBuilder.php | 7 +++- packages/support/src/functions.php | 4 +- .../Builder/CountQueryBuilderTest.php | 17 ++++++++ .../Builder/DeleteQueryBuilderTest.php | 15 +++++++ .../Builder/SelectQueryBuilderTest.php | 41 +++++++++++++++++++ .../Builder/UpdateQueryBuilderTest.php | 16 ++++++++ 11 files changed, 137 insertions(+), 12 deletions(-) create mode 100644 packages/database/src/Builder/QueryBuilders/TapsQueryBuilder.php diff --git a/packages/database/src/Builder/QueryBuilders/CountQueryBuilder.php b/packages/database/src/Builder/QueryBuilders/CountQueryBuilder.php index c8bf91c93..b37edf461 100644 --- a/packages/database/src/Builder/QueryBuilders/CountQueryBuilder.php +++ b/packages/database/src/Builder/QueryBuilders/CountQueryBuilder.php @@ -21,7 +21,7 @@ */ final class CountQueryBuilder implements BuildsQuery { - use HasConditions, OnDatabase, HasWhereQueryBuilderMethods; + use HasConditions, OnDatabase, HasWhereQueryBuilderMethods, TapsQueryBuilder; private CountStatement $count; @@ -86,4 +86,9 @@ private function getModel(): ModelInspector { return $this->model; } + + private function clone(): self + { + return clone $this; + } } diff --git a/packages/database/src/Builder/QueryBuilders/DeleteQueryBuilder.php b/packages/database/src/Builder/QueryBuilders/DeleteQueryBuilder.php index d8d3ed940..4864c017e 100644 --- a/packages/database/src/Builder/QueryBuilders/DeleteQueryBuilder.php +++ b/packages/database/src/Builder/QueryBuilders/DeleteQueryBuilder.php @@ -18,7 +18,7 @@ */ final class DeleteQueryBuilder implements BuildsQuery { - use HasConditions, OnDatabase, HasWhereQueryBuilderMethods; + use HasConditions, OnDatabase, HasWhereQueryBuilderMethods, TapsQueryBuilder; private DeleteStatement $delete; @@ -81,4 +81,9 @@ private function getModel(): ModelInspector { return $this->model; } + + private function clone(): self + { + return clone $this; + } } diff --git a/packages/database/src/Builder/QueryBuilders/InsertQueryBuilder.php b/packages/database/src/Builder/QueryBuilders/InsertQueryBuilder.php index 82aad21e3..b3abed94e 100644 --- a/packages/database/src/Builder/QueryBuilders/InsertQueryBuilder.php +++ b/packages/database/src/Builder/QueryBuilders/InsertQueryBuilder.php @@ -19,7 +19,7 @@ final class InsertQueryBuilder implements BuildsQuery { - use HasConditions, OnDatabase; + use HasConditions, OnDatabase, TapsQueryBuilder; private InsertStatement $insert; diff --git a/packages/database/src/Builder/QueryBuilders/SelectQueryBuilder.php b/packages/database/src/Builder/QueryBuilders/SelectQueryBuilder.php index 6d971baee..be5aecd86 100644 --- a/packages/database/src/Builder/QueryBuilders/SelectQueryBuilder.php +++ b/packages/database/src/Builder/QueryBuilders/SelectQueryBuilder.php @@ -33,7 +33,7 @@ */ final class SelectQueryBuilder implements BuildsQuery { - use HasConditions, OnDatabase, HasWhereQueryBuilderMethods; + use HasConditions, OnDatabase, HasWhereQueryBuilderMethods, TapsQueryBuilder; private ModelInspector $model; @@ -232,11 +232,6 @@ public function build(mixed ...$bindings): Query return new Query($this->select, [...$this->bindings, ...$bindings])->onDatabase($this->onDatabase); } - private function clone(): self - { - return clone $this; - } - /** @return \Tempest\Database\Relation[] */ private function getIncludedRelations(): array { @@ -270,4 +265,9 @@ private function getModel(): ModelInspector { return $this->model; } + + private function clone(): self + { + return clone $this; + } } diff --git a/packages/database/src/Builder/QueryBuilders/TapsQueryBuilder.php b/packages/database/src/Builder/QueryBuilders/TapsQueryBuilder.php new file mode 100644 index 000000000..caa822a07 --- /dev/null +++ b/packages/database/src/Builder/QueryBuilders/TapsQueryBuilder.php @@ -0,0 +1,21 @@ +clone(), $callback); + } +} diff --git a/packages/database/src/Builder/QueryBuilders/UpdateQueryBuilder.php b/packages/database/src/Builder/QueryBuilders/UpdateQueryBuilder.php index e6413b4eb..cb7eeb153 100644 --- a/packages/database/src/Builder/QueryBuilders/UpdateQueryBuilder.php +++ b/packages/database/src/Builder/QueryBuilders/UpdateQueryBuilder.php @@ -24,7 +24,7 @@ */ final class UpdateQueryBuilder implements BuildsQuery { - use HasConditions, OnDatabase, HasWhereQueryBuilderMethods; + use HasConditions, OnDatabase, HasWhereQueryBuilderMethods, TapsQueryBuilder; private UpdateStatement $update; @@ -153,4 +153,9 @@ private function getModel(): ModelInspector { return $this->model; } + + private function clone(): self + { + return clone $this; + } } diff --git a/packages/support/src/functions.php b/packages/support/src/functions.php index 2111811e4..eb250f45b 100644 --- a/packages/support/src/functions.php +++ b/packages/support/src/functions.php @@ -39,11 +39,11 @@ function path(Stringable|string ...$parts): Path * @template T * * @param T $value - * @param (Closure(T): void) $callback + * @param (callable(T): void) $callback * * @return T */ - function tap(mixed $value, Closure $callback): mixed + function tap(mixed $value, callable $callback): mixed { $callback($value); diff --git a/tests/Integration/Database/Builder/CountQueryBuilderTest.php b/tests/Integration/Database/Builder/CountQueryBuilderTest.php index 6e8011cac..e71fd5f49 100644 --- a/tests/Integration/Database/Builder/CountQueryBuilderTest.php +++ b/tests/Integration/Database/Builder/CountQueryBuilderTest.php @@ -212,4 +212,21 @@ public function test_multiple_where_field(): void $this->assertSameWithoutBackticks($expected, $sql); } + + public function test_tap(): void + { + $this->migrate(CreateMigrationsTable::class, CreatePublishersTable::class, CreateAuthorTable::class); + + query('authors')->insert( + ['id' => 1, 'name' => 'Brent'], + ['id' => 2, 'name' => 'Other'], + )->execute(); + + $count = query('authors') + ->count() + ->tap(fn (CountQueryBuilder $query) => $query->where('id = ?', 1)) + ->execute(); + + $this->assertSame(1, $count); + } } diff --git a/tests/Integration/Database/Builder/DeleteQueryBuilderTest.php b/tests/Integration/Database/Builder/DeleteQueryBuilderTest.php index 70ddfa36f..ede1c3487 100644 --- a/tests/Integration/Database/Builder/DeleteQueryBuilderTest.php +++ b/tests/Integration/Database/Builder/DeleteQueryBuilderTest.php @@ -154,4 +154,19 @@ public function test_multiple_where_field(): void $this->assertSameWithoutBackticks($expected, $sql); } + + public function test_tap(): void + { + $query = query('foo') + ->delete() + ->tap(fn (DeleteQueryBuilder $query) => $query->where('bar = ?', 'boo')) + ->build(); + + $this->assertSameWithoutBackticks(<<toSql()); + + $this->assertSameWithoutBackticks('boo', $query->bindings[0]); + } } diff --git a/tests/Integration/Database/Builder/SelectQueryBuilderTest.php b/tests/Integration/Database/Builder/SelectQueryBuilderTest.php index 565a3480d..f46d4d310 100644 --- a/tests/Integration/Database/Builder/SelectQueryBuilderTest.php +++ b/tests/Integration/Database/Builder/SelectQueryBuilderTest.php @@ -424,6 +424,47 @@ public function test_group_by(): void $this->assertSameWithoutBackticks($expected, $sql); } + public function test_tap(): void + { + $sql = query('authors') + ->select() + ->tap(fn (SelectQueryBuilder $query) => $query->where('name = ?', 'Brent')) + ->toSql(); + + $expected = <<assertSameWithoutBackticks($expected, $sql); + } + + public function test_tap_invokable(): void + { + $sql = query('authors') + ->select() + ->tap(new readonly class('Brent') { + public function __construct( + private string $author, + ) {} + + public function __invoke(SelectQueryBuilder $query): void + { + $query->where('name = ?', $this->author); + } + }) + ->toSql(); + + $expected = <<assertSameWithoutBackticks($expected, $sql); + } + public function test_having(): void { $sql = query('authors') diff --git a/tests/Integration/Database/Builder/UpdateQueryBuilderTest.php b/tests/Integration/Database/Builder/UpdateQueryBuilderTest.php index d5cb9edfc..9e8cf3126 100644 --- a/tests/Integration/Database/Builder/UpdateQueryBuilderTest.php +++ b/tests/Integration/Database/Builder/UpdateQueryBuilderTest.php @@ -331,4 +331,20 @@ public function test_multiple_where_field(): void $this->assertSameWithoutBackticks($expected, $sql); } + + public function test_tap(): void + { + $query = query(Book::class) + ->update(title: 'Chapter 02') + ->tap(fn (UpdateQueryBuilder $query) => $query->where('id = ?', 10)) + ->build(); + + $this->assertSameWithoutBackticks(<<toSql()); + + $this->assertSame(['Chapter 02', 10], $query->bindings); + } }