如何在doctrine 2实体的存储库中使用复杂的标准?

Dor*_*ron 34 php sql doctrine criteria doctrine-orm

让我们说我有一张桌子,里面有关于节日的信息.
每个节日都有开始和结束日期.

我想选择在给定日期生活(发生)的所有节日.
这意味着,我想选择他们的开始日期在给定日期之前或之后的所有节日,以及他们的结束日期是在同一日期之后或之后.

所以我继续了节日实体的存储库类,并创建了一个方法来做到这一点.
但是标准参数"findBy"期望的是一个数组,所有的例子都只将其视为一个简单的标准(例如"array('name'=>'billy')"将选择所有具有billy值的行. name column),它只使用比较运算符.

我怎样才能使用其他运算符

>, <, !=, IN, NOT IN, LIKE    
Run Code Online (Sandbox Code Playgroud)

等等. ?

谢谢

Ree*_*e45 89

Doctrine 2.3添加了一个匹配()方法,允许您使用Criteria.

Jeremy Hicks的例子可能是这样写的(注意,这会返回一个ArrayCollection而不是数组).

public function findActiveFestivals($start, $end)
{
    $expr = Criteria::expr();
    $criteria = Criteria::create();
    $criteria->where($expr->gte('start', $start));
    $criteria->andWhere($expr->lte('end', $end);
    return $this->matching($criteria);
}
Run Code Online (Sandbox Code Playgroud)

就个人而言,我不会在这里使用和使用更多的行来提高可读性,如下所示:

public function findActiveFestivals($start, $end)
{
    $expr = Criteria::expr();
    $criteria = Criteria::create();
    $criteria->where(
      $expr->andX(
        $expr->gte('start', $start),
        $expr->lte('end', $end)
      )
    );
    return $this->matching($criteria);
}
Run Code Online (Sandbox Code Playgroud)

使用IN子句非常简单.

public function findFestivalsByIds($ids)
{
    $expr = Criteria::expr();
    $criteria = Criteria::create();
    $criteria->where($expr->in('id', $ids));
    return $this->matching($criteria);
}
Run Code Online (Sandbox Code Playgroud)

Criteria类在Doctrine的非真正ORM或DBAL Common的命名空间中,就像它们的ArrayCollection(它支持的Criteria比EntityRepository更长).

它的意思是非存储库代码创建sophicated标准的解耦方式.所以在存储库之外使用这个类应该没问题.QueryBuilder 最近也支持Criteria.因此,即使在构建需要QueryBuilder的更复杂的查询时,您也可以使用Criteria为其提供非数据库代码的灵活性.

  • 在doctrine/orm 2.5中,EntityRepository :: matching将返回LazyCriteriaCollection以允许在不获取所有行的情况下进行计数(https://github.com/doctrine/doctrine2/pull/882) (11认同)
  • 有人可以解释为什么`Criteria :: create()`用来代替`new Criteria()`? (4认同)

Jer*_*cks 22

如果你想要特定的东西,你需要编写自己的查询(可能使用DQL).我相信内置的"findBy"方法更适合在没有特定标准的情况下快速抓取对象.我不知道您的实体名称或存储位置.可能是这样的节日存储库中的函数.

public function findActiveFestivals($start, $end)
{
    $qb = $this->_em->createQueryBuilder();
    $qb->select('f')
        ->from('Festival', 'f')
        ->where('f.start >= :start')
        ->andWhere('f.end <= :end')
        ->setParameters(array('start' => $start, 'end' => $end));

    return $qb->getQuery()->getArrayResult();
}
Run Code Online (Sandbox Code Playgroud)

  • 您知道 - 当从存储库类中调用$ this-> createQueryBuilder()时,您将获得一个查询构建器,其中已经配置了表和别名 (4认同)
  • 对于提出的问题,这不是一个真正的答案 (4认同)

小智 10

那不是Doron问题学说的答案有实体存储库,根本不能让我们使用查询...

$this->em->getRepository($this->entity)->findBy(array $criteria);
Run Code Online (Sandbox Code Playgroud)

但他怎么问是阵列的常规格式如何复杂的操作在数组$标准$criteriaarray('field'=> $value);


小智 9

我曾经遇到过同样的问题,由于复杂的查询,我的Doctrine存储库变得非常难看.我还必须将Yii ActiveRecord(带有Criteria对象)转换为Doctrine,而Doctrine当时没有Criteria对象.

我找到了Benjamin Eberlei博客文章,根据规范模式,这个问题有一个有趣的解决方案.

它使您能够将Query Builder对象的操作推迟到其他类.

$spec = new AndX(
    new Equals('ended', 0),
    new OrX(
        new LowerThan('endDate', new \DateTime()),
        new AndX(
            new IsNull('endDate'),
            new LowerThan('startDate', new \DateTime('-4weeks'))
        )
    )
);

return $this->em->getRepository('Advertisement')->match($spec)->execute()
Run Code Online (Sandbox Code Playgroud)

此外,您可以将2个或更多类组合在一起,从而创建可靠的可重用构建块:

public function myQuery(User $user)
{
    $spec = new AndX(
        new ExpiredAds(),
        new AdsByUser($user)
    );

    return $this->em->getRepository('Advertisement')->match($spec)->execute();
}
Run Code Online (Sandbox Code Playgroud)

在这种情况下,ExpiredAds()和AdsByUser()包含类似于第一个代码示例中的结构.

如果您认为该解决方案适合您,请让我建议您可以通过composer安装两个库: