PHP项目标识映射与单元工作

wen PHP项目 2

本文目录导读:

PHP项目标识映射与单元工作

  1. 标识映射(Identity Map)
  2. 工作单元(Unit of Work)
  3. 组合使用示例
  4. 高级特性
  5. 最佳实践
  6. 框架中的实现

我来全面讲解PHP项目中标识映射(Identity Map)工作单元(Unit of Work) 这两种企业级设计模式。

标识映射(Identity Map)

概念

确保每个数据库记录在内存中只有一个对象实例,避免同一记录被加载多次。

实现示例

<?php
class IdentityMap
{
    private array $objects = [];
    // 按类名和ID存储对象
    public function set(string $class, $id, object $object): void
    {
        $key = $this->createKey($class, $id);
        $this->objects[$key] = $object;
    }
    // 按类名和ID获取对象
    public function get(string $class, $id): ?object
    {
        $key = $this->createKey($class, $id);
        return $this->objects[$key] ?? null;
    }
    // 检查对象是否已加载
    public function has(string $class, $id): bool
    {
        $key = $this->createKey($class, $id);
        return isset($this->objects[$key]);
    }
    // 移除对象
    public function remove(string $class, $id): void
    {
        $key = $this->createKey($class, $id);
        unset($this->objects[$key]);
    }
    // 清空所有对象
    public function clear(): void
    {
        $this->objects = [];
    }
    private function createKey(string $class, $id): string
    {
        return sprintf('%s:%s', $class, $id);
    }
}
// 使用示例
class UserRepository
{
    private IdentityMap $identityMap;
    private PDO $pdo;
    public function __construct(PDO $pdo, IdentityMap $identityMap)
    {
        $this->pdo = $pdo;
        $this->identityMap = $identityMap;
    }
    public function findById(int $id): ?User
    {
        // 先从标识映射中查找
        if ($this->identityMap->has(User::class, $id)) {
            echo "从缓存返回\n";
            return $this->identityMap->get(User::class, $id);
        }
        // 从数据库加载
        $stmt = $this->pdo->prepare('SELECT * FROM users WHERE id = ?');
        $stmt->execute([$id]);
        $data = $stmt->fetch(PDO::FETCH_ASSOC);
        if (!$data) {
            return null;
        }
        // 创建对象并存入标识映射
        $user = new User($data);
        $this->identityMap->set(User::class, $id, $user);
        echo "从数据库加载\n";
        return $user;
    }
}

工作单元(Unit of Work)

概念

维护所有被修改的对象列表,并在事务边界统一提交所有变更。

完整实现

<?php
// 对象状态枚举
enum ObjectState: string
{
    case CLEAN = 'clean';
    case DIRTY = 'dirty';      // 需要更新
    case NEW = 'new';          // 需要插入
    case REMOVED = 'removed';  // 需要删除
}
// 工作单元实现
class UnitOfWork
{
    private IdentityMap $identityMap;
    private array $objects = [];  // key => {object, state, originalData}
    private array $callbacks = [];
    public function __construct(IdentityMap $identityMap)
    {
        $this->identityMap = $identityMap;
    }
    // 注册新对象
    public function registerNew(object $object): void
    {
        $key = $this->getObjectKey($object);
        $this->objects[$key] = [
            'object' => $object,
            'state' => ObjectState::NEW,
            'originalData' => $this->getObjectData($object)
        ];
    }
    // 注册脏对象(已修改)
    public function registerDirty(object $object): void
    {
        $key = $this->getObjectKey($object);
        if (!isset($this->objects[$key])) {
            $this->objects[$key] = [
                'object' => $object,
                'state' => ObjectState::DIRTY,
                'originalData' => $this->getObjectData($object)
            ];
        } elseif ($this->objects[$key]['state'] !== ObjectState::NEW) {
            $this->objects[$key]['state'] = ObjectState::DIRTY;
        }
    }
    // 注册删除对象
    public function registerRemoved(object $object): void
    {
        $key = $this->getObjectKey($object);
        if (isset($this->objects[$key]) && $this->objects[$key]['state'] === ObjectState::NEW) {
            unset($this->objects[$key]);  // 如果是新对象,直接移除
            return;
        }
        $this->objects[$key] = [
            'object' => $object,
            'state' => ObjectState::REMOVED,
            'originalData' => $this->getObjectData($object)
        ];
    }
    // 注册清理对象
    public function registerClean(object $object): void
    {
        $key = $this->getObjectKey($object);
        $this->objects[$key] = [
            'object' => $object,
            'state' => ObjectState::CLEAN,
            'originalData' => $this->getObjectData($object)
        ];
    }
    // 提交所有变更
    public function commit(Persister $persister): void
    {
        try {
            $persister->beginTransaction();
            // 按顺序处理:新增、更新、删除
            foreach ($this->objects as $key => $info) {
                switch ($info['state']) {
                    case ObjectState::NEW:
                        $persister->insert($info['object']);
                        break;
                    case ObjectState::DIRTY:
                        $persister->update($info['object']);
                        break;
                    case ObjectState::REMOVED:
                        $persister->delete($info['object']);
                        break;
                }
            }
            $persister->commit();
            // 提交后,所有对象变为clean状态
            foreach ($this->objects as $key => $info) {
                if ($info['state'] !== ObjectState::REMOVED) {
                    $this->registerClean($info['object']);
                } else {
                    unset($this->objects[$key]);
                }
            }
            // 执行回调
            foreach ($this->callbacks as $callback) {
                $callback();
            }
        } catch (\Exception $e) {
            $persister->rollback();
            throw $e;
        }
    }
    // 注册提交后的回调
    public function addCommitCallback(callable $callback): void
    {
        $this->callbacks[] = $callback;
    }
    private function getObjectKey(object $object): string
    {
        return spl_object_id($object);
    }
    private function getObjectData(object $object): array
    {
        // 实际项目中应该使用反射或序列化
        return (array) $object;
    }
}
// 持久化器接口
interface Persister
{
    public function beginTransaction(): void;
    public function commit(): void;
    public function rollback(): void;
    public function insert(object $object): void;
    public function update(object $object): void;
    public function delete(object $object): void;
}
// 具体持久化器实现
class DatabasePersister implements Persister
{
    private PDO $pdo;
    public function __construct(PDO $pdo)
    {
        $this->pdo = $pdo;
    }
    public function beginTransaction(): void
    {
        $this->pdo->beginTransaction();
    }
    public function commit(): void
    {
        $this->pdo->commit();
    }
    public function rollback(): void
    {
        $this->pdo->rollBack();
    }
    public function insert(object $object): void
    {
        if ($object instanceof User) {
            $stmt = $this->pdo->prepare('INSERT INTO users (name, email) VALUES (?, ?)');
            $stmt->execute([$object->getName(), $object->getEmail()]);
            $object->setId((int) $this->pdo->lastInsertId());
        }
    }
    public function update(object $object): void
    {
        if ($object instanceof User) {
            $stmt = $this->pdo->prepare('UPDATE users SET name = ?, email = ? WHERE id = ?');
            $stmt->execute([$object->getName(), $object->getEmail(), $object->getId()]);
        }
    }
    public function delete(object $object): void
    {
        if ($object instanceof User) {
            $stmt = $this->pdo->prepare('DELETE FROM users WHERE id = ?');
            $stmt->execute([$object->getId()]);
        }
    }
}

