Pregunta

I have an issue where I need to filter a entity based on various entity properties. Some of these properties are linking to other entities in ManyToOne or ManyToMany relationships. I am working in Symfony2 and have come to the conclusion that writing a single DQL query dynamically to filter only by the selected properties is too complex for my level of experience, so what I want to do is retrieve a result set array for every filter property and then merge them, but in such a way that only the array items that exist in BOTH array A and array B will appear in my resulting array. I have been trying to find something that would easily allow me to do this, but I have been unable to find anything.

If any Symfony2 developers look at this thread, I realize that doing it in the way mentioned above is not ideal, so if you could help me create a DQL query that can do this I would be most grateful. Below is my entity that I need to filter results for. The properties that I need to filter by are "categories", "authors", "genre" and "language". Assume that I extract from the REQUEST an array that looks similar to array('category' => 1, 'genre' => 6, 'author' => 8, 'language' => 2); of which at least one must be present, but not all of them have to be present.

<?php

namespace Pmb\LicensingBundle\Entity;

use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\Criteria;
use Doctrine\Common\Collections\ArrayCollection;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
use Symfony\Component\Validator\Constraints as Assert;
use JMS\Serializer\Annotation as Ser;
use Gedmo\Mapping\Annotation as Gedmo;

/**
 * Ebook
 *
 * @ORM\Entity
 * @ORM\Table(name="ebooks")
 * @Ser\ExclusionPolicy("all")
 */
class Ebook
{
    /**
     * @var integer
     *
     * @ORM\Column(name="id", type="integer")
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="AUTO")
     * @Ser\Expose
     */
    private $id;

    /**
     * @var Category[]
      * 
      * @ORM\ManyToMany(targetEntity="Pmb\LicensingBundle\Entity\Category", inversedBy="ebooks", cascade={"remove"})
      * @ORM\OrderBy({"name" = "ASC"})
     **/
    private $categories;

    /**
     * @var Author[]
      * 
      * @ORM\ManyToMany(targetEntity="Pmb\LicensingBundle\Entity\Author", inversedBy="ebooks", cascade={"remove"})
      * @ORM\OrderBy({"lastname" = "ASC", "firstnames" = "ASC"})
      * @Ser\Expose
     **/
    private $authors;

    /**
     * @var Curriculum
      * 
      * @ORM\ManyToOne(targetEntity="Pmb\LicensingBundle\Entity\Curriculum", inversedBy="ebooks", cascade={"remove"})
      * @ORM\OrderBy({"name" = "ASC"})
      * @Ser\Expose
     **/
    private $curriculum;

    /**
     * @var Genre
      * 
      * @ORM\ManyToOne(targetEntity="Pmb\LicensingBundle\Entity\Genre", inversedBy="ebooks", cascade={"remove"})
      * @ORM\OrderBy({"description" = "ASC"})
      * @Ser\Expose
     **/
    private $genre;

    /**
     * @var Language
      * 
      * @ORM\ManyToOne(targetEntity="Pmb\LicensingBundle\Entity\Language", inversedBy="ebooks", cascade={"remove"})
      * @Ser\Expose
     **/
    private $language;

    /**
     * @var string
     *
     * @ORM\Column(name="isbn", type="string", length=16, unique=true)
     * @Ser\Expose
     * @Assert\Length(min=4, max=16)
     */
    private $isbn;

    /**
     * @var string
     *
     * @ORM\Column(name="stock_code", type="string", length=16)
     * @Ser\Expose
     * @Assert\Length(min=4, max=16)
     */
    private $stockCode;

    /**
     * @var string
     *
     * @ORM\Column(name="grade", type="integer", nullable=true)
     * @Ser\Expose
     * @Assert\Type({"int"})
     */
    private $grade;

    /**
     * @var string
     *
     * @ORM\Column(name="price", type="float")
     * @Ser\Expose
     * @Assert\Type({"float"})
     */
    private $price;

