实体和存储库中的 Doctrine 可重用条件与连接

Doctrine reusable Criteria in Entity and Repository with joins

我已经搜索这个特定问题很长时间了,不确定我是否对在查询中使用 Criteria 有误解,或者它是否不可能(现在?)

这是一个只有最少字段和条件的示例。

我有两个实体:

在品牌实体中,我有一个方法 "getVisibleProducts()" 我想在其中获取所有产品 product.visible = 1

在我的 ProductRepository 中,我有一个方法 findAllVisibleForBrand(Brand $brand),我想在其中获取具有 product.visible = 1brand.active = 1

的所有产品

所以我的最佳可重用性想法是为我的 ProductRepository 中的 product.visible 值制定一个标准,并将其用于存储库中的查询

/**
 * @param Brand $brand
 *
 * @return Product[]
 */
public function findAllVisibleForBrand(Brand $brand)
{
    return $this->createQueryBuilder('product')
        ->leftJoin('product.brand', 'brand')
        ->addCriteria(ProductRepository::createVisibleCriteria())
        ->andWhere('product.brand = :brand')
        ->setParameter('brand', $brand)
        ->getQuery()
        ->execute();
        ;
}

/**
 * @return Criteria
 */
public static function createVisibleCriteria()
{
    return Criteria::create()
        ->andWhere(Criteria::expr()->eq('visible', '1'))
    ;
}  

在我的品牌实体中:

/**
 * @return Product[]|ArrayCollection
 */
public function getVisibleProducts()
{
    return $this->getProducts()->matching(ProductRepository::createVisibleCriteria());
}

这工作正常,没有问题。

BrandRepository 的想法相同

/**
 * @return Brand[]
 */
public function findAllActiveBrands()
{
    return $this->createQueryBuilder('brand')
                ->addCriteria(BrandRepository::createActiveCriteria())
                ->getQuery()
                ->execute()
        ;
}

/**
 * @return Criteria
 */
public static function createActiveCriteria()
{
    return Criteria::create()
       ->andWhere(Criteria::expr()->eq('active', '1'))
    ;
}

现在我的想法是在我的 ProductRepository 中使用 Brand Active Criteria 来只获取可见的产品,但前提是品牌是活跃的。所以我的方法现在看起来像这样:

/**
 * @param Brand $brand
 *
 * @return Product[]
 */
public function findAllVisibleForBrand(Brand $brand)
{
    return $this->createQueryBuilder('product')
        ->leftJoin('product.brand', 'brand')
        ->addCriteria(BrandRepository::createActiveCriteria())  // added this line
        ->addCriteria(ProductRepository::createVisibleCriteria())
        ->andWhere('product.brand = :brand')
        ->setParameter('brand', $brand)
        ->getQuery()
        ->execute();
        ;
}

通过这种方式添加 brandActiveCriteria,我收到以下错误:

[Semantical Error] line 0, col 97 near 'active = :active': Error: Class AppBundle\Entity\Product has no field or association named active

正在构建的查询学说如下所示:

SELECT product FROM AppBundle\Entity\Product product LEFT JOIN product.brand brand WHERE product.active= :active AND product.visible = :visible AND product.brand = :brand

将 table 名称添加到标准中适用于存储库中的查询,但不适用于实体中的查询,因为 table 别名不同。

是否有机会将规则作为标准或在任何其他地方而不重复多个 类?

我想到了使用静态函数来创建标准 https://knpuniversity.com/screencast/collections/criteria-collection-filtering

您可以在条件中添加别名:

public static function createActiveCriteria($alias = null)
{
    return Criteria::create()
       ->andWhere(Criteria::expr()->eq(($alias ? $alias.'.' : '').'active', '1'))
    ;
}

然后你这样调用你的标准:

// "brand" is the name of the given alias in leftJoin
BrandRepository::createActiveCriteria('brand'); 

因此您的 findAllVisibleForBrand() 方法将如下所示:

public function findAllVisibleForBrand(Brand $brand)
{
    return $this->createQueryBuilder('product')
        ->leftJoin('product.brand', 'brand')
        ->addCriteria(BrandRepository::createActiveCriteria('brand')) // Notice the alias parameter here
        ->addCriteria(ProductRepository::createVisibleCriteria())
        ->andWhere('product.brand = :brand')
        ->setParameter('brand', $brand)
        ->getQuery()
        ->execute();
        ;
}

你的 DQL 应该是这样的:

SELECT product FROM 
   AppBundle\Entity\Product product 
   LEFT JOIN product.brand brand 
   WHERE 
      brand.active= :active 
      AND product.visible = :visible 
      AND product.brand = :brand