本文目录导读:

我来详细讲解PHP模型层的设计原则和最佳实践。
基础模型架构
单一职责原则的模型设计
<?php
// 基础模型类
abstract class BaseModel
{
protected PDO $db;
protected string $table;
protected string $primaryKey = 'id';
public function __construct(PDO $db)
{
$this->db = $db;
}
// 通用CRUD操作
public function find(int $id): ?array
{
$sql = "SELECT * FROM {$this->table} WHERE {$this->primaryKey} = :id";
$stmt = $this->db->prepare($sql);
$stmt->execute(['id' => $id]);
return $stmt->fetch(PDO::FETCH_ASSOC) ?: null;
}
public function findAll(array $conditions = []): array
{
$sql = "SELECT * FROM {$this->table}";
$params = [];
if (!empty($conditions)) {
$where = [];
foreach ($conditions as $field => $value) {
$where[] = "{$field} = :{$field}";
$params[$field] = $value;
}
$sql .= " WHERE " . implode(' AND ', $where);
}
$stmt = $this->db->prepare($sql);
$stmt->execute($params);
return $stmt->fetchAll(PDO::FETCH_ASSOC);
}
public function create(array $data): int
{
$fields = implode(', ', array_keys($data));
$placeholders = implode(', ', array_map(fn($key) => ":{$key}", array_keys($data)));
$sql = "INSERT INTO {$this->table} ({$fields}) VALUES ({$placeholders})";
$stmt = $this->db->prepare($sql);
$stmt->execute($data);
return (int)$this->db->lastInsertId();
}
public function update(int $id, array $data): bool
{
$sets = [];
foreach ($data as $field => $value) {
$sets[] = "{$field} = :{$field}";
}
$sql = "UPDATE {$this->table} SET " . implode(', ', $sets)
. " WHERE {$this->primaryKey} = :id";
$data['id'] = $id;
$stmt = $this->db->prepare($sql);
return $stmt->execute($data);
}
public function delete(int $id): bool
{
$sql = "DELETE FROM {$this->table} WHERE {$this->primaryKey} = :id";
$stmt = $this->db->prepare($sql);
return $stmt->execute(['id' => $id]);
}
}
业务模型设计
实体模型(Entity)
<?php
class UserEntity
{
private ?int $id;
private string $name;
private string $email;
private ?string $phone;
private DateTime $createdAt;
// 构造函数
public function __construct(array $data = [])
{
$this->id = $data['id'] ?? null;
$this->name = $data['name'] ?? '';
$this->email = $data['email'] ?? '';
$this->phone = $data['phone'] ?? null;
$this->createdAt = isset($data['created_at'])
? new DateTime($data['created_at'])
: new DateTime();
}
// Getters
public function getId(): ?int
{
return $this->id;
}
public function getName(): string
{
return $this->name;
}
public function getEmail(): string
{
return $this->email;
}
public function getPhone(): ?string
{
return $this->phone;
}
public function getCreatedAt(): DateTime
{
return $this->createdAt;
}
// Setters with validation
public function setName(string $name): void
{
if (strlen($name) < 2 || strlen($name) > 50) {
throw new InvalidArgumentException('Name must be between 2 and 50 characters');
}
$this->name = $name;
}
public function setEmail(string $email): void
{
if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
throw new InvalidArgumentException('Invalid email format');
}
$this->email = $email;
}
// 转换为数组
public function toArray(): array
{
return [
'id' => $this->id,
'name' => $this->name,
'email' => $this->email,
'phone' => $this->phone,
'created_at' => $this->createdAt->format('Y-m-d H:i:s')
];
}
}
业务模型(Repository模式)
<?php
interface UserRepositoryInterface
{
public function findById(int $id): ?UserEntity;
public function findByEmail(string $email): ?UserEntity;
public function findAll(): array;
public function save(UserEntity $user): UserEntity;
public function delete(int $id): bool;
public function searchUsers(string $keyword): array;
}
class UserRepository extends BaseModel implements UserRepositoryInterface
{
protected string $table = 'users';
public function findById(int $id): ?UserEntity
{
$data = $this->find($id);
return $data ? new UserEntity($data) : null;
}
public function findByEmail(string $email): ?UserEntity
{
$sql = "SELECT * FROM {$this->table} WHERE email = :email";
$stmt = $this->db->prepare($sql);
$stmt->execute(['email' => $email]);
$data = $stmt->fetch(PDO::FETCH_ASSOC);
return $data ? new UserEntity($data) : null;
}
public function findAll(): array
{
$results = parent::findAll();
return array_map(fn($data) => new UserEntity($data), $results);
}
public function save(UserEntity $user): UserEntity
{
$data = $user->toArray();
if ($user->getId()) {
$this->update($user->getId(), $data);
} else {
unset($data['id']);
$id = $this->create($data);
$user = $this->findById($id);
}
return $user;
}
public function searchUsers(string $keyword): array
{
$sql = "SELECT * FROM {$this->table}
WHERE name LIKE :keyword
OR email LIKE :keyword";
$stmt = $this->db->prepare($sql);
$stmt->execute(['keyword' => "%{$keyword}%"]);
$results = $stmt->fetchAll(PDO::FETCH_ASSOC);
return array_map(fn($data) => new UserEntity($data), $results);
}
}
业务逻辑服务层
<?php
class UserService
{
private UserRepositoryInterface $userRepository;
private ValidationService $validator;
private EventDispatcher $eventDispatcher;
public function __construct(
UserRepositoryInterface $userRepository,
ValidationService $validator,
EventDispatcher $eventDispatcher
) {
$this->userRepository = $userRepository;
$this->validator = $validator;
$this->eventDispatcher = $eventDispatcher;
}
public function registerUser(array $userData): UserEntity
{
// 数据验证
$this->validator->validate($userData, [
'name' => 'required|min:2|max:50',
'email' => 'required|email|unique:users',
'password' => 'required|min:8|confirmed'
]);
// 检查邮箱唯一性
if ($this->userRepository->findByEmail($userData['email'])) {
throw new DuplicateEmailException('Email already exists');
}
// 密码加密
$userData['password_hash'] = password_hash(
$userData['password'],
PASSWORD_BCRYPT
);
unset($userData['password']);
// 创建用户实体
$user = new UserEntity($userData);
// 保存用户
$savedUser = $this->userRepository->save($user);
// 触发事件
$this->eventDispatcher->dispatch(
new UserRegisteredEvent($savedUser)
);
return $savedUser;
}
public function updateProfile(int $userId, array $profileData): UserEntity
{
$user = $this->userRepository->findById($userId);
if (!$user) {
throw new UserNotFoundException('User not found');
}
// 更新字段
if (isset($profileData['name'])) {
$user->setName($profileData['name']);
}
if (isset($profileData['phone'])) {
$user->setPhone($profileData['phone']);
}
// 保存更新
return $this->userRepository->save($user);
}
}
高级模式:查询构建器
<?php
class QueryBuilder
{
private PDO $db;
private string $from;
private array $selects = ['*'];
private array $wheres = [];
private array $joins = [];
private array $orderBys = [];
private ?int $limit = null;
private ?int $offset = null;
private array $params = [];
public function __construct(PDO $db, string $table)
{
$this->db = $db;
$this->from = $table;
}
public function select(array $fields): self
{
$this->selects = $fields;
return $this;
}
public function where(string $field, string $operator, $value): self
{
$paramName = str_replace('.', '_', $field) . '_' . count($this->wheres);
$this->wheres[] = "{$field} {$operator} :{$paramName}";
$this->params[$paramName] = $value;
return $this;
}
public function join(string $table, string $first, string $operator, string $second): self
{
$this->joins[] = "JOIN {$table} ON {$first} {$operator} {$second}";
return $this;
}
public function orderBy(string $field, string $direction = 'ASC'): self
{
$this->orderBys[] = "{$field} {$direction}";
return $this;
}
public function limit(int $limit): self
{
$this->limit = $limit;
return $this;
}
public function offset(int $offset): self
{
$this->offset = $offset;
return $this;
}
public function get(): array
{
$sql = $this->buildSelect();
$stmt = $this->db->prepare($sql);
$stmt->execute($this->params);
return $stmt->fetchAll(PDO::FETCH_ASSOC);
}
private function buildSelect(): string
{
$sql = "SELECT " . implode(', ', $this->selects)
. " FROM {$this->from}";
if (!empty($this->joins)) {
$sql .= " " . implode(' ', $this->joins);
}
if (!empty($this->wheres)) {
$sql .= " WHERE " . implode(' AND ', $this->wheres);
}
if (!empty($this->orderBys)) {
$sql .= " ORDER BY " . implode(', ', $this->orderBys);
}
if ($this->limit !== null) {
$sql .= " LIMIT {$this->limit}";
}
if ($this->offset !== null) {
$sql .= " OFFSET {$this->offset}";
}
return $sql;
}
}
数据验证层
<?php
trait Validatable
{
private array $errors = [];
public function validate(array $rules): bool
{
$this->errors = [];
foreach ($rules as $field => $validations) {
$validations = explode('|', $validations);
foreach ($validations as $validation) {
// 解析验证规则
if (strpos($validation, ':') !== false) {
[$rule, $parameter] = explode(':', $validation);
} else {
$rule = $validation;
$parameter = null;
}
// 执行验证
$methodName = 'validate' . ucfirst($rule);
if (method_exists($this, $methodName)) {
if (!$this->$methodName($this->$field, $parameter)) {
$this->errors[$field][] = "Field {$field} failed {$rule} validation";
}
}
}
}
return empty($this->errors);
}
private function validateRequired($value): bool
{
return !empty($value);
}
private function validateEmail($value): bool
{
return filter_var($value, FILTER_VALIDATE_EMAIL) !== false;
}
private function validateMin($value, $min): bool
{
return strlen($value) >= (int)$min;
}
public function getErrors(): array
{
return $this->errors;
}
}
最佳实践总结
设计原则
- 单一职责:每个模型只负责一个业务领域
- 依赖注入:通过构造函数注入依赖
- 接口编程:定义接口,实现松耦合
- 实体与数据分离:Entity和Repository分离
层次结构
Controller
↓
Service (业务逻辑)
↓
Repository (数据访问)
↓
Entity (数据实体)
注意事项
- 避免在模型层写SQL,使用查询构建器
- 使用类型提示和返回类型声明
- 实现数据库事务管理
- 添加适当的日志记录
- 进行单元测试覆盖
这样设计的模型层具有良好的可扩展性、可维护性和测试性。