    /**
     * @var string
     *
     * @ORM\Column(name="name", type="string", length=128)
     * @Ser\Expose
     * @Assert\Length(min=3, max=128)
     */
    private $name;

    /**
     * @var string
     *
     * @ORM\Column(name="description", type="string", length=255, nullable=true)
     * @Ser\Expose
     * @Assert\Length(max=255)
     */
    private $description;

    /**
     * @var \DateTime
     *
     * @ORM\Column(name="year_of_pub", type="datetime", nullable=true)
     * @Ser\Expose
     * @Assert\DateTime
     */
    private $yearOfPub;

    /**
     * @var string
     *
     * @ORM\Column(name="ebook_file", type="string", length=256, nullable=true)
     * @Ser\Expose
     */
    private $ebookFile;

    /**
     * @var string
     *
     * @ORM\Column(name="thumbnail", type="string", length=256, nullable=true)
     * @Ser\Expose
     */
    private $thumbnail;

    /**
     * @var \DateTime
     *
     * @ORM\Column(name="date_created", type="datetime")
     * @Ser\Expose
     * @Assert\DateTime
     */
    private $dateCreated;

    /**
     * @var boolean
     *
     * @ORM\Column(name="active", type="boolean")
     * @Ser\Expose
     */
    private $active;

    /**
     * @var boolean
     *
     * @ORM\Column(name="allow_trial", type="boolean")
     * @Ser\Expose
     */
    private $allowTrial;

    /**
     * @var boolean
     *
     * @ORM\Column(name="featured", type="boolean")
     * @Ser\Expose
     */
    private $featured;

    //////////////////////////////////////////////////////////////////////////////////////
    // METHODS
    //////////////////////////////////////////////////////////////////////////////////////

    //////////////////////////////////////////////////////////////////////////////////////
    // GETTERS AND SETTERS
    //////////////////////////////////////////////////////////////////////////////////////

    /**
     * Constructor
     */
    public function __construct()
    {
        $this->categories = new \Doctrine\Common\Collections\ArrayCollection();
        $this->authors = new \Doctrine\Common\Collections\ArrayCollection();
        $this->curriculums = new \Doctrine\Common\Collections\ArrayCollection();
        $this->genres = new \Doctrine\Common\Collections\ArrayCollection();
        $this->active = true;
    }

    /**
     * Get id
     *
     * @return integer 
     */
    public function getId()
    {
        return $this->id;
    }

    /**
     * Set isbn
     *
     * @param string $isbn
     * @return Ebook
     */
    public function setIsbn($isbn)
    {
        $this->isbn = $isbn;

        return $this;
    }

    /**
     * Get isbn
     *
     * @return string 
     */
    public function getIsbn()
    {
        return $this->isbn;
    }

    /**
     * Set stockCode
     *
     * @param string $stockCode
     * @return Ebook
     */
    public function setStockCode($stockCode)
    {
        $this->stockCode = $stockCode;

        return $this;
    }

    /**
     * Get stockCode
     *
     * @return string 
     */
    public function getStockCode()
    {
        return $this->stockCode;
    }

    /**
     * Set grade
     *
     * @param integer $grade
     * @return Ebook
     */
    public function setGrade($grade)
    {
        $this->grade = $grade;

        return $this;
    }

    /**
     * Get grade
     *
     * @return integer 
     */
    public function getGrade()
    {
        return $this->grade;
    }

    /**
     * Set price
     *
     * @param float $price
     * @return Ebook
     */
    public function setPrice($price)
    {
        $this->price = $price;

        return $this;
    }

    /**
     * Get price
     *
     * @return float 
     */
    public function getPrice()
    {
        return $this->price;
    }

    /**
     * Set name
     *
     * @param string $name
     * @return Ebook
     */
    public function setName($name)
    {
        $this->name = $name;

        return $this;
    }