组合使用示例

<?php
// 完整的应用服务示例
class UserService
{
    private UnitOfWork $unitOfWork;
    private UserRepository $repository;
    public function __construct(UnitOfWork $unitOfWork, UserRepository $repository)
    {
        $this->unitOfWork = $unitOfWork;
        $this->repository = $repository;
    }
    // 创建用户
    public function createUser(string $name, string $email): User
    {
        $user = new User(null, $name, $email);
        // 注册到工作单元
        $this->unitOfWork->registerNew($user);
        // 提交事务
        $this->unitOfWork->commit(new DatabasePersister(new PDO('...')));
        return $user;
    }
    // 更新用户信息
    public function updateUser(int $userId, string $name, string $email): void
    {
        // 从标识映射获取用户(会先检查缓存)
        $user = $this->repository->findById($userId);
        if (!$user) {
            throw new \RuntimeException('User not found');
        }
        // 修改用户属性
        $user->setName($name);
        $user->setEmail($email);
        // 标记为脏对象
        $this->unitOfWork->registerDirty($user);
        // 批量提交
        $this->unitOfWork->commit(new DatabasePersister(new PDO('...')));
    }
    // 批量操作示例
    public function batchUpdate(): void
    {
        $persister = new DatabasePersister(new PDO('...'));
        // 多个修改
        $user1 = $this->repository->findById(1);
        $user1->setName('New Name 1');
        $this->unitOfWork->registerDirty($user1);
        $user2 = $this->repository->findById(2);
        $this->unitOfWork->registerRemoved($user2);
        $user3 = new User(null, 'New User', 'new@email.com');
        $this->unitOfWork->registerNew($user3);
        // 一次提交所有变更
        $this->unitOfWork->commit($persister);
    }
}
// 使用示例
try {
    $identityMap = new IdentityMap();
    $unitOfWork = new UnitOfWork($identityMap);
    $pdo = new PDO('mysql:host=localhost;dbname=test', 'root', '');
    $repository = new UserRepository($pdo, $identityMap);
    $service = new UserService($unitOfWork, $repository);
    // 批量操作
    $service->batchUpdate();
    echo "所有操作成功完成\n";
} catch (\Exception $e) {
    echo "操作失败: " . $e->getMessage() . "\n";
}

高级特性

懒加载与标识映射结合

<?php
class LazyLoader
{
    private \Closure $loader;
    private bool $loaded = false;
    private $value;
    public function __construct(\Closure $loader)
    {
        $this->loader = $loader;
    }
    public function get()
    {
        if (!$this->loaded) {
            $this->value = ($this->loader)();
            $this->loaded = true;
        }
        return $this->value;
    }
}
// 在标识映射中使用懒加载
class ProxyUser extends User
{
    private LazyLoader $profileLoader;
    public function __construct(int $id)
    {
        parent::__construct();
        $this->setId($id);
        // 配置懒加载
        $this->profileLoader = new LazyLoader(function () {
            // 从数据库加载额外数据
            return $this->loadProfileFromDB();
        });
    }
    public function getProfile()
    {
        return $this->profileLoader->get();
    }
}

最佳实践

  1. 事务边界清晰:工作单元应位于单一事务边界内
  2. 合理的粒度:不要在一个工作单元中处理过多对象
  3. 缓存策略:标识映射应与缓存策略结合,考虑过期时间
  4. 并发控制:考虑乐观锁或悲观锁防止并发问题
  5. 测试:使用依赖注入方便单元测试

框架中的实现

许多PHP框架已经实现了这些模式:

  • Doctrine ORM:完整的标识映射和工作单元实现
  • Laravel Eloquent:部分实现了标识映射
  • Yii2 ActiveRecord:有自己的工作单元实现

了解这些底层模式有助于更好地理解和使用ORM框架。

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