Query Builder

The QueryBuilder is the main entry point for building API endpoints. It ties together filtering, sorting, includes, and pagination into a single fluent API.

Basic Usage

use BlueBeetle\ApiToolkit\QueryBuilder;

class ProductController
{
    public function index(Request $request)
    {
        return QueryBuilder::for(Product::class, $request)
            ->fromResource(ProductResource::class)
            ->paginate();
    }
}

This single call handles:

  • Parsing ?filter[name]=widget and applying allowed filters
  • Parsing ?sort=-created_at,name and applying allowed sorts
  • Parsing ?include=category,tags and eager loading relationships
  • Parsing ?page[number]=2&page[size]=20 and paginating

From a Resource

The fromResource() method pulls filter, sort, and include configuration from your resource class:

class ProductResource extends Resource
{
    public function allowedFilters(): array
    {
        return [
            'name'     => new PartialFilter(),
            'status'   => new ExactFilter(),
            'category' => new ScopeFilter(),
        ];
    }

    public function allowedSorts(): array
    {
        return ['name', 'price', 'created_at'];
    }

    public function defaultSort(): ?string
    {
        return '-created_at';
    }

    public function allowedIncludes(): array
    {
        return ['category', 'tags'];
    }
}

Then in your controller, fromResource() picks up all of these automatically.

Overriding Resource Configuration

Method calls take priority over resource definitions:

QueryBuilder::for(Product::class, $request)
    ->fromResource(ProductResource::class)
    ->allowedFilters(['name' => new ExactFilter()]) // overrides resource filters
    ->allowedSorts(['name'])                         // overrides resource sorts
    ->defaultSort('name')                            // overrides resource default
    ->paginate();

From an Existing Builder

Pass an Eloquent builder instead of a model class to start with a scoped query:

$activeProducts = Product::where('is_active', true);

return QueryBuilder::for($activeProducts, $request)
    ->fromResource(ProductResource::class)
    ->paginate();

Pagination Methods

// Offset pagination: ?page[number]=2&page[size]=20
->paginate()

// Cursor pagination: ?page[cursor]=eyJpZCI6MTB9&page[size]=20
->cursorPaginate()

// No pagination, returns all results
->get()

Apply Without Fetching

Use apply() to apply filters, sorts, and includes without executing the query:

$builder = QueryBuilder::for(Product::class, $request)
    ->fromResource(ProductResource::class)
    ->apply();

$query = $builder->getQuery(); // Get the underlying Eloquent builder

// Do something custom with the query
$count = $query->count();