我目前正在使用Symfony 3构建一个支持多种语言的电子商务网站,并意识到我设计产品实体的方式需要加入多个其他实体,使用DQL/查询生成器来加载翻译、产品评论和折扣/特惠等内容。但这意味着我将有一个连接块,这些连接块在多个存储库中是相同的,这似乎是错误的,因为如果我们需要添加或更改连接以加载额外的产品数据,就必须找出所有这些块。
例如,在CartRepository的loadCart()函数中,我有一个DQL查询,如下所示:
SELECT c,i,p,pd,pt,ps FROM
AppBundle:Cart c
join c.items i
join i.product p
left join p.productDiscount pd
join p.productTranslation pt
left join p.productSpecial ps
where c.id = :id
当我在该页面上显示产品列表时,SectionRepository中会出现类似的内容,正确的处理方法是什么?有没有什么地方我可以集中定义需要加载的实体列表,以使连接的实体(在本例中为Product)完整。我意识到我可以只使用延迟加载,但这会导致大量查询在部分页面这样的页面上运行(如果我使用正确连接的查询,显示40个产品的部分需要运行121个查询,而不是1个)。
一种方法(这只是我的想法,有些人可能有更好的方法)。您可以很容易地使用一个集中的querybuilder函数/服务来实现这一点。querybuilder非常适合以程序方式构建查询。关键区别在于根实体和过滤实体。
例如,像这样的东西。当然,请注意,这些不会都在同一个地方(它们可能跨几个服务、存储库等),这只是一个需要考虑的方法的例子。
public function getCartBaseQuery($cartId, $joinAlias = 'o') {
$qb = $this->getEntityManager()->createQueryBuilder();
$qb->select($joinAlias)
->from('AppBundle:Cart', 'c')
->join('c.items', $joinAlias)
->where($qb->expr()->eq('c.id', ':cartId'))
->setParameter('cartId', $cartId);
return $qb;
}
public function addProductQueryToItem($qb, $alias) {
/** @var QueryBuilder $query */
$qb
->addSelect('p, pd, pt, ps')
->join($alias.'product', 'p')
->leftJoin('p.productDiscount', 'pd')
->join('p.productTranslation', 'pt')
->join('p.productSpecial', 'ps')
;
return $qb;
}
public function loadCart($cartId) {
$qbcart = $someServiceOrRepository->getCartBaseQuery($cartId);
$qbcart = $someServiceOrRepository->addProductQueryToItem($qbcart);
return $qbcart->getQuery()->getResult();
}
就像我说的,这只是一种可能的方法,但希望它能给你一些想法,让你开始解决这个问题。
注意:如果您对附加产品数据的实体严格使用相同的联接别名,您甚至不必在调用中指定它(但我会自己配置它)。
您的问题没有一个正确答案。
但如果我必须提出建议,我会说看看CQRS(http://martinfowler.com/bliki/CQRS.html)这基本上意味着你有一个分离的读取模型。
为了使其尽可能简单,假设您构建了一个单独的"extended_product"表,在该表中,所有数据都已连接和取消规范化。此表可以通过后台任务定期填充,也可以通过每次更新产品或相关实体时触发的命令填充。
当您需要读取产品数据时,您可以查询此表,而不是原始表。当然,没有什么可以阻止您使用许多不同的扩展表,以单独的方式排列数据。
在某种程度上,这是一个与数据库"视图"非常相似的概念,只是:
- 它更快,因为您查询的是一个实际的表
- 由于您是通过代码创建该表的,因此您不局限于处理数据的单个SQL查询(想想筛选器、聚合等)
我知道这并不完全是一个"答案",但希望它能给你一些关于如何解决问题的好主意。