让我说我有这样的事情:
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 ...也许是服务或其他东西......
答案 0 :(得分:4)
有很多方法可以解决您的问题。
这可能是最简单的一个。使用Doctrine postLoad
事件填写User
模型所需的数据。
这是一种完全有效的方法,但有一些缺点:
我非常赞成这种方法,我几乎在每个项目中都使用它。
即使我是其中一个尝试拥有最少层数和间接需求的人,我确实认为将数据持久性包装到您自己的服务中是一个好主意。它将您系统的其他部分与必须知道 数据的存储隔离开来。
我建议不直接使用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或其他...
它使您的系统具有哪些能力,使理解更容易并且更容易进行测试。
作为第一种方法,它不会遇到性能问题。请看以下示例:
您的$eventsCount
型号上有User
字段。
如果您加载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;
答案 1 :(得分:1)
您可以实现自己的存储库以包含您自己的SQL查询,Symfony在其文档中已经很好地记录了它,请参阅here。
以下是我之前使用注释做过的事情(这也可以通过yaml完成,只需查看上面的链接)...
实体:
namespace AppBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* @ORM\Entity(repositoryClass="AppBundle\Repository\UserRepository")
*/
class User
{
// ...
private $statistic;
// ...
}
用户存储库:
namespace AppBundle\Entity;
use Doctrine\ORM\EntityRepository;
class ProductRepository extends EntityRepository
{
public function getUserStats()
{
// Use query builder to build your query for stats here
}
}
由于您的自定义存储库正在扩展EntityRepository,您仍然可以访问Doctrines延迟加载方法(find,findBy,findAll等...)
答案 2 :(得分:1)
也许您不需要完全用户实例。 您可以在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();
}
}