    /**
     * Get name
     *
     * @return string 
     */
    public function getName()
    {
        return $this->name;
    }

    /**
     * Set description
     *
     * @param string $description
     * @return Ebook
     */
    public function setDescription($description)
    {
        $this->description = $description;

        return $this;
    }

    /**
     * Get description
     *
     * @return string 
     */
    public function getDescription()
    {
        return $this->description;
    }

    /**
     * Set yearOfPub
     *
     * @param \DateTime $yearOfPub
     * @return Ebook
     */
    public function setYearOfPub($yearOfPub)
    {
        $this->yearOfPub = $yearOfPub;

        return $this;
    }

    /**
     * Get yearOfPub
     *
     * @return \DateTime 
     */
    public function getYearOfPub()
    {
        return $this->yearOfPub;
    }

    /**
     * Set active
     *
     * @param boolean $active
     * @return Ebook
     */
    public function setActive($active)
    {
        $this->active = $active;

        return $this;
    }

    /**
     * Get active
     *
     * @return boolean 
     */
    public function getActive()
    {
        return $this->active;
    }

    /**
     * Add categories
     *
     * @param \Pmb\LicensingBundle\Entity\Category $categories
     * @return Ebook
     */
    public function addCategory(\Pmb\LicensingBundle\Entity\Category $categories)
    {
        $this->categories[] = $categories;

        return $this;
    }

    /**
     * Remove categories
     *
     * @param \Pmb\LicensingBundle\Entity\Category $categories
     */
    public function removeCategory(\Pmb\LicensingBundle\Entity\Category $categories)
    {
        $this->categories->removeElement($categories);
    }

    /**
     * Get categories
     *
     * @return \Doctrine\Common\Collections\Collection 
     */
    public function getCategories()
    {
        return $this->categories;
    }

    /**
     * Add authors
     *
     * @param \Pmb\LicensingBundle\Entity\Author $authors
     * @return Ebook
     */
    public function addAuthor(\Pmb\LicensingBundle\Entity\Author $authors)
    {
        $this->authors[] = $authors;

        return $this;
    }

    /**
     * Remove authors
     *
     * @param \Pmb\LicensingBundle\Entity\Author $authors
     */
    public function removeAuthor(\Pmb\LicensingBundle\Entity\Author $authors)
    {
        $this->authors->removeElement($authors);
    }

    /**
     * Get authors
     *
     * @return \Doctrine\Common\Collections\Collection 
     */
    public function getAuthors()
    {
        return $this->authors;
    }

    /**
     * Set language
     *
     * @param \Pmb\LicensingBundle\Entity\Language $language
     * @return Ebook
     */
    public function setLanguage(\Pmb\LicensingBundle\Entity\Language $language = null)
    {
        $this->language = $language;

        return $this;
    }

    /**
     * Get language
     *
     * @return \Pmb\LicensingBundle\Entity\Language 
     */
    public function getLanguage()
    {
        return $this->language;
    }

    /**
     * Set allowTrial
     *
     * @param boolean $allowTrial
     * @return Ebook
     */
    public function setAllowTrial($allowTrial)
    {
        $this->allowTrial = $allowTrial;

        return $this;
    }

    /**
     * Get allowTrial
     *
     * @return boolean 
     */
    public function getAllowTrial()
    {
        return $this->allowTrial;
    }

    /**
     * Set dateCreated
     *
     * @param \DateTime $dateCreated
     * @return Ebook
     */
    public function setDateCreated($dateCreated)
    {
        $this->dateCreated = $dateCreated;

        return $this;
    }

    /**
     * Get dateCreated
     *
     * @return \DateTime 
     */
    public function getDateCreated()
    {
        return $this->dateCreated;
    }

    /**
     * Set featured
     *
     * @param boolean $featured
     * @return Ebook
     */
    public function setFeatured($featured)
    {
        $this->featured = $featured;

        return $this;
    }

