重写条令惰性加载方法,将计算数据包括到对象中



假设我有这样的东西:

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

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

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

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

但我不知道用教条标准的行为怎么能做到这一点。

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

我正在使用symfony。。。也许是服务什么的。。。

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

条令倾听者

这可能是最简单的一个。使用条令postLoad事件填写您需要的User模型数据。

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

  1. 每当条令从数据库中获取用户实体实例时,都会运行此操作。如果它获取一个包含100个用户的列表,它将被运行100次。如果你在那里做一些耗时的任务,这可能会造成性能问题
  2. 它们很难调试:如果遇到错误,事件通常会使代码流变得不那么清晰,从而使调试更加困难。如果你做一些简单的事情,不要过度使用,那么可能没关系,否则就考虑其他选择

抽象主义

我非常赞成这种方法,我几乎在每个项目中都使用它。

尽管我是那种尽量减少层和间接性的人,但我确实认为将数据持久性封装到自己的服务中是个好主意。它使系统的其余部分不必知道数据是如何存储的。

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

这使您的持久性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
    }
}

这种方法有很多优点:

与条令脱钩

您的业务代码不再与条令耦合,它根本不知道条令存储库/实体管理器的存在。如果需要,您可以更改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;

您可以实现自己的存储库来包含自己的sql查询,Symfony在文档中已经很好地记录了这一点,请参阅此处。

以下是我以前使用注释的方法(这也可以通过yaml完成,只需查看上面的链接)。。。

实体:

namespace AppBundleEntity;
use DoctrineORMMapping as ORM;
/**
 * @ORMEntity(repositoryClass="AppBundleRepositoryUserRepository")
 */
class User
{
    // ...
    private $statistic;
    // ...
}

用户存储库:

namespace AppBundleEntity;
use DoctrineORMEntityRepository;
class ProductRepository extends EntityRepository
{
    public function getUserStats()
    {
        // Use query builder to build your query for stats here
    }
}

由于您的自定义存储库正在扩展EntityRepository,因此您仍然可以访问Doctrines惰性加载方法(find、findBy、findAll等)

也许您并不需要用户实例。您可以在DQL中使用NEW()语法。

class UserStatDTO
{
     private $user;
     private $statistic;
     private $sum;
     public function __construct(User $user, $statistic, $sum)
     {
         $this->user = $user;
         $this->statistic = $statistic;
         $this->sum = $sum;
     }
     public function getUser()
     {
         return $this->user;
     }
     public function getSum()
     {
         return $this->sum;
     }
     public function getStatistic()
     {
         return $this->statistic;
     }
}
class UserRepository
{
     public function getUsersWithCalculatedStat()
     { 
           return $this->getEntityManager()->createQuery('
                     SELECT NEW UserStatDTO(
                         u, u.statistic, u.count1 + u.count2
                     ) FROM User 
                  ')->getResult();
     }
}

http://doctrine-orm.readthedocs.org/projects/doctrine-orm/en/latest/reference/dql-doctrine-query-language.html#new-运算符语法

相关内容

  • 没有找到相关文章

最新更新