Wygodne filtrowanie kolekcji z Doctrine criteria

Problem

Mamy encję Category i przypisane do niej obiekty klasy Product. Standardowe połączenie jeden do wielu. Do jednej kategorii mamy przypisane wiele produktów, ale każdy produkt ma przypisaną tylko jedną kategorię. Teraz, gdy chcemy wykonać jakąś operację na kolekcji produktów przypisanych do danej kategorii, na przykład pobrać tylko część z nic na potrzeby paginacji, czy przefiltrować po jakiejś właściwości jak cena, standardowo musimy pobrać wszystkie produkty należące do danej kategorii. Następnie możemy wykonać na operacje na kolekcji przechowywanej w pamięci. Jednak nie jest to zbyt optymalne rozwiązanie. Produktów przypisanych do danej kategorii mogą być setki czy tysiące. Pobieranie więc ich wszystkich, kiedy tak naprawdę potrzebujemy tylko kilku spełniających dany warunek, nie wydaje się być dobrym pomysłem

Filtrowanie za pomocą mechanizmu 'Criteria’

Na szczęście Dcotrine posiada mechanizm, który pozwala nam uniknąć tego problemu. Możemy tworzyć kryteria filtrowania i wyszukiwania na kolekcję, zanim ta zostanie pobrana z bazy danych. Po pierwsze musimy utworzyć obiekt 'criteria’:

class Category {

    public function getPremiumProducts {
        $criteria = Criteria::create();
    }
    
}

Następnie możemy dodać do tego obiektu wymagane przez nas warunki w bardzo podobny sposób jak przy użyciu Query buildera:

class Category {

    public function getPremiumProducts {
        $criteria = Criteria::create();

        $criteria->andWhere(Criteria::expr()->eq('isPremium', true));
    }

}

W tym przypadku chcemy wybrać te produkty, które pole $isPremium będą miały ustawione na wartość true. Za pomocą funkcji 'Criteria::expr()’ mamy dostęp do różnych warunków które możemy nałożyć na naszą kolekcję – https://www.doctrine-project.org/projects/doctrine-collections/en/1.6/expression-builder.html#expression-builder. Na koniec aplikujemy nasze kryteria na kolekcję z klasy Category za pomocą metody 'matching’ dostępnej w kolekcji Doctrine:

class Category {

    public function getPremiumProducts {
        $criteria = Criteria::create();

        $criteria->andWhere(Criteria::expr()->eq('isPremium', true));

        return $this->products->matching($criteria);
    }

}

Wykorzystanie Criteria w QueryBuilder

Tak utworzone kryteria możemy również aplikować w obiektach QueryBuilder:

class ProductRepository extends ServiceEntityRepository
{
    public function findAllPremium
    {
        $criteria = Criteria::create();
        $criteria->andWhere(Criteria::expr()->eq('isPremium', true));

        return $this->createQueryBuilder('product')
            ->addCriteria($criteria)
            ->getQuery()
            ->getResult();
    }
}

Jeżeli dodatkowe wyekstrahujemy nasz obiekt Criteria do zewnętrznie dostępnej funkcji, dostajemy ładny re-używalny kod, którego możemy użyć w obu miejscach bez powtarzania się.

Subscribe
Powiadom o
guest
0 komentarzy
Inline Feedbacks
View all comments
0
Would love your thoughts, please comment.x