本文目录导读:

在PHP项目框架中封装数据模型层,通常遵循面向对象设计原则,主要目的是将数据操作逻辑与业务逻辑分离,提高代码的可维护性和复用性。
以下是几种常见的封装方式,从简单到复杂,适合不同的框架和项目规模:
核心:Active Record 模式(最常用)
这是 Laravel、Yii2、CodeIgniter 等主流框架采用的模式,它让一个模型类“代表”一个数据库表,同时这个类的实例“代表”表中的一行记录。
封装方法:
<?php
// 1. 模型类定义 (假设基于 Laravel Eloquent)
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class User extends Model
{
// 1. 封装表名 (通常使用约定,但可显式定义)
protected $table = 'users';
// 2. 封装可批量赋值的字段 (安全防护)
protected $fillable = ['name', 'email', 'password'];
// 3. 封装隐藏字段 (如密码)
protected $hidden = ['password', 'remember_token'];
// 4. 封装日期字段格式
protected $dates = ['created_at', 'updated_at'];
// 5. **封装关联关系** (最关键的部分)
public function posts()
{
return $this->hasMany(Post::class);
}
public function profile()
{
return $this->hasOne(Profile::class);
}
// 6. **封装数据访问器/修改器** (数据处理逻辑)
public function getNameAttribute($value)
{
return ucfirst($value); // 获取时自动首字母大写
}
public function setPasswordAttribute($value)
{
$this->attributes['password'] = bcrypt($value); // 设置时自动加密
}
// 7. **封装局部作用域** (常用查询条件)
public function scopeActive($query)
{
return $query->where('status', 'active');
}
public function scopeRecent($query)
{
return $query->orderBy('created_at', 'desc');
}
// 8. **自定义业务方法** (不仅仅是CRUD)
public function sendWelcomeEmail()
{
// 封装数据相关的业务逻辑
Mail::to($this->email)->send(new WelcomeMail($this));
}
}
使用方式:
// 创建 $user = User::create(['name' => 'John', 'email' => '...', 'password' => 'secret']); // 查询并使用作用域 $activeUsers = User::active()->recent()->get(); // 访问封装后的数据 echo $user->name; // 输出 "John" (已通过访问器处理) // 调用模型方法 $user->sendWelcomeEmail(); // 使用关联 $user->posts; // 延迟加载该用户的所有文章
进阶:Repository 模式(用于复杂业务/大型项目)
当业务逻辑变得复杂(比如跨表查询、缓存策略、数据统计)时,直接在模型里写会变得臃肿,Repository 模式在模型和业务逻辑之间加了一层“数据仓库”。
封装方法:
<?php
// 1. 先定义接口 (解耦)
namespace App\Repositories\Contracts;
interface UserRepositoryInterface
{
public function all();
public function find($id);
public function create(array $data);
public function update($id, array $data);
public function delete($id);
public function getActiveUsersWithPosts();
}
// 2. 实现接口 (封装具体数据操作)
namespace App\Repositories\Eloquent;
use App\Models\User;
use App\Repositories\Contracts\UserRepositoryInterface;
class UserRepository implements UserRepositoryInterface
{
protected $model;
public function __construct(User $user)
{
$this->model = $user; // 依赖注入模型
}
public function all()
{
return $this->model->all();
}
public function find($id)
{
return $this->model->findOrFail($id);
}
// 封装一个复杂的跨表查询
public function getActiveUsersWithPosts()
{
return $this->model->active() // 使用模型里的 scope
->with('posts') // 预加载关联
->whereHas('profile', function($q) { // 跨表条件
$q->where('is_verified', true);
})
->get();
}
public function create(array $data)
{
// 可以在这里添加额外的数据验证或处理
$data['password'] = bcrypt($data['password']);
return $this->model->create($data);
}
// 其他实现...
}
使用方式 (在 Controller 中):
// 通过依赖注入使用
public function index(UserRepositoryInterface $userRepo)
{
$users = $userRepo->getActiveUsersWithPosts();
return view('users.index', compact('users'));
}
实用技巧:常见封装模式
| 实现方式 | 示例 |
|---------|---------|------|
| 时间戳自动管理 | 框架内置 (如 $timestamps = true) | $user->created_at 自动填充 |
| 软删除 | Trait 封装 | use SoftDeletes; |
| 多态关联 | 模型方法 | $post->comments() 和 $video->comments() 共用 Comment 模型 |
| 缓存策略 | Repository 或 Model 中的静态方法 | User::getCachedCount() |
| 数据校验 | 模型中的 rules 属性或独立 Validation | 在 create 方法前调用 $this->validate() |
| 事件监听 | 模型事件 (boot 方法) | static::creating(function($user) { ... }) |
一些重要建议
- 不要直接在 Controller 里写 SQL:所有数据库交互都应该通过模型层。
- 保持 Model 的纯净:Model 主要负责数据映射和关联关系,业务逻辑(如发邮件、计算价格)应该放在 Service 层或 Model 的自定义方法里。
- 使用类型声明:PHP 7.4+ 支持属性类型,8.0+ 支持构造器属性提升,让数据更安全。
- 善用 Trait:对于通用的数据行为(如日志、缓存),通过 Trait 组合到模型中,避免继承的复杂性。
- 遵循单一职责原则:一个模型最好只对应一个数据实体(用户、订单、商品),不要混入其他数据逻辑。
- 简单项目:使用 Active Record (如 Laravel Eloquent),直接在 Model 里写访问器、修改器、作用域和方法。
- 中等项目:Model + Service Layer,Model 负责数据映射,Service 负责业务逻辑。
- 大型/复杂项目:Model + Repository + Service,Repository 负责数据访问逻辑,Service 负责业务逻辑,Controller 调用 Service。
如果你在使用特定的框架(Laravel、ThinkPHP、Yii2等),可以告诉我,我可以给你更具体的代码示例。