Skip to content

Custom Query Builder ​

Introduction ​

A Custom Query Builder extends Laravel's Illuminate\Database\Eloquent\Builder, letting you add custom query methods to your models. This encapsulates complex query logic so code stays readable, maintainable, and reusable.

Why use a Custom Query Builder?

  • Reuse: Keep complex query logic in one place and call it from many callers.
  • Readability: Instead of a long chain of where conditions, call a clearly named method such as Product::query()->hasNoBom().
  • Maintainability: Query logic lives in a single class, so changes and extensions stay simple.

Implementation ​

Implementation has two main steps: create the Builder class, then register it on the Model.

1. Create the Custom Query Builder class ​

Create a new class that extends Illuminate\Database\Eloquent\Builder. Define your custom query methods there.

Example with ProductBuilder:

php
final class ProductBuilder extends Builder
{
    public function hasNoBom(): self
    {
        return $this->whereDoesntHave('boms');
    }
}

2. Register the Builder on the Model ​

On the corresponding model (for example, Product), override newEloquentBuilder() to return an instance of your custom builder.

php
public function newEloquentBuilder($query): ProductBuilder
{
    return new ProductBuilder($query);
}

3. Add PHPDoc to the Model ​

So IDEs (such as PhpStorm) understand Product::query() and provide accurate type-hinting, add a PHPDoc block with a @method annotation:

php
/**
 * @method static ProductBuilder query()
 */
final class Product extends Model {}

Usage ​

After registration, call your custom methods directly from the model.

php
$products = Product::query()
    ->hasNoBom()
    ->withCategory()
    ->get();

This keeps query logic neatly encapsulated in ProductBuilder, so Action/Service code stays much cleaner and easier to follow.

Internal engineering documentation