



























在实际的后端项目开发中,例如 CRM、ERP 或用户系统,Model 很容易变得越来越臃肿。随着业务增加,很多通用能力会不断叠加,例如:
如果把这些功能全部写在 Model 里,最终很容易出现一个 2000~3000 行的 Model 文件,维护成本非常高。
一种在企业项目中非常常见的解决方案是:
Model + Trait 组合设计
Trait 用来承载可复用的横切功能(Cross-cutting concerns),Model 只负责数据结构和关系定义。
下面通过一个完整工程示例说明这种设计。
假设有一个 Student 模型,需要具备以下能力:
1 自动生成 UUID
2 软删除
3 操作日志
4 搜索条件封装
5 审计字段自动记录
项目目录结构可以设计为:
app
├─ Models
│ └─ Student.php
│ └─ OperationLog.php
│
├─ Traits
│ ├─ SoftDeleteTrait.php
│ ├─ OperationLogTrait.php
│ ├─ UuidTrait.php
│ ├─ SearchTrait.php
│ └─ BlameableTrait.php
Student 模型只负责数据结构和使用 Trait。
app/Models/Student.php
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use App\Traits\SoftDeleteTrait;
use App\Traits\OperationLogTrait;
use App\Traits\UuidTrait;
use App\Traits\SearchTrait;
use App\Traits\BlameableTrait;
class Student extends Model
{
use SoftDeleteTrait, OperationLogTrait, UuidTrait, SearchTrait, BlameableTrait;
protected $table = 'students';
protected $fillable = [
'uuid',
'name',
'phone',
'email',
'created_by',
'updated_by'
];
}
这里可以看到,Student 模型本身非常干净,只负责表结构定义。
SoftDeleteTrait 用来实现逻辑删除,而不是直接删除数据。
app/Traits/SoftDeleteTrait.php
<?php
namespace App\Traits;
trait SoftDeleteTrait
{
public static function bootSoftDeleteTrait()
{
static::addGlobalScope('soft_delete', function ($builder) {
$builder->whereNull('deleted_at');
});
}
public function softDelete()
{
$this->deleted_at = now();
$this->save();
}
public function restore()
{
$this->deleted_at = null;
$this->save();
}
}
数据库需要增加字段:
deleted_at datetime
这样删除数据时只会记录时间,而不会真正删除。
很多系统希望主键或业务 ID 使用 UUID。
app/Traits/UuidTrait.php
<?php
namespace App\Traits;
use Illuminate\Support\Str;
trait UuidTrait
{
public static function bootUuidTrait()
{
static::creating(function ($model) {
if (empty($model->uuid)) {
$model->uuid = (string) Str::uuid();
}
});
}
}
数据库字段:
uuid varchar(36)
当创建数据时自动生成 UUID。
在很多后台系统中,记录操作日志是非常重要的功能。
app/Traits/OperationLogTrait.php
<?php
namespace App\Traits;
use App\Models\OperationLog;
trait OperationLogTrait
{
public static function bootOperationLogTrait()
{
static::created(function ($model) {
OperationLog::create([
'model' => get_class($model),
'model_id' => $model->id,
'action' => 'create',
'content' => json_encode($model->toArray())
]);
});
static::updated(function ($model) {
OperationLog::create([
'model' => get_class($model),
'model_id' => $model->id,
'action' => 'update',
'content' => json_encode($model->getDirty())
]);
});
static::deleted(function ($model) {
OperationLog::create([
'model' => get_class($model),
'model_id' => $model->id,
'action' => 'delete',
'content' => ''
]);
});
}
}
日志表 operation_logs 示例字段:
id
model
model_id
action
content
created_at
这样所有模型的增删改都会自动记录。
很多列表页面都有搜索需求,可以把查询逻辑封装在 Trait 里。
app/Traits/SearchTrait.php
<?php
namespace App\Traits;
trait SearchTrait
{
public function scopeSearch($query, $params)
{
if (!empty($params['name'])) {
$query->where('name', 'like', '%' . $params['name'] . '%');
}
if (!empty($params['phone'])) {
$query->where('phone', $params['phone']);
}
if (!empty($params['email'])) {
$query->where('email', 'like', '%' . $params['email'] . '%');
}
return $query;
}
}
使用方式:
Student::search($params)->paginate(20);
这样所有查询逻辑都集中在 Trait 中。
很多企业系统需要记录是谁创建或修改了数据。
app/Traits/BlameableTrait.php
<?php
namespace App\Traits;
use Illuminate\Support\Facades\Auth;
trait BlameableTrait
{
public static function bootBlameableTrait()
{
static::creating(function ($model) {
if (Auth::check()) {
$model->created_by = Auth::id();
}
});
static::updating(function ($model) {
if (Auth::check()) {
$model->updated_by = Auth::id();
}
});
}
}
数据库字段:
created_by
updated_by
这样系统可以自动记录数据是谁创建、谁修改。
创建学生
Student::create([
'name' => '张三',
'phone' => '13800000000'
]);
自动触发:
UUID 生成
操作日志记录
created_by 自动写入
更新学生
$student = Student::find(1);
$student->name = '李四';
$student->save();
自动触发:
更新日志
updated_by 自动写入
第一,Model 变得非常干净
只负责数据结构和关系定义。
第二,功能高度复用
多个模型可以共享同一个 Trait。
第三,代码更易维护
横切逻辑统一管理。
第四,更符合大型系统架构
Trait 负责通用能力,Service 负责业务逻辑。
典型调用结构如下:
Controller
↓
Service
↓
Model + Traits
↓
Database
在大型后端系统中,Model 不应该承担过多业务逻辑。通过 Trait 可以把通用能力模块化,例如:
这种 Model + Trait 的组合模式,可以显著降低代码耦合度,使系统结构更加清晰。
在实际项目中,很多成熟的 CRM、ERP 系统都会采用这种设计模式。
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。