0

I have those 2 entities in my symfony project : house and software.

Many Home can have many software and Many software can belongs to many home

I'm trying to get only the Homes that have , let's say the software n° 1 + software n°2.

Actually I've managed to retrieve the Homes that have software n°1 and those that have software n°2 but not both those that have soft 1 + soft2

If I'm not wrong, It should be a Inner join join, right ?

Here's my entities and repository's method :

    class Software {
          /**
           * @ORM\ManyToMany(targetEntity="App\Entity\Home", mappedBy="softwares")
           */
           private $homes;

           public function __constuct() {
               $this->homes = new ArrayCollection();
           }

           // ...

           public function getHomes(){ ... }
           public function addHome(Home $home){ ... }
           // ...

    }


    class Home {
          /**
           * @ORM\ManyToMany(targetEntity="App\Entity\Software", inversedBy="homes")
           */
           private $softwares;

           public function __constuct() {
               $this->softwares = new ArrayCollection();
           }

           //...

           public function getSoftwares(){ ... }
           public function addSoftware(Software $software){ ... }
           //...

    }

Home repository

    class HomeRepository extends ServiceEntityRepository {
          public function innerJoinSoftware($softIds)
          {
               $qb = $this->createQueryBuilder('c')
                   ->innerJoin('c.softwares', 's')
                   ->andWhere('s.id IN(:softIds)')
                     ->setParameter('softIds', $softIds)
              ;
              return $qb->getQuery()->getResult();
         }
    }

To illustrate my point :

  • Home1 has soft1, soft2

  • Home2 has soft1, soft3

  • Home3 has soft2, soft3

What I wanna do is something like

  dump(homeRepo->innerJoinSoftware([1, 2]));
  //should output Home1 but actually I have
  //it outputs Home1, Home2, Home3

Here's the SQL version I came out with, but I'm still not able to do it with Doctrine

  SELECT home.id, home.name FROM Home as home
  INNER JOIN (
        SELECT home_id as home_id, COUNT(home_id) as count_home
        FROM home_software
        WHERE software_id IN (1, 2)
        GROUP BY home_id
        HAVING count_home = 2) as soft # count_home should be dynamic 
  ON home.id = soft.home_id
  ORDER BY home.name
Monolith
  • 1,067
  • 1
  • 13
  • 29
skytorner
  • 405
  • 2
  • 8
  • 19
  • Have a look at [this answer](https://stackoverflow.com/a/18584028/546262). – ehymel Sep 06 '18 at 22:55
  • I've took a look in the link you've provided ! It's really interesting ! I've discovered a lot ! But it doesn't solve my problem : I don't want to retrieve the software but the houses whose software is in the list passed in parameter – skytorner Sep 07 '18 at 14:00

1 Answers1

0

Here's how I solve this problem (helped by the raw SQL I've posted)

public function findBySoftwaresIn($softIds)
{
    //retrieve nbr of soft per home
    $nbrSoftToFind = count($softIds);
    $qb = $this->createQueryBuilder('h');

    $qb->innerJoin('h.softwares', 's')
        ->andWhere('h.id IN (:softIds)')
            ->setParameter('softIds', $softIds)

        //looking for home coming back by nbrSoft
        ->andHaving($qb->expr()->eq($qb->expr()->count('h.id'), $nbrSoftToFind))
        ->groupBy('h.id')//dont forget to group by ID
        ->addOrderBy('h.name')
    ;

    return $qb->getQuery()->getResult();
}
skytorner
  • 405
  • 2
  • 8
  • 19