假设我有这样的东西:
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
模型数据。
这是一种完全有效的方法,但有几个缺点:
- 每当条令从数据库中获取用户实体实例时,都会运行此操作。如果它获取一个包含100个用户的列表,它将被运行100次。如果你在那里做一些耗时的任务,这可能会造成性能问题
- 它们很难调试:如果遇到错误,事件通常会使代码流变得不那么清晰,从而使调试更加困难。如果你做一些简单的事情,不要过度使用,那么可能没关系,否则就考虑其他选择
抽象主义
我非常赞成这种方法,我几乎在每个项目中都使用它。
尽管我是那种尽量减少层和间接性的人,但我确实认为将数据持久性封装到自己的服务中是个好主意。它使系统的其余部分不必知道数据是如何存储的。
我建议不要直接使用条令存储库/实体管理器。相反,将它们封装在您自己的服务中
这使您的持久性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-运算符语法