laravel-query-builders
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseLaravel Query Builders
Laravel 查询构建器
Always use custom query builders instead of local scopes.
Related guides:
- Models - Model integration with custom builders
- Controllers - Using query objects in controllers
始终使用自定义查询构建器而非本地作用域。
相关指南:
- Models - 模型与自定义构建器的集成
- 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(), etc.orWhereHas() - Better organization - All query logic in one class
- More composable - Easier to chain and compose
- Easier testing - Test query logic in isolation
❌ 请勿使用本地作用域。
✅ 使用自定义查询构建器的原因:
- 更优的类型提示 - 完整的IDE自动补全
- 类型安全的嵌套查询 - 为、
whereHas()等方法中的闭包提供类型提示orWhereHas() - 更清晰的代码组织 - 所有查询逻辑集中在一个类中
- 更强的可组合性 - 更易于链式调用和组合
- 更简单的测试 - 可独立测试查询逻辑
Basic Builder Structure
基础构建器结构
php
<?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');
}
}php
<?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:
php
public function whereHasItems(array|string $productIds): self
{
return $this->whereHas('items', function (OrderItemBuilder $query) use ($productIds): void {
$query->whereIn('product_id', (array) $productIds);
});
}Usage:
php
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();为闭包添加类型提示,在关联查询中获得完整的IDE支持:
php
public function whereHasItems(array|string $productIds): self
{
return $this->whereHas('items', function (OrderItemBuilder $query) use ($productIds): void {
$query->whereIn('product_id', (array) $productIds);
});
}使用示例:
php
Order::query()
->whereHas('items', function (OrderItemBuilder $query): void {
$query->whereActive() // 自定义方法 - 自动补全生效!
->whereProduct($id); // 完整的类型安全!
})
->whereHas('customer', function (CustomerBuilder $query): void {
$query->whereVerified() // 自定义方法
->wherePremium(); // IDE可识别所有方法!
})
->get();PHPDoc for External Methods
为外部方法添加PHPDoc注释
Document methods from Spatie packages or macros:
php
/**
* @method static OrderBuilder whereState(string $column, string|array $state)
* @method static OrderBuilder whereNotState(string $column, string|array $state)
*/
class OrderBuilder extends Builder
{
// ...
}为Spatie包或宏定义的方法添加文档:
php
/**
* @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
<?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:
php
class OrderBuilder extends Builder
{
use HasProducts;
// ...
}提取可复用的查询逻辑:
php
<?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);
});
}
}在构建器中使用:
php
class OrderBuilder extends Builder
{
use HasProducts;
// ...
}Register Builder in Model
在模型中注册构建器
Preferred: PHP Attribute (Laravel 11+)
推荐方式:PHP属性(Laravel 11+)
php
use Illuminate\Database\Eloquent\Attributes\UseEloquentBuilder;
#[UseEloquentBuilder(OrderBuilder::class)]
class Order extends Model
{
// ...
}php
use Illuminate\Database\Eloquent\Attributes\UseEloquentBuilder;
#[UseEloquentBuilder(OrderBuilder::class)]
class Order extends Model
{
// ...
}Alternative: Static Property
替代方式:静态属性
php
class Order extends Model
{
protected static string $eloquentBuilder = OrderBuilder::class;
// ...
}php
class Order extends Model
{
protected static string $eloquentBuilder = OrderBuilder::class;
// ...
}Deprecated: Method Override
已废弃:重写方法
php
// ❌ Don't use this approach anymore
public function newEloquentBuilder($query): OrderBuilder
{
return new OrderBuilder($query);
}php
// ❌ 请勿再使用此方式
public function newEloquentBuilder($query): OrderBuilder
{
return new OrderBuilder($query);
}Usage Examples
使用示例
Basic Chaining
基础链式调用
php
Order::query()
->wherePending()
->whereTotalGreaterThan(10000)
->wherePlacedBetween(now()->subWeek(), now())
->withRelated()
->recent()
->paginate();php
Order::query()
->wherePending()
->whereTotalGreaterThan(10000)
->wherePlacedBetween(now()->subWeek(), now())
->withRelated()
->recent()
->paginate();Lazy Iteration
惰性迭代
php
Order::query()
->whereCompleted()
->lazyById()
->each(function (Order $order): void {
// Process order
});php
Order::query()
->whereCompleted()
->lazyById()
->each(function (Order $order): void {
// 处理订单
});Complex Queries
复杂查询
php
$orders = Order::query()
->wherePending()
->whereCustomer($user)
->whereHasItems([$productId1, $productId2])
->wherePlacedBetween($startDate, $endDate)
->withRelated()
->get();php
$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
<?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
}即使初始为空,也要创建构建器 - 便于未来扩展:
php
<?php
declare(strict_types=1);
namespace App\Builders;
use Illuminate\Database\Eloquent\Builder;
class CustomerBuilder extends Builder
{
// 目前为空,但已为未来方法做好准备
}Common Builder Methods
常见构建器方法
Status Filtering
状态筛选
php
public function whereActive(): self
{
return $this->where('status', 'active');
}
public function whereInactive(): self
{
return $this->where('status', 'inactive');
}php
public function whereActive(): self
{
return $this->where('status', 'active');
}
public function whereInactive(): self
{
return $this->where('status', 'inactive');
}Date Ranges
日期范围
php
public function whereCreatedAfter(Carbon $date): self
{
return $this->where('created_at', '>', $date);
}
public function whereCreatedToday(): self
{
return $this->whereDate('created_at', today());
}php
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
用户/所有者筛选
php
public function whereUser(User|int $user): self
{
$userId = $user instanceof User ? $user->id : $user;
return $this->where('user_id', $userId);
}php
public function whereUser(User|int $user): self
{
$userId = $user instanceof User ? $user->id : $user;
return $this->where('user_id', $userId);
}Relationship Loading
关联加载
php
public function withFullRelations(): self
{
return $this->with([
'user',
'items.product',
'customer.address',
]);
}php
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.phpapp/Builders/
├── OrderBuilder.php
├── CustomerBuilder.php
├── ProductBuilder.php
└── Concerns/
├── HasProducts.php
├── HasDates.php
└── HasStatus.phpSummary
总结
Custom query builders:
- Provide better IDE support than scopes
- Enable type-safe nested queries
- Keep query logic organized
- Are easier to test
- Support method chaining
Never use local scopes - always use custom builders.
自定义查询构建器:
- 比作用域提供更优的IDE支持
- 支持类型安全的嵌套查询
- 使查询逻辑更有条理
- 更易于测试
- 支持方法链式调用
永远不要使用本地作用域 - 始终使用自定义构建器。