Skip to content

Commit 11e3363

Browse files
[12.x]: Add UseEloquentBuilder attribute to register custom Eloquent Builder (#56025)
* feat: add UseEloquentBuilder attribute to register custom Eloquent Builders * fix: failing code style tests * fix: check if was resolved * Trigger Build * formatting --------- Co-authored-by: Taylor Otwell <taylor@laravel.com>
1 parent 49c617e commit 11e3363

File tree

3 files changed

+74
-0
lines changed

3 files changed

+74
-0
lines changed
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<?php
2+
3+
namespace Illuminate\Database\Eloquent\Attributes;
4+
5+
use Attribute;
6+
7+
#[Attribute(Attribute::TARGET_CLASS)]
8+
class UseEloquentBuilder
9+
{
10+
/**
11+
* Create a new attribute instance.
12+
*
13+
* @param class-string<\Illuminate\Database\Eloquent\Builder> $builderClass
14+
*/
15+
public function __construct(public string $builderClass)
16+
{
17+
}
18+
}

src/Illuminate/Database/Eloquent/Model.php

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
use Illuminate\Contracts\Support\Jsonable;
1414
use Illuminate\Database\ConnectionResolverInterface as Resolver;
1515
use Illuminate\Database\Eloquent\Attributes\Scope as LocalScope;
16+
use Illuminate\Database\Eloquent\Attributes\UseEloquentBuilder;
1617
use Illuminate\Database\Eloquent\Collection as EloquentCollection;
1718
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
1819
use Illuminate\Database\Eloquent\Relations\Concerns\AsPivot;
@@ -26,6 +27,7 @@
2627
use JsonException;
2728
use JsonSerializable;
2829
use LogicException;
30+
use ReflectionClass;
2931
use ReflectionMethod;
3032
use Stringable;
3133

@@ -1651,9 +1653,30 @@ public function newQueryForRestoration($ids)
16511653
*/
16521654
public function newEloquentBuilder($query)
16531655
{
1656+
$builderClass = $this->resolveCustomBuilderClass();
1657+
1658+
if ($builderClass && is_subclass_of($builderClass, Builder::class)) {
1659+
return new $builderClass($query);
1660+
}
1661+
16541662
return new static::$builder($query);
16551663
}
16561664

1665+
/**
1666+
* Resolve the custom Eloquent builder class from the model attributes.
1667+
*
1668+
* @return class-string<\Illuminate\Database\Eloquent\Builder>|false
1669+
*/
1670+
protected function resolveCustomBuilderClass()
1671+
{
1672+
$attributes = (new ReflectionClass($this))
1673+
->getAttributes(UseEloquentBuilder::class);
1674+
1675+
return ! empty($attributes)
1676+
? $attributes[0]->newInstance()->builderClass
1677+
: false;
1678+
}
1679+
16571680
/**
16581681
* Get a new query builder instance for the connection.
16591682
*

tests/Database/DatabaseEloquentModelTest.php

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3365,6 +3365,39 @@ public function testUseFactoryAttribute()
33653365
$this->assertEquals(EloquentModelWithUseFactoryAttribute::class, $factory->modelName());
33663366
$this->assertEquals('test name', $instance->name); // Small smoke test to ensure the factory is working
33673367
}
3368+
3369+
public function testUseCustomBuilderWithUseEloquentBuilderAttribute()
3370+
{
3371+
$model = new EloquentModelWithUseEloquentBuilderAttributeStub();
3372+
3373+
$query = $this->createMock(\Illuminate\Database\Query\Builder::class);
3374+
$eloquentBuilder = $model->newEloquentBuilder($query);
3375+
3376+
$this->assertInstanceOf(CustomBuilder::class, $eloquentBuilder);
3377+
}
3378+
3379+
public function testDefaultBuilderIsUsedWhenUseEloquentBuilderAttributeIsNotPresent()
3380+
{
3381+
$model = new EloquentModelWithoutUseEloquentBuilderAttributeStub();
3382+
3383+
$query = $this->createMock(\Illuminate\Database\Query\Builder::class);
3384+
$eloquentBuilder = $model->newEloquentBuilder($query);
3385+
3386+
$this->assertNotInstanceOf(CustomBuilder::class, $eloquentBuilder);
3387+
}
3388+
}
3389+
3390+
class CustomBuilder extends Builder
3391+
{
3392+
}
3393+
3394+
#[\Illuminate\Database\Eloquent\Attributes\UseEloquentBuilder(CustomBuilder::class)]
3395+
class EloquentModelWithUseEloquentBuilderAttributeStub extends Model
3396+
{
3397+
}
3398+
3399+
class EloquentModelWithoutUseEloquentBuilderAttributeStub extends Model
3400+
{
33683401
}
33693402

33703403
class EloquentTestObserverStub

0 commit comments

Comments
 (0)