怎样在PHP项目中实现数据模型?

wen java案例 5

本文目录导读:

怎样在PHP项目中实现数据模型?

  1. 最简单:使用PDO封装(适合小项目)
  2. ActiveRecord模式(熟悉但易混淆)
  3. Data Mapper模式(复杂但干净)
  4. 实战推荐:基于PDO的仓库(Repository)模式
  5. 完整项目结构建议
  6. 使用第三方ORM(生产推荐)
  7. 关键注意事项
  8. 选择建议

在PHP项目中实现数据模型,通常有三种主流方式:原生PDO封装ActiveRecord模式Data Mapper模式,下面我会从基础到进阶,给出完整的实现方案和代码示例。

最简单:使用PDO封装(适合小项目)

这是最轻量的方式,适合没有ORM框架的简单项目。

// User.php - 数据模型类
class User {
    private $pdo;
    public function __construct(PDO $pdo) {
        $this->pdo = $pdo;
    }
    public function find($id) {
        $stmt = $this->pdo->prepare('SELECT * FROM users WHERE id = ?');
        $stmt->execute([$id]);
        return $stmt->fetch(PDO::FETCH_ASSOC);
    }
    public function findByEmail($email) {
        $stmt = $this->pdo->prepare('SELECT * FROM users WHERE email = ?');
        $stmt->execute([$email]);
        return $stmt->fetch(PDO::FETCH_ASSOC);
    }
    public function create(array $data) {
        $sql = 'INSERT INTO users (name, email, password) VALUES (?, ?, ?)';
        $stmt = $this->pdo->prepare($sql);
        return $stmt->execute([
            $data['name'],
            $data['email'],
            password_hash($data['password'], PASSWORD_DEFAULT)
        ]);
    }
}

使用示例:

$pdo = new PDO('mysql:host=localhost;dbname=test', 'root', '');
$userModel = new User($pdo);
$user = $userModel->findByEmail('user@example.com');

ActiveRecord模式(熟悉但易混淆)

Laravel的Eloquent、ThinkPHP的Model都采用此模式。

// User.php - 使用ActiveRecord模式
class User {
    protected $pdo;
    protected $table = 'users';
    public $id;
    public $name;
    public $email;
    public function __construct(PDO $pdo, array $data = []) {
        $this->pdo = $pdo;
        foreach ($data as $key => $value) {
            if (property_exists($this, $key)) {
                $this->$key = $value;
            }
        }
    }
    // 查找并返回模型实例
    public static function find(PDO $pdo, $id) {
        $stmt = $pdo->prepare("SELECT * FROM users WHERE id = ?");
        $stmt->execute([$id]);
        $data = $stmt->fetch(PDO::FETCH_ASSOC);
        return $data ? new self($pdo, $data) : null;
    }
    // 保存当前实例(插入或更新)
    public function save() {
        if ($this->id) {
            // 更新
            $stmt = $this->pdo->prepare(
                "UPDATE users SET name = ?, email = ? WHERE id = ?"
            );
            return $stmt->execute([$this->name, $this->email, $this->id]);
        } else {
            // 插入
            $stmt = $this->pdo->prepare(
                "INSERT INTO users (name, email) VALUES (?, ?)"
            );
            $result = $stmt->execute([$this->name, $this->email]);
            if ($result) {
                $this->id = $this->pdo->lastInsertId();
            }
            return $result;
        }
    }
    // 删除
    public function delete() {
        if (!$this->id) return false;
        $stmt = $this->pdo->prepare("DELETE FROM users WHERE id = ?");
        return $stmt->execute([$this->id]);
    }
}

Data Mapper模式(复杂但干净)

像Doctrine 2采用此模式,模型完全与数据库解耦。

// 1. 实体类 - 纯数据对象
class UserEntity {
    private $id;
    private $name;
    private $email;
    public function getId() { return $this->id; }
    public function getName() { return $this->name; }
    public function setName($name) { $this->name = $name; }
    public function getEmail() { return $this->email; }
    public function setEmail($email) { $this->email = $email; }
}
// 2. 映射器 - 负责数据库操作
class UserMapper {
    private $pdo;
    public function __construct(PDO $pdo) {
        $this->pdo = $pdo;
    }
    public function find($id): ?UserEntity {
        $stmt = $this->pdo->prepare("SELECT * FROM users WHERE id = ?");
        $stmt->execute([$id]);
        $data = $stmt->fetch(PDO::FETCH_ASSOC);
        if (!$data) return null;
        $user = new UserEntity();
        $user->setId($data['id']); // 需要额外加setId方法
        $user->setName($data['name']);
        $user->setEmail($data['email']);
        return $user;
    }
    public function save(UserEntity $user) {
        $id = $user->getId();
        if ($id) {
            $stmt = $this->pdo->prepare("UPDATE users SET name=?, email=? WHERE id=?");
            return $stmt->execute([$user->getName(), $user->getEmail(), $id]);
        } else {
            $stmt = $this->pdo->prepare("INSERT INTO users (name, email) VALUES (?, ?)");
            $result = $stmt->execute([$user->getName(), $user->getEmail()]);
            if ($result) {
                $user->setId($this->pdo->lastInsertId());
            }
            return $result;
        }
    }
}

