更新时间:2023-09-19 23:20:04
有很多方法可以解决您的问题。
这可能是最简单的一个。使用原则 postLoad
事件,以填写用户
模型所需的数据。
这是一个完全有效的方法,但有一些缺点:
我非常赞成这种方法,我几乎在每个项目中都使用它。
尽管我是尝试拥有最少层次和间接需求的人之一,但我认为将数据持久性包装到您自己的服务中是一个好主意。它隔离系统的其余部分不必知道如何存储数据。
我建议不要使用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) - > ;发现($用户id);
$ this-> calculateUserStatistics($ user);
return $ user;
}
public function save(User $ user)
{
$ this-> em-> persist($ user);
$ this-> em-> flush();
}
// ...
私有函数calculateUserStatistics(User $ user)
{
//计算并设置统计信息用户对象
}
}
这种方法有很多优点: / p>
您的商业代码已不再适用于Doctrine,它不知道Doctrine存储库/实体管理器存在。如果需要,您可以从任何地方更改 UserRepository
实现来从远程API加载用户,从磁盘上的文件....
它允许您在获得业务逻辑之前操纵模型,允许您计算不保留作为数据库中字段的值。例如,从Redis,某些API或其他...获取它们。
它使真的很明显你的系统有哪些能力,使得理解更容易,并且允许更容易的测试。
它不以性能问题为首,以下示例:
您的用户$中有
$ eventsCount
字段c $ c> model。
如果您加载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事件WHERE user_id IN(:user_ids)GROUP BY user_id;
let say that I have something like this:
class User{
/**
* Object that is lazy loaded
*/
private $statistic; //object with some stored data and some calculated data
}
Some of the $statistic's properties are stored in the DB but some other of them are calculated by analyzing the user activity (querying data records).
the thing is that I got a $user and when I run $user->getStatistic() as spected, I get the stored $statistic data and I need to add more data using sql queries and I don't know where to program this functionality. ¿overriding the Repository? I try overriding the find() method but it doesn't work
I know that if I use the active record pattern this can be done with no problem giving that I can access the DB in the construct method or the getters maybe, etc.
but I don't know how this could be done with doctrine standard behavior.
I believe that there must be a way to ensure that every instance of the Statistic Class have this calculated data on it.
I'm using symfony... maybe a service or something...
There are a number of ways to solve your problem.
This is probably the easiest one. Use Doctrine postLoad
event to fill out data you need on your User
model.
This is a completely valid approach, but has a couple of drawbacks:
I'm strongly in favor of this approach and I use it in almost every project.
Even though I'm one of the people who try to have the least amount of layers and indirection necessary, I do think that wrapping data persistence into your own services is a good idea. It isolates rest of your system from having to know how your data is stored.
I suggest not using Doctrine repositories/Entity manager directly. Instead, wrap them in your own services.
This makes your persistence API squeaky clean and obvious, while giving you ability to manipulate your models before they reach your business logic.
Here is an example of how I would approach your problem:
# 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
}
}
This approach has a number of advantages:
Your business code is no longer coupled to Doctrine, it doesn't know that Doctrine repositories/entity manager exist at all. If need arises, you can change UserRepository
implementation to load users from remote API, from file on disk....from anywhere.
It allows you to manipulate your models before they get to business logic, allowing you to calculate values not persisted as a field in database. Eg, to fetch them from Redis, from some API or other...
It makes it really obvious what abilities your system has, making understanding easier and allowing easier testing.
It doesn't suffer from performance issues as first approach. Take the following example:
You have $eventsCount
field on your User
model.
If you load list of 100 users and use first approach, you would need to fire 100 queries to count number of events belonging to each user.
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;
...
If you have your own UserRepository implementation, however, you can just make method getEventCountsForUsers($userIds)
which would fire one query:
SELECT COUNT(*) FORM events WHERE user_id IN (:user_ids) GROUP BY user_id;