    /**
     * Get featured
     *
     * @return boolean 
     */
    public function getFeatured()
    {
        return $this->featured;
    }

    /**
     * Add categories
     *
     * @param \Pmb\LicensingBundle\Entity\Category $categories
     * @return Ebook
     */
    public function addCategorie(\Pmb\LicensingBundle\Entity\Category $categories)
    {
        $this->categories[] = $categories;

        return $this;
    }

    /**
     * Remove categories
     *
     * @param \Pmb\LicensingBundle\Entity\Category $categories
     */
    public function removeCategorie(\Pmb\LicensingBundle\Entity\Category $categories)
    {
        $this->categories->removeElement($categories);
    }

    /**
     * Set curriculum
     *
     * @param \Pmb\LicensingBundle\Entity\Curriculum $curriculum
     * @return Ebook
     */
    public function setCurriculum(\Pmb\LicensingBundle\Entity\Curriculum $curriculum = null)
    {
        $this->curriculum = $curriculum;

        return $this;
    }

    /**
     * Get curriculum
     *
     * @return \Pmb\LicensingBundle\Entity\Curriculum 
     */
    public function getCurriculum()
    {
        return $this->curriculum;
    }

    /**
     * Set genre
     *
     * @param \Pmb\LicensingBundle\Entity\Genre $genre
     * @return Ebook
     */
    public function setGenre(\Pmb\LicensingBundle\Entity\Genre $genre = null)
    {
        $this->genre = $genre;

        return $this;
    }

    /**
     * Get genre
     *
     * @return \Pmb\LicensingBundle\Entity\Genre 
     */
    public function getGenre()
    {
        return $this->genre;
    }

    /**
     * Set ebookFile
     *
     * @param string $ebookFile
     * @return Ebook
     */
    public function setEbookFile($ebookFile)
    {
        $this->ebookFile = $ebookFile;

        return $this;
    }

    /**
     * Get ebookFile
     *
     * @return string 
     */
    public function getEbookFile()
    {
        return $this->ebookFile;
    }

    /**
     * Set thumbnail
     *
     * @param string $thumbnail
     * @return Ebook
     */
    public function setThumbnail($thumbnail)
    {
        $this->thumbnail = $thumbnail;

        return $this;
    }

    /**
     * Get thumbnail
     *
     * @return string 
     */
    public function getThumbnail()
    {
        return $this->thumbnail;
    }
}
¿Fue útil?

Solución

Here is a quick example on how to achieve that. Assuming you have a BookRepository and also a BookFilterType which defines what kind of filters the user can use:

class BookRepository extends EntityRepository
{
    protected $queryBuilder;

    public function filterByGenre($genre)
    {
        return $this->getQueryBuilder()
            ->andWhere("b.genre = :genre")
            ->setParameter('genre', $genre);
    }

    public function filterByWhatever($whatever)
    {
        return $this->getQueryBuilder()
            ->andWhere("b.whatever = :whatever")
            ->setParameter('whatever', $whatever);
    }

    public function getFilterResult()
    {
        $result = $this->getQueryBuilder()
            ->getQuery->getResult();

        $this->queryBuilder = null;

        return $result;
    }


    public function getQueryBuilder()
    {
        if(!$this->queryBuilder)
            $this->queryBuilder = $this->createQueryBuilder("b");

        return $this->queryBuilder;
    }
}

In your controller:

public function indexAction(Request $request)
{
    $form = $this->createForm(new BookFilterType());

    if($request->isMethod("POST"))
    {
        $form->submit($request);

        if($form->isValid())
        {
            $filters = $form->getData();

            // pass filters to the repository
            $books = $bookRepository
                ->filterByGenre($filters['genre'])
                ->filterByWhatever($filters['whatever'])
                ->getFilterResult();

            return ['books' => $books]; // render filtered books in template
        }
    }
}

You can also use a GET form (to pass filters over the URL); that does not matter because it's handled under the hood by the bind method.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top