实战推荐:基于PDO的仓库(Repository)模式

这是目前最推荐的实践方式,结合了简洁与清晰。

// repositories/UserRepository.php
class UserRepository {
    private $pdo;
    public function __construct(PDO $pdo) {
        $this->pdo = $pdo;
    }
    public function findById($id): ?array {
        $stmt = $this->pdo->prepare("SELECT * FROM users WHERE id = ? AND deleted_at IS NULL");
        $stmt->execute([$id]);
        return $stmt->fetch(PDO::FETCH_ASSOC) ?: null;
    }
    public function findByEmail($email): ?array {
        $stmt = $this->pdo->prepare("SELECT * FROM users WHERE email = ?");
        $stmt->execute([$email]);
        return $stmt->fetch(PDO::FETCH_ASSOC) ?: null;
    }
    public function findAll(int $page = 1, int $perPage = 20): array {
        $offset = ($page - 1) * $perPage;
        $stmt = $this->pdo->prepare(
            "SELECT * FROM users ORDER BY created_at DESC LIMIT ? OFFSET ?"
        );
        $stmt->execute([$perPage, $offset]);
        return $stmt->fetchAll(PDO::FETCH_ASSOC);
    }
    public function save(array $data): int {
        $stmt = $this->pdo->prepare(
            "INSERT INTO users (name, email, password, created_at) VALUES (?, ?, ?, NOW())"
        );
        $stmt->execute([
            $data['name'],
            $data['email'],
            password_hash($data['password'], PASSWORD_DEFAULT)
        ]);
        return (int)$this->pdo->lastInsertId();
    }
    public function update($id, array $data): bool {
        $sets = [];
        $values = [];
        foreach ($data as $key => $value) {
            $sets[] = "$key = ?";
            $values[] = $value;
        }
        $values[] = $id;
        $sql = "UPDATE users SET " . implode(', ', $sets) . " WHERE id = ?";
        return $this->pdo->prepare($sql)->execute($values);
    }
    public function delete($id): bool {
        $stmt = $this->pdo->prepare("DELETE FROM users WHERE id = ?");
        return $stmt->execute([$id]);
    }
}

完整项目结构建议

your-project/
├── src/
│   ├── Models/          # 数据模型(实体类,可选)
│   │   └── UserEntity.php
│   ├── Repositories/    # 仓库层(数据访问)
│   │   └── UserRepository.php
│   ├── Services/        # 业务逻辑层
│   │   └── UserService.php
│   └── Database/        # 数据库连接
│       └── Connection.php
├── config/
│   └── database.php
└── public/
    └── index.php

使用第三方ORM(生产推荐)

如果项目规模较大,建议直接使用成熟的ORM:

Laravel + Eloquent

// 模型定义
class User extends Model {
    protected $fillable = ['name', 'email', 'password'];
    public function posts() {
        return $this->hasMany(Post::class);
    }
}
// 使用
$user = User::find(1);
$user->name = 'New Name';
$user->save();

MySQLi基础封装(最简方案)

class Db {
    private static $instance;
    private $mysqli;
    private function __construct() {
        $this->mysqli = new mysqli(DB_HOST, DB_USER, DB_PASS, DB_NAME);
    }
    public static function table($table) {
        self::getInstance()->table = $table;
        return self::getInstance();
    }
    public function find($id) {
        $stmt = $this->mysqli->prepare("SELECT * FROM {$this->table} WHERE id = ?");
        $stmt->bind_param("i", $id);
        $stmt->execute();
        return $stmt->get_result()->fetch_assoc();
    }
}

关键注意事项

  1. SQL注入防护:永远使用预处理语句(prepare + execute)
  2. 连接管理:生产环境使用连接池或单例模式
  3. 数据验证:在模型中做基础验证,在Service层做业务验证
  4. 异常处理:统一封装成自定义异常类
  5. 性能考虑:对于复杂查询,使用查询构建器而不是纯SQL拼接

选择建议

  • 项目<5万行代码:PDO封装 + Repository模式足够
  • 中大型项目:使用Laravel的Eloquent或Symfony的Doctrine
  • 微服务:可以考虑轻量级的Medoo或NotORM
  • 追求极致性能:手写PDO + 简单的ActiveRecord模式

最重要的是保持一致性:整个团队使用同一模式,并且数据访问层与业务逻辑层充分分离。

抱歉,评论功能暂时关闭!