关于 laravel-models
laravel-models 是面向 Laravel 开发者的 Eloquent 模型设计技能,介绍模型职责、类型转换、关系和路由绑定,并强调将业务逻辑放在 Actions 中。
主要功能
- 01模型职责与结构
- 02自定义查询构建器和关系
- 03类型安全的属性转换
- 04观察器与可复用 Trait
- 05路由模型绑定指南
适用场景
- 01设计应用的 Eloquent 模型
- 02确定业务逻辑的放置位置
- 03配置类型转换和模型生命周期钩子
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ leeovery/ agentic-skills --skill laravel-models安装后在 Claude Code 中输入/laravel-models确认已加载。
附带文件references/HasUuid.phpreferences/query-builders.md
详细介绍
Laravel 模型
模型表示数据库表和领域实体。
相关指南:
理念
模型应当:
- 使用自定义查询构建器(而非本地作用域)——参见查询构建器
- 定义关系
- 定义类型转换
- 包含简单的访问器/修改器
- 不包含业务逻辑(业务逻辑应放在 Actions 中)
- 在可用时优先使用 PHP 属性,而非属性/方法(Laravel 12+ 支持
#[UseEloquentBuilder],Laravel 13+ 支持#[Table]、#[ObservedBy]、#[UsePolicy]、#[UseFactory]等)
基本模型结构
<?php
declare(strict_types=1);
namespace App\Models;
use App\Builders\OrderBuilder;
use App\Enums\OrderStatus;
use Illuminate\Database\Eloquent\Attributes\UseEloquentBuilder;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasMany;
#[UseEloquentBuilder(OrderBuilder::class)]
class Order extends Model
{
use HasFactory;
protected function casts(): array
{
return [
'status' => OrderStatus::class,
'total' => 'integer',
];
}
// Relationships
public function user(): BelongsTo
{
return $this->belongsTo(User::class);
}
public function items(): HasMany
{
return $this->hasMany(OrderItem::class);
}
}
类型转换
定义类型转换以确保类型安全:
protected function casts(): array
{
return [
'status' => OrderStatus::class, // Enum
'total' => 'integer', // Integer
'is_paid' => 'boolean', // Boolean
'metadata' => OrderMetadataData::class, // DTO
'completed_at' => 'datetime', // Carbon
'tags' => 'array', // JSON array
];
}
模型方法
简单的辅助方法是可以接受的:
class Order extends Model
{
public function isPending(): bool
{
return $this->status === OrderStatus::Pending;
}
public function isCompleted(): bool
{
return $this->status === OrderStatus::Completed;
}
public function canBeCancelled(): bool
{
return $this->isPending() || $this->status === OrderStatus::Processing;
}
}
但不要包含业务逻辑:
// ❌ Bad - business logic in model
class Order extends Model
{
public function cancel(): void
{
DB::transaction(function () {
$this->update(['status' => OrderStatus::Cancelled]);
$this->refundPayment();
$this->notifyCustomer();
});
}
}
// ✅ Good - business logic in action
class CancelOrderAction
{
public function __invoke(Order $order): Order
{
return DB::transaction(function () use ($order) {
$order->update(['status' => OrderStatus::Cancelled]);
resolve(RefundPaymentAction::class)($order);
resolve(NotifyCustomerAction::class)($order);
return $order;
});
}
}
模型观察者
用于模型生命周期钩子:
<?php
declare(strict_types=1);
namespace App\Observers;
use App\Models\Order;
use Illuminate\Support\Str;
class OrderObserver
{
public function creating(Order $order): void
{
if (! $order->uuid) {
$order->uuid = Str::uuid();
}
}
public function created(Order $order): void
{
// Dispatch event, queue job, etc.
}
public function updating(Order $order): void
{
// Before update
}
public function updated(Order $order): void
{
// After update
}
public function deleted(Order $order): void
{
// After delete
}
}
在 AppServiceProvider 中注册:
use App\Models\Order;
use App\Observers\OrderObserver;
public function boot(): void
{
Order::observe(OrderObserver::class);
}
模型关注点(Traits)
提取可复用的行为:
在模型中使用:
class Order extends Model
{
use HasUuid;
}
路由模型绑定
隐式绑定
// Route
Route::get('/orders/{order}', [OrderController::class, 'show']);
// Controller - automatically receives Order model
public function show(Order $order) { }
自定义键
Route::get('/orders/{order:uuid}', [OrderController::class, 'show']);
自定义解析
public function resolveRouteBinding($value, $field = null)
{
return $this->where($field ?? 'id', $value)
->where('is_active', true)
->firstOrFail();
}
批量赋值保护
所有模型默认都应取消保护。
AppServiceProvider 设置
在 AppServiceProvider::boot() 方法中调用 Model::unguard():
<?php
declare(strict_types=1);
namespace App\Providers;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\ServiceProvider;
class AppServiceProvider extends ServiceProvider
{
public function boot(): void
{
Model::unguard();
}
}
模型配置
不要在模型中使用 $fillable 或 $guarded 属性:
// ✅ Good - no fillable/guarded
class Order extends Model
{
protected function casts(): array
{
return [
'status' => OrderStatus::class,
];
}
}
// ❌ Bad - don't use fillable
class Order extends Model
{
protected $fillable = ['name', 'email'];
}
// ❌ Bad - don't use guarded
class Order extends Model
{
protected $guarded = [];
}
为什么取消保护?
- 简单性:无需维护 fillable/guarded 数组
- 灵活性:所有属性都可以批量赋值
- 信任:在 Form Requests 和 Actions 中进行适当验证后,批量赋值保护就显得多余
- 更简洁的模型:减少样板代码
**重要:**将输入传递给 Actions/模型前,务必在 Form Requests 中验证。
模型组织
app/Models/
├── Order.php
├── User.php
├── Concerns/
│ ├── HasUuid.php
│ ├── BelongsToTenant.php
│ └── Searchable.php
└── Contracts/
└── Searchable.php
测试模型
it('can mass assign attributes', function () {
$order = Order::create([
'user_id' => 1,
'status' => 'pending',
'total' => 1000,
'notes' => 'Test order',
]);
expect($order->user_id)->toBe(1)
->and($order->total)->toBe(1000);
});
it('casts status to enum', function () {
$order = Order::factory()->create(['status' => 'pending']);
expect($order->status)->toBeInstanceOf(OrderStatus::class);
});
it('has user relationship', function () {
$order = Order::factory()->create();
expect($order->user)->toBeInstanceOf(User::class);
});
评论
支持 Markdown