php - 重写原则延迟加载方法以将计算数据包含到对象中

标签 php symfony doctrine-orm

假设我有这样的东西:

class User{
    /**
     * Object that is lazy loaded 
     */
    private $statistic; //object with some stored data and some calculated data
}

$statistic 的一些属性存储在数据库中,但其他一些属性是通过分析用户事件(查询数据记录)来计算的。

问题是我得到了一个 $user,当我按照预期运行 $user->getStatistic() 时,我得到了存储的 $statistic 数据,我需要使用 sql 查询添加更多数据,但我不知道在哪里对此功能进行编程。 ¿覆盖存储库?我尝试重写 find() 方法,但它不起作用

我知道,如果我使用事件记录模式,这可以毫无问题地完成,因为我可以在构造方法或 getter 中访问数据库,等等。

但我不知道如何通过学说标准行为来做到这一点。

我相信必须有一种方法来确保统计类的每个实例都有此计算数据。

我正在使用 symfony...也许是一项服务或其他东西...

最佳答案

有多种方法可以解决您的问题。

Doctrine listener

这可能是最简单的一个。使用教义 postLoad事件来填写 User 模型上所需的数据。

这是一种完全有效的方法,但有一些缺点:

  1. 每次学说从数据库获取用户实体实例时都会运行该命令。如果它获取 100 个用户的列表,它将运行 100 次。如果您在那里执行一些耗时的任务,这可能会导致性能问题。
  2. 它们很难调试:如果遇到错误,事件通常会使代码流变得不太清晰,从而使调试变得更加困难。如果您做简单的事情,并且不过度使用它们,那么可能没问题,否则请考虑其他选择。

抽象主义

我强烈支持这种方法,并且我几乎在每个项目中都使用它。

尽管我是尝试拥有最少必要层数和间接性的人之一,但我确实认为将数据持久性包装到您自己的服务中是一个好主意。它将系统的其余部分与您的数据如何存储隔离。

我建议不要直接使用 Doctrine 存储库/实体管理器。相反,将它们包装在您自己的服务中。

这使得您的持久性 API 非常干净和明显,同时使您能够在模型到达业务逻辑之前对其进行操作。

以下是我如何解决您的问题的示例:

# src/AppBundle/Repository/UserRepository.php

class UserRepository
{
    private $em;

    public function __construct(EntityManagerInterface $em)
    {
        $this->em = $em;
    }

    public function findById($userId)
    {
        $user = $this->em->getRepository(User::class)->find($userId);
        $this->calculateUserStatistics($user);

        return $user;
    }

    public function save(User $user)
    {
        $this->em->persist($user);
        $this->em->flush();
    }

    // ...

    private function calculateUserStatistics(User $user)
    {
        // calculate and set statistics on user object
    }
}

这种方法有很多优点:

与学说脱钩

您的业务代码不再与 Doctrine 耦合,它根本不知道 Doctrine 存储库/实体管理器的存在。如果需要,您可以更改 UserRepository 实现,以从远程 API、磁盘上的文件......从任何地方加载用户。

模型操作

它允许您在模型进入业务逻辑之前对其进行操作,从而允许您计算未作为数据库字段保留的值。例如,从 Redis、某些 API 或其他方式获取它们...

清洁 API

它使您的系统具有哪些功能变得非常明显,使理解更容易,测试也更容易。

性能优化

与第一种方法相比,它不会遇到性能问题。举个例子:

您的 User 模型上有 $eventsCount 字段。

如果您加载 100 个用户的列表并使用第一种方法,则需要触发 100 个查询来计算属于每个用户的事件数。

SELECT COUNT(*) FROM events WHERE user_id = 1;
SELECT COUNT(*) FROM events WHERE user_id = 2;
SELECT COUNT(*) FROM events WHERE user_id = 3;
SELECT COUNT(*) FROM events WHERE user_id = 4;
...

但是,如果您有自己的 UserRepository 实现,则只需创建方法 getEventCountsForUsers($userIds) 即可触发一个查询:

SELECT COUNT(*) FORM events WHERE user_id IN (:user_ids) GROUP BY user_id;

关于php - 重写原则延迟加载方法以将计算数据包含到对象中,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36607604/

相关文章:

php - PHP 和 Mysql 中的 session

symfony - 如何在 Sonata Admin Bundle 日期表单字段中设置最小年份值

php - Symfony2 无法在依赖注入(inject)中实例化接口(interface)

php - Symfony2 中的多线程

php - 教义实体未保存,但具有自动增量 id

events - Doctrine 2 preFlush 事件添加关联类

php - 将数据以加密形式存储在数据库中,并检索

php - jquery获取输入类型文本中写入的文本

php - Symfony 从数据库 DateTime 字段获取不同的月份

php - 在将 PHP 和 MySQL 代码存储到数据库之前或在显示时何时清理它们?