From edccd52023891972cddb71a3a1f863285137d0d2 Mon Sep 17 00:00:00 2001 From: Raj Siva-Rajah <5361908+binaryfire@users.noreply.github.com> Date: Sun, 18 Jan 2026 18:06:06 +0000 Subject: [PATCH] Allow custom Query Builders via Connection Model, Pivot, and MorphPivot now delegate to $connection->query() when creating base query builders, enabling custom connections to provide custom query builders with additional methods. --- src/core/src/Database/Eloquent/Model.php | 16 ++ .../Eloquent/Relations/MorphPivot.php | 16 ++ .../src/Database/Eloquent/Relations/Pivot.php | 16 ++ .../Eloquent/NewBaseQueryBuilderTest.php | 161 ++++++++++++++++++ 4 files changed, 209 insertions(+) create mode 100644 tests/Core/Database/Eloquent/NewBaseQueryBuilderTest.php diff --git a/src/core/src/Database/Eloquent/Model.php b/src/core/src/Database/Eloquent/Model.php index fb6c06e37..c4e84a352 100644 --- a/src/core/src/Database/Eloquent/Model.php +++ b/src/core/src/Database/Eloquent/Model.php @@ -136,6 +136,22 @@ protected function resolveCustomBuilderClass(): string|false return $attributes[0]->newInstance()->builderClass; } + /** + * Get a new query builder instance for the connection. + * + * Delegates to the connection so custom connections can provide + * custom query builders with additional methods. + * + * @return \Hyperf\Database\Query\Builder + */ + protected function newBaseQueryBuilder() + { + /** @var \Hyperf\Database\Connection $connection */ + $connection = $this->getConnection(); + + return $connection->query(); + } + /** * @param array $models * @return \Hypervel\Database\Eloquent\Collection diff --git a/src/core/src/Database/Eloquent/Relations/MorphPivot.php b/src/core/src/Database/Eloquent/Relations/MorphPivot.php index cdfa47a3c..c3282b043 100644 --- a/src/core/src/Database/Eloquent/Relations/MorphPivot.php +++ b/src/core/src/Database/Eloquent/Relations/MorphPivot.php @@ -20,6 +20,22 @@ class MorphPivot extends BaseMorphPivot use HasObservers; use HasTimestamps; + /** + * Get a new query builder instance for the connection. + * + * Delegates to the connection so custom connections can provide + * custom query builders with additional methods. + * + * @return \Hyperf\Database\Query\Builder + */ + protected function newBaseQueryBuilder() + { + /** @var \Hyperf\Database\Connection $connection */ + $connection = $this->getConnection(); + + return $connection->query(); + } + /** * Delete the pivot model record from the database. * diff --git a/src/core/src/Database/Eloquent/Relations/Pivot.php b/src/core/src/Database/Eloquent/Relations/Pivot.php index 085b717ef..d11d1469f 100644 --- a/src/core/src/Database/Eloquent/Relations/Pivot.php +++ b/src/core/src/Database/Eloquent/Relations/Pivot.php @@ -20,6 +20,22 @@ class Pivot extends BasePivot use HasObservers; use HasTimestamps; + /** + * Get a new query builder instance for the connection. + * + * Delegates to the connection so custom connections can provide + * custom query builders with additional methods. + * + * @return \Hyperf\Database\Query\Builder + */ + protected function newBaseQueryBuilder() + { + /** @var \Hyperf\Database\Connection $connection */ + $connection = $this->getConnection(); + + return $connection->query(); + } + /** * Delete the pivot model record from the database. * diff --git a/tests/Core/Database/Eloquent/NewBaseQueryBuilderTest.php b/tests/Core/Database/Eloquent/NewBaseQueryBuilderTest.php new file mode 100644 index 000000000..9ca180853 --- /dev/null +++ b/tests/Core/Database/Eloquent/NewBaseQueryBuilderTest.php @@ -0,0 +1,161 @@ +shouldReceive('query')->once()->andReturn($customBuilder); + + $model = new NewBaseQueryBuilderTestModel(); + $model->setTestConnection($connection); + + $builder = $model->testNewBaseQueryBuilder(); + + $this->assertInstanceOf(CustomQueryBuilder::class, $builder); + $this->assertSame($customBuilder, $builder); + } + + public function testPivotUsesConnectionQueryMethod(): void + { + $customBuilder = new CustomQueryBuilder( + m::mock(ConnectionInterface::class), + new Grammar(), + new Processor() + ); + + $connection = m::mock(ConnectionInterface::class); + $connection->shouldReceive('query')->once()->andReturn($customBuilder); + + $pivot = new NewBaseQueryBuilderTestPivot(); + $pivot->setTestConnection($connection); + + $builder = $pivot->testNewBaseQueryBuilder(); + + $this->assertInstanceOf(CustomQueryBuilder::class, $builder); + $this->assertSame($customBuilder, $builder); + } + + public function testMorphPivotUsesConnectionQueryMethod(): void + { + $customBuilder = new CustomQueryBuilder( + m::mock(ConnectionInterface::class), + new Grammar(), + new Processor() + ); + + $connection = m::mock(ConnectionInterface::class); + $connection->shouldReceive('query')->once()->andReturn($customBuilder); + + $morphPivot = new NewBaseQueryBuilderTestMorphPivot(); + $morphPivot->setTestConnection($connection); + + $builder = $morphPivot->testNewBaseQueryBuilder(); + + $this->assertInstanceOf(CustomQueryBuilder::class, $builder); + $this->assertSame($customBuilder, $builder); + } +} + +// Test fixtures + +class NewBaseQueryBuilderTestModel extends Model +{ + protected ?string $table = 'test_models'; + + protected ?ConnectionInterface $testConnection = null; + + public function setTestConnection(ConnectionInterface $connection): void + { + $this->testConnection = $connection; + } + + public function getConnection(): ConnectionInterface + { + return $this->testConnection ?? parent::getConnection(); + } + + public function testNewBaseQueryBuilder(): QueryBuilder + { + return $this->newBaseQueryBuilder(); + } +} + +class NewBaseQueryBuilderTestPivot extends Pivot +{ + protected ?string $table = 'test_pivots'; + + protected ?ConnectionInterface $testConnection = null; + + public function setTestConnection(ConnectionInterface $connection): void + { + $this->testConnection = $connection; + } + + public function getConnection(): ConnectionInterface + { + return $this->testConnection ?? parent::getConnection(); + } + + public function testNewBaseQueryBuilder(): QueryBuilder + { + return $this->newBaseQueryBuilder(); + } +} + +class NewBaseQueryBuilderTestMorphPivot extends MorphPivot +{ + protected ?string $table = 'test_morph_pivots'; + + protected ?ConnectionInterface $testConnection = null; + + public function setTestConnection(ConnectionInterface $connection): void + { + $this->testConnection = $connection; + } + + public function getConnection(): ConnectionInterface + { + return $this->testConnection ?? parent::getConnection(); + } + + public function testNewBaseQueryBuilder(): QueryBuilder + { + return $this->newBaseQueryBuilder(); + } +} + +/** + * A custom query builder to verify the connection's builder is used. + */ +class CustomQueryBuilder extends QueryBuilder +{ +}