过滤与Doctrine 2中的条件对象关联

时间:2014-03-26 19:50:59

标签: php doctrine-orm doctrine

假设我有一个实体Profile,它与Account实体有关联。我想使用profileCode = 12345 获取配置文件,其中相关的Account的电子邮件地址为my@email.com。所以,我需要在两个实体上指定一个条件。

为此,我创建了一个自定义存储库Repository\Profile,现在我想知道如何实现它。我知道我可以使用“原始”DQL查询或使用查询构建器来解决所有这些问题。但是,我觉得它不像我想的那样漂亮,因为它非常接近原始SQL。当然语法有点不同,但从概念上讲,我会在SQL方面考虑比OOP更多。我会做很多这样的事情,所以我真的想以最好的方式去做。

我已经对Criteria对象进行了一些阅读(文档很稀疏),只要我在单个实体上进行过滤,它看起来真的很棒。在过滤关联实体时,我无法找到使用Criteria的任何解决方案。

所以基本上我的问题是:有没有什么方法可以使用Criteria对象直接从数据库和自定义存储库中过滤多个实体?我会真的更愿意在自定义存储库中对其进行编码而不是实体本身,就像我看到一些人那样。这是可能的,还是有任何好的选择,或者是普通的DQL还是查询构建器真的要走的路?

2 个答案:

答案 0 :(得分:1)

Criteria只能过滤实体本身的关联,但是如果我理解你的用例,你需要过滤2个级别,所以它不起作用。

你正试图通过“按自己的方式”做事来将学说塑造成不可能的事物。从长远来看,这只会伤害到你。解决方案已经存在,使用DQL非常好,特别是在存储库中。

答案 1 :(得分:0)

我不明白为什么不这样做,但不确定你的协会是如何相关的。 One-to-OneOne-to-ManyMany-to-Many

如果您尝试从第3个关联深度级别检索记录,例如Account.profile - > Profile.code - > Code.xxx (not id)您需要使用DQL或QueryBuilder来确定Profile和Code加入帐户或解析Code实体以将其提供给Criteria。

假设One-to-Many其中一个帐户有多个个人资料。您也可以选择在实体本身内定义它,以便更轻松地管理并减少对getProfiles()的额外调用。

存储库使用

use Doctrine\ORM\EntityRepository;
use Doctrine\Common\Collections\Criteria;

class AccountRepository extends EntityRepository {

    /**
     * @var string|Account $accountOrEmail
     * @var string $code
     * @return Profile|null
     */
    public function getAccountProfileByEmailAndCode($accountOrEmail, $code)
    {
         try{
             $account = ($accountOrEmail instanceof Account ? $accountOrEmail: $this->findOneBy(['email' => $accountOrEmail]));
             if (!$account instanceof Account) {
                 throw new \InvalidArgumentException('Unknown Account Specified.');
             }
             $criteria = Criteria::create()->setMaxResult(1);
             $expr = $criteria::expr();
             $criteria->where($expr->eq('code', $code));

             return $account->getProfiles()->matching($criteria)->first();
         } catch(\Exception $e) {
             return null;
         }
    }

}
$accountRepo = $em->getRepository('\Entities\Account');
$profile = $accountRepo->getAccountProfileByEmailAndCode('my@email.com', '12345');

实体使用

use Doctrine\Common\Collections\Criteria;

/**
 * @ORM\Entity(repositoryClass="AccountRepository")
 */
class Account {

    // ...

    public function getProfiles(Criteria $criteria = null)
    {
        if ($criteria instanceof Criteria) {
            return $this->profiles->matching($criteria);
        }

        return $this->profiles;
    }

    /**
     * @var string $code
     * @return null|Profile
     */
    public function getProfileByCode($code)
    {
         $criteria = Criteria::create()->setMaxResult(1);
         $expr = $criteria::expr();
         $criteria->where($expr->eq('code', $code));

         return $this->getProfiles($criteria)->first();
    }

}
$account = $em->getRepository('\Entities\Account')->findOneBy(['email' => 'my@email.com']);
$profile = $account->getProfileByCode('12345');