laravel-models
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseLaravel Models
Laravel 模型
Models represent database tables and domain entities.
Related guides:
- Query Builders - Custom query builders (not scopes)
- Actions - Actions contain business logic
- DTOs - Casting model JSON columns to DTOs
模型代表数据库表和领域实体。
相关指南:
- 查询构建器 - 自定义查询构建器(非作用域)
- 动作 - 动作包含业务逻辑
- DTO - 将模型JSON列转换为DTO
Philosophy
设计理念
Models should:
- Use custom query builders (not local scopes) - see Query Builders
- Define relationships
- Define casts
- Contain simple accessors/mutators
- NOT contain business logic (that belongs in Actions)
模型应:
- 使用自定义查询构建器(而非本地作用域)- 参见查询构建器
- 定义关联关系
- 定义类型转换
- 包含简单的访问器/修改器
- 不包含业务逻辑(业务逻辑应放在动作中)
Basic Model Structure
基础模型结构
php
<?php
declare(strict_types=1);
namespace App\Models;
use App\Builders\OrderBuilder;
use App\Enums\OrderStatus;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasMany;
class Order extends Model
{
use HasFactory;
protected function casts(): array
{
return [
'status' => OrderStatus::class,
'total' => 'integer',
];
}
// Custom Query Builder
public function newEloquentBuilder($query): OrderBuilder
{
return new OrderBuilder($query);
}
// Relationships
public function user(): BelongsTo
{
return $this->belongsTo(User::class);
}
public function items(): HasMany
{
return $this->hasMany(OrderItem::class);
}
}php
<?php
declare(strict_types=1);
namespace App\Models;
use App\Builders\OrderBuilder;
use App\Enums\OrderStatus;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasMany;
class Order extends Model
{
use HasFactory;
protected function casts(): array
{
return [
'status' => OrderStatus::class,
'total' => 'integer',
];
}
// Custom Query Builder
public function newEloquentBuilder($query): OrderBuilder
{
return new OrderBuilder($query);
}
// Relationships
public function user(): BelongsTo
{
return $this->belongsTo(User::class);
}
public function items(): HasMany
{
return $this->hasMany(OrderItem::class);
}
}Casts
类型转换
Define casts for type safety:
php
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
];
}Available casts:
- ,
'integer','real','float''double' - ,
'string''boolean' - ,
'array','json','object''collection' - ,
'date','datetime','immutable_date''immutable_datetime' 'timestamp'- ,
'encrypted','encrypted:array','encrypted:collection','encrypted:json''encrypted:object' - Custom cast classes
- Enum classes
- DTO classes
为类型安全定义类型转换:
php
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
];
}可用的类型转换:
- ,
'integer','real','float''double' - ,
'string''boolean' - ,
'array','json','object''collection' - ,
'date','datetime','immutable_date''immutable_datetime' 'timestamp'- ,
'encrypted','encrypted:array','encrypted:collection','encrypted:json''encrypted:object' - 自定义转换类
- 枚举类
- DTO类
Relationships
关联关系
BelongsTo
BelongsTo(属于)
php
public function user(): BelongsTo
{
return $this->belongsTo(User::class);
}
public function customer(): BelongsTo
{
return $this->belongsTo(Customer::class, 'customer_id', 'id');
}php
public function user(): BelongsTo
{
return $this->belongsTo(User::class);
}
public function customer(): BelongsTo
{
return $this->belongsTo(Customer::class, 'customer_id', 'id');
}HasMany
HasMany(有多个)
php
public function orders(): HasMany
{
return $this->hasMany(Order::class);
}
public function items(): HasMany
{
return $this->hasMany(OrderItem::class);
}php
public function orders(): HasMany
{
return $this->hasMany(Order::class);
}
public function items(): HasMany
{
return $this->hasMany(OrderItem::class);
}HasOne
HasOne(有一个)
php
public function profile(): HasOne
{
return $this->hasOne(UserProfile::class);
}php
public function profile(): HasOne
{
return $this->hasOne(UserProfile::class);
}BelongsToMany
BelongsToMany(属于多个)
php
public function roles(): BelongsToMany
{
return $this->belongsToMany(Role::class)
->withTimestamps()
->withPivot('assigned_at');
}php
public function roles(): BelongsToMany
{
return $this->belongsToMany(Role::class)
->withTimestamps()
->withPivot('assigned_at');
}HasManyThrough
HasManyThrough(通过...有多个)
php
public function deployments(): HasManyThrough
{
return $this->hasManyThrough(Deployment::class, Environment::class);
}php
public function deployments(): HasManyThrough
{
return $this->hasManyThrough(Deployment::class, Environment::class);
}MorphTo / MorphMany
MorphTo / MorphMany(多态关联)
php
// MorphTo
public function commentable(): MorphTo
{
return $this->morphTo();
}
// MorphMany
public function comments(): MorphMany
{
return $this->morphMany(Comment::class, 'commentable');
}php
// MorphTo
public function commentable(): MorphTo
{
return $this->morphTo();
}
// MorphMany
public function comments(): MorphMany
{
return $this->morphMany(Comment::class, 'commentable');
}Accessors & Mutators
访问器与修改器
Accessors (Get)
访问器(获取)
php
use Illuminate\Database\Eloquent\Casts\Attribute;
protected function fullName(): Attribute
{
return Attribute::make(
get: fn () => "{$this->first_name} {$this->last_name}",
);
}
// Usage
$user->full_name; // "John Doe"php
use Illuminate\Database\Eloquent\Casts\Attribute;
protected function fullName(): Attribute
{
return Attribute::make(
get: fn () => "{$this->first_name} {$this->last_name}",
);
}
// Usage
$user->full_name; // "John Doe"Mutators (Set)
修改器(设置)
php
protected function password(): Attribute
{
return Attribute::make(
set: fn (string $value) => bcrypt($value),
);
}
// Usage
$user->password = 'secret'; // Automatically hashedphp
protected function password(): Attribute
{
return Attribute::make(
set: fn (string $value) => bcrypt($value),
);
}
// Usage
$user->password = 'secret'; // Automatically hashedBoth Get and Set
同时支持获取与设置
php
protected function email(): Attribute
{
return Attribute::make(
get: fn (string $value) => strtolower($value),
set: fn (string $value) => strtolower(trim($value)),
);
}php
protected function email(): Attribute
{
return Attribute::make(
get: fn (string $value) => strtolower($value),
set: fn (string $value) => strtolower(trim($value)),
);
}Model Methods
模型方法
Simple helper methods are acceptable:
php
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;
}
}But NOT business logic:
php
// ❌ 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
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;
}
}但不要包含业务逻辑:
php
// ❌ 错误 - 业务逻辑放在模型中
class Order extends Model
{
public function cancel(): void
{
DB::transaction(function () {
$this->update(['status' => OrderStatus::Cancelled]);
$this->refundPayment();
$this->notifyCustomer();
});
}
}
// ✅ 正确 - 业务逻辑放在动作中
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;
});
}
}Model Observers
模型观察者
For model lifecycle hooks:
php
<?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
}
}Register in AppServiceProvider:
php
use App\Models\Order;
use App\Observers\OrderObserver;
public function boot(): void
{
Order::observe(OrderObserver::class);
}用于模型生命周期钩子:
php
<?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中注册:
php
use App\Models\Order;
use App\Observers\OrderObserver;
public function boot(): void
{
Order::observe(OrderObserver::class);
}Model Concerns (Traits)
模型关注点(Traits)
Extract reusable behavior:
View full implementation →
Use in models:
php
class Order extends Model
{
use HasUuid;
}提取可复用的行为:
查看完整实现 →
在模型中使用:
php
class Order extends Model
{
use HasUuid;
}Route Model Binding
路由模型绑定
Implicit Binding
隐式绑定
php
// Route
Route::get('/orders/{order}', [OrderController::class, 'show']);
// Controller - automatically receives Order model
public function show(Order $order) { }php
// Route
Route::get('/orders/{order}', [OrderController::class, 'show']);
// Controller - automatically receives Order model
public function show(Order $order) { }Custom Key
自定义键
php
Route::get('/orders/{order:uuid}', [OrderController::class, 'show']);php
Route::get('/orders/{order:uuid}', [OrderController::class, 'show']);Custom Resolution
自定义解析逻辑
php
public function resolveRouteBinding($value, $field = null)
{
return $this->where($field ?? 'id', $value)
->where('is_active', true)
->firstOrFail();
}php
public function resolveRouteBinding($value, $field = null)
{
return $this->where($field ?? 'id', $value)
->where('is_active', true)
->firstOrFail();
}Mass Assignment Protection
批量赋值保护
All models should be unguarded by default.
所有模型默认应取消保护。
AppServiceProvider Setup
AppServiceProvider 设置
In your method, call :
AppServiceProvider::boot()Model::unguard()php
<?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();
}
}在方法中,调用:
AppServiceProvider::boot()Model::unguard()php
<?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();
}
}Model Configuration
模型配置
Do NOT use or properties on your models:
$fillable$guardedphp
// ✅ 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$guardedphp
// ✅ 正确 - 无fillable/guarded
class Order extends Model
{
protected function casts(): array
{
return [
'status' => OrderStatus::class,
];
}
}
// ❌ 错误 - 不要使用fillable
class Order extends Model
{
protected $fillable = ['name', 'email'];
}
// ❌ 错误 - 不要使用guarded
class Order extends Model
{
protected $guarded = [];
}Why Unguard?
为什么取消保护?
- Simplicity: No need to maintain fillable/guarded arrays
- Flexibility: All attributes can be mass-assigned
- Trust: With proper validation in Form Requests and Actions, mass assignment protection is redundant
- Cleaner Models: Less boilerplate code
Important: Always validate input in Form Requests before passing to Actions/Models.
- 简洁性:无需维护fillable/guarded数组
- 灵活性:所有属性都可以批量赋值
- 信任:在Form Requests和Actions中进行适当验证后,批量赋值保护是多余的
- 更简洁的模型:更少的样板代码
重要提示: 在将输入传递给Actions/Models之前,始终在Form Requests中验证输入。
Timestamps
时间戳
php
// Disable timestamps
public $timestamps = false;
// Custom timestamp columns
const CREATED_AT = 'creation_date';
const UPDATED_AT = 'updated_date';php
// Disable timestamps
public $timestamps = false;
// Custom timestamp columns
const CREATED_AT = 'creation_date';
const UPDATED_AT = 'updated_date';Soft Deletes
软删除
php
use Illuminate\Database\Eloquent\SoftDeletes;
class Order extends Model
{
use SoftDeletes;
}Usage:
php
$order->delete(); // Soft delete
$order->forceDelete(); // Permanent delete
$order->restore(); // Restore
Order::withTrashed()->find($id);
Order::onlyTrashed()->get();php
use Illuminate\Database\Eloquent\SoftDeletes;
class Order extends Model
{
use SoftDeletes;
}用法:
php
$order->delete(); // Soft delete
$order->forceDelete(); // Permanent delete
$order->restore(); // Restore
Order::withTrashed()->find($id);
Order::onlyTrashed()->get();Collections
集合
Query results return Collections:
php
$orders = Order::all(); // Illuminate\Database\Eloquent\Collection
$orders->filter(fn($order) => $order->isPending());
$orders->map(fn($order) => $order->total);
$orders->sum('total');查询结果返回集合:
php
$orders = Order::all(); // Illuminate\Database\Eloquent\Collection
$orders->filter(fn($order) => $order->isPending());
$orders->map(fn($order) => $order->total);
$orders->sum('total');Model Organization
模型组织
app/Models/
├── Order.php
├── User.php
├── Concerns/
│ ├── HasUuid.php
│ ├── BelongsToTenant.php
│ └── Searchable.php
└── Contracts/
└── Searchable.phpapp/Models/
├── Order.php
├── User.php
├── Concerns/
│ ├── HasUuid.php
│ ├── BelongsToTenant.php
│ └── Searchable.php
└── Contracts/
└── Searchable.phpTesting Models
测试模型
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);
});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);
});Summary
总结
Models should:
- Be unguarded globally via in AppServiceProvider
Model::unguard() - Define structure (casts, relationships)
- Use custom query builders (not scopes)
- Have simple helper methods
- Use observers for lifecycle hooks
Models should NOT:
- Use or
$fillableproperties$guarded - Contain business logic (use Actions)
- Have complex methods (use Actions)
- Use local scopes (use custom builders)
模型应:
- 通过AppServiceProvider中的全局取消保护
Model::unguard() - 定义结构(类型转换、关联关系)
- 使用自定义查询构建器(而非作用域)
- 包含简单的辅助方法
- 使用观察者处理生命周期钩子
模型不应:
- 使用或
$fillable属性$guarded - 包含业务逻辑(使用Actions)
- 包含复杂方法(使用Actions)
- 使用本地作用域(使用自定义构建器)