web-dev-qa-db-fra.com

Comment utiliser un critère complexe dans un référentiel d'entité doctrine 2)?

Disons que j'ai une table qui contient des informations sur les festivals.
Chaque festival a une date de début et de fin.

Je veux sélectionner tous les festivals qui sont en direct (qui se produisent) à une date donnée.
.

Je suis donc passé à la classe de référentiel de l'entité festival et j'ai créé une méthode pour le faire.
. la valeur billy dans leur colonne de nom), qui utilise uniquement l'opérateur de comparaison.

Comment puis-je utiliser d'autres opérateurs tels que

>, <, !=, IN, NOT IN, LIKE    

et etc. ?

Merci

34
Doron

Vous devrez écrire votre propre requête (probablement en utilisant DQL) si vous voulez quelque chose d'aussi spécifique. Je crois que les méthodes intégrées "findBy" sont plus pour simplement saisir des objets rapidement si vous avez des critères moins spécifiques. Je ne connais pas les noms de vos entités ni où ils sont stockés. Cela pourrait être quelque chose comme ça en tant que fonction dans votre référentiel de festival.

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();
}
21
Jeremy Hicks

Doctrine 2.3 a ajouté une méthode matching () qui vous permet d'utiliser Criteria .

L'exemple de Jeremy Hicks peut être écrit comme ceci (notez que cela retourne un ArrayCollection au lieu d'un tableau).

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);
}

Personnellement, je n'utiliserais pas andWhere ici, et j'utiliserais quelques lignes supplémentaires pour améliorer la lisibilité, comme ceci:

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);
}

L'utilisation d'une clause IN est très simple.

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

La classe Criteria se trouve dans l'espace de noms de Doctrine pas vraiment ORM ou DBAL Common, comme leur ArrayCollection (qui a pris en charge les critères plus longtemps que EntityRepository).

C'est censé être un moyen découplé pour le code non référentiel de créer des critères sophicés. Il devrait donc être judicieux d'utiliser cette classe en dehors du référentiel. QueryBuilder prend également en charge les critères récemment également. Ainsi, même lors de la création de requêtes plus sophistiquées qui nécessitent QueryBuilder, vous pouvez utiliser des critères pour donner au code non-base de données une flexibilité dans ce qu'il demande.

86
Reece45

ce n'est pas la réponse à la question de Doron doctrine ont un référentiel d'entités qui ne nous oblige pas du tout à utiliser la requête ...

$this->em->getRepository($this->entity)->findBy(array $criteria);

mais qu'est-ce qu'il a demandé, c'est comment complexifier l'opérateur dans le tableau $ critère, le format normal du tableau $criteria est array('field'=> $value);

10
nuenuh

J'ai eu le même problème il y a quelque temps, où mes référentiels Doctrine sont devenus très laids en raison de requêtes complexes. J'ai également dû convertir Yii ActiveRecord (avec les critères) objets) à Doctrine, et Doctrine n'avait pas d'objets Criteria à l'époque.

J'ai trouvé un blogpost de Benjamin Eberlei qui a une solution intéressante à ce problème basée sur le modèle de spécification .

Il vous donne la possibilité de reporter la manipulation de l'objet Query Builder à d'autres classes.

$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()

De plus, vous pouvez composer 2 classes ou plus ensemble, ce qui crée de jolis blocs de construction réutilisables:

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

    return $this->em->getRepository('Advertisement')->match($spec)->execute();
}

Dans ce cas, ExpiredAds () et AdsByUser () contiennent une structure comme dans le premier exemple de code.

Si vous pensez que cette solution fonctionnerait pour vous, permettez-moi de suggérer deux bibliothèques que vous pouvez installer via composer:

9
rikbruil