Laravel query builders
Skill noppu-labs/ai-toolkit/laravel/skills/laravel-query-builders
Toolkit for agentic development of Laravel & React apps
npx -y skills add noppu-labs/ai-toolkit --skill laravel-query-buildersAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 1 stars1 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
What its author says it does
Copied from the file, not written here
Custom query builders for type-safe, composable database queries. Use when creating or modifying query builders, query scopes, or complex database queries.
SKILL.md
6.2 KB, as published. Nobody here has run it
Laravel Query Builders
Always use custom query builders instead of local scopes.
Related guides:
- Models - Model integration with custom builders
- Controllers - Using query objects in controllers
Why Custom Builders Over Scopes
❌ Do NOT use local scopes.
✅ Use custom query builders because they provide:
- Better type hinting - Full IDE autocomplete
- Type-safe nested queries - Type-hint closures in
whereHas(),orWhereHas(), etc. - Better organization - All query logic in one class
- More composable - Easier to chain and compose
- Easier testing - Test query logic in isolation
Basic Builder Structure
<?php
declare(strict_types=1);
namespace App\Builders;
use App\Enums\OrderStatus;
use App\Models\User;
use Carbon\Carbon;
use Illuminate\Database\Eloquent\Builder;
class OrderBuilder extends Builder
{
public function wherePending(): self
{
return $this->where('status', OrderStatus::Pending);
}
public function whereCompleted(): self
{
return $this->where('status', OrderStatus::Completed);
}
public function whereCustomer(User|int $customer): self
{
$customerId = $customer instanceof User ? $customer->id : $customer;
return $this->where('customer_id', $customerId);
}
public function whereTotalGreaterThan(int $amount): self
{
return $this->where('total', '>', $amount);
}
public function wherePlacedBetween(Carbon $start, Carbon $end): self
{
return $this->whereBetween('placed_at', [$start, $end]);
}
public function withRelated(): self
{
return $this->with(['customer', 'items.product', 'shipments']);
}
public function recent(): self
{
return $this->latest('placed_at');
}
}
Type-Safe Nested Queries
Type-hint closures for full IDE support in relationship queries:
public function whereHasItems(array|string $productIds): self
{
return $this->whereHas('items', function (OrderItemBuilder $query) use ($productIds): void {
$query->whereIn('product_id', (array) $productIds);
});
}
Usage:
Order::query()
->whereHas('items', function (OrderItemBuilder $query): void {
$query->whereActive() // Custom method - autocomplete works!
->whereProduct($id); // Full type safety!
})
->whereHas('customer', function (CustomerBuilder $query): void {
$query->whereVerified() // Custom method
->wherePremium(); // IDE knows all methods!
})
->get();
PHPDoc for External Methods
Document methods from Spatie packages or macros:
/**
* @method static OrderBuilder whereState(string $column, string|array $state)
* @method static OrderBuilder whereNotState(string $column, string|array $state)
*/
class OrderBuilder extends Builder
{
// ...
}
Builder Traits
Extract reusable query logic:
<?php
declare(strict_types=1);
namespace App\Builders\Concerns;
use Illuminate\Support\Arr;
trait HasProducts
{
public function whereHasProducts(array|string $productIds): self
{
return $this->whereHas('products', function ($query) use ($productIds): void {
$query->whereIn('id', Arr::wrap($productIds));
});
}
public function whereHasActiveProducts(): self
{
return $this->whereHas('products', function ($query): void {
$query->where('active', true);
});
}
}
Usage in builder:
class OrderBuilder extends Builder
{
use HasProducts;
// ...
}
Register Builder in Model
Preferred: PHP Attribute (Laravel 12+)
use Illuminate\Database\Eloquent\Attributes\UseEloquentBuilder;
#[UseEloquentBuilder(OrderBuilder::class)]
class Order extends Model
{
// ...
}
Fallback: Method Override (pre-Laravel 12)
public function newEloquentBuilder($query): OrderBuilder
{
return new OrderBuilder($query);
}
Usage Examples
Basic Chaining
Order::query()
->wherePending()
->whereTotalGreaterThan(10000)
->wherePlacedBetween(now()->subWeek(), now())
->withRelated()
->recent()
->paginate();
Lazy Iteration
Order::query()
->whereCompleted()
->lazyById()
->each(function (Order $order): void {
// Process order
});
Complex Queries
$orders = Order::query()
->wherePending()
->whereCustomer($user)
->whereHasItems([$productId1, $productId2])
->wherePlacedBetween($startDate, $endDate)
->withRelated()
->get();
Empty Builders
Always create builders even if empty initially - for future extensibility:
<?php
declare(strict_types=1);
namespace App\Builders;
use Illuminate\Database\Eloquent\Builder;
class CustomerBuilder extends Builder
{
// Empty for now, but ready for future methods
}
Common Builder Methods
Status Filtering
public function whereActive(): self
{
return $this->where('status', 'active');
}
public function whereInactive(): self
{
return $this->where('status', 'inactive');
}
Date Ranges
public function whereCreatedAfter(Carbon $date): self
{
return $this->where('created_at', '>', $date);
}
public function whereCreatedToday(): self
{
return $this->whereDate('created_at', today());
}
User/Owner Filtering
public function whereUser(User|int $user): self
{
$userId = $user instanceof User ? $user->id : $user;
return $this->where('user_id', $userId);
}
Relationship Loading
public function withFullRelations(): self
{
return $this->with([
'user',
'items.product',
'customer.address',
]);
}
Builder Organization
app/Builders/
├── OrderBuilder.php
├── CustomerBuilder.php
├── ProductBuilder.php
└── Concerns/
├── HasProducts.php
├── HasDates.php
└── HasStatus.php