简体   繁体   中英

Doctrine, how to join tables with many to many relation

I'm working with Symfony 2, Doctrine and Query Builder. I have 2 tables : product and category.

The product ORM file contains the following :

manyToMany:
    categories:
        targetEntity: Pim\Component\Catalog\Model\CategoryInterface
        joinTable:
            name: pim_catalog_category_product
            joinColumns:
                product_id:
                    referencedColumnName: id
                    onDelete: CASCADE
            inverseJoinColumns:
                category_id:
                    referencedColumnName: id
                    onDelete: CASCADE

The effect of this is, I have a table named "pim_catalog_category_product" that links categories wih products. I have no information about products in my category ORM file.

I'm trying to build a query with QueryBuilder and I don't understand how to start with a query on category table, that links products in order to add some filters on products.

$qb = $this->getEntityManager()
        ->createQueryBuilder()
        ->addSelect('category')
        ->from($this->_entityName, 'category')
        ->join('Pim\Component\Catalog\Model\Product', 'product', 'WITH', 'category.id IN (product.categories);

But for now I get the following error :

Expected Literal, got 'product' at ... SELECT category FROM Pim\\Bundle\\CatalogBundle\\Entity\\Category category INNER JOIN Pim\\Component\\Catalog\\Model\\Product product WITH category.id IN (product.categories) at ...

Can somebody help me ? Thank you.

When you use ManyToMany doctrine does a lot of 'magic' for you in the background regarding relationship management so you can do fast joins like without needing to take into consideration the cartesian product of the join

$this->getEntityManager()
        ->createQueryBuilder()
        ->addSelect('product')
        ->from($this->_entityName, 'p')
        ->join('p.category')

or fast flush like

$product->setCategories($categories);
$em->persist($product);
$em->flush();

The downside is that, in order for doctrine to be able to do this 'magic' you don't have access to the internal join table that you need to do the join that you want.

Also I don't think doctrine can manage this join for you if you don't define the reverse relation on 'category'. But be aware that doing this will be a big memory overhead if you have many product, because doctrine will lazy load all products on your categories.

If you don't want to define the reverse relation (because of the issue mentioned above), an workaround for this is to define an entity on your relation table (something like ProductCategory), and define 2 1->1 relations on the 2 columns of this entity. Then you can use this entity as a bridge whenever you want to create a query builder starting from Category and joining with Product, without having to define the M<->M relation on Category

Something like

$qb = $this->getEntityManager()
    ->createQueryBuilder()
    ->addSelect('c')
    ->from($this->_entityName, 'c')
    ->join('Pim\Component\Catalog\Model\ProductCategory', 'pc', 'WITH', 'c.id = pc.category_id')
    ->join('Pim\Component\Catalog\Model\Product', 'p', 'WITH', 'p.id = pc.product_id')

PS: try to use short alias like in the above example, to avoid any more "literal" exceptions.

Hope this helps,

Alexandru Cosoi

I've finally managed to solve my problem with a subquery and the correct way to join a table from a ManyToMany relation.

$qb2 = $this->getEntityManager()
            ->createQueryBuilder()
            ->addSelect('product_category.id')
            ->from('Pim\Component\Catalog\Model\Product', 'product')
            ->join('product.categories', 'product_category');

And in my main query :

$qb->andWhere($qb->expr()->in('category.id', $qb2->getDQL()));

Thank you Alexandru Cosoi and Stmm for the helpful info !

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM