Select many to many entity collection using DQL - php

I have to entities that have ManyToMany relation with linking table. Like this:
class User
{
/**
* #ORM\ManyToMany(targetEntity="Post")
* #ORM\JoinTable(name="favorite_posts",
* joinColumns={#ORM\JoinColumn(name="user_id", referencedColumnName="id")},
* inverseJoinColumns={#ORM\JoinColumn(name="post_id", referencedColumnName="id")}
* )
**/
private $favoritePosts;
}
class Post
{
/**
* #ORM\ManyToMany(targetEntity="User", mappedBy="favoritePosts")
*/
private $usersInFavorite;
}
And I can get all user's favorite posts using a User entity object:
$favorites = $user->getFavoritesPosts();
But I have no idea how to get EXACTLY THE SAME result using DQL or Doctrine Query Builder. Under result i mean an array of POST entity objects.

Based on this exemple
If you want to fetch it by dql,
$dql = "SELECT p FROM Posts p INNER JOIN p.$usersInFavorite u WHERE u= ?1";
$query = $entityManager->createQuery($dql)
->setParameter(1, $user);
$favoritePosts = $query->getResult();
I tested it this time and i found the results as requested.
if you have the id of the user entity instead of the entity the same code will work with $user being the id of the user.

Related

How to select a group of entities according to a condition in a related OneToMany association?

I'm using Symfony4 and I have two entities User and Car.
A Car has a field isRent which is of type bool and further a database column user_id for the relation.
class User
{
/**
* #var Collection
*
* #ORM\OneToMany(targetEntity="Car", mappedBy="user")
*
* #Serializer\Type("ArrayCollection<ListRestAPI\Entity\Cars>")
* #Serializer\Expose()
*/
private $cars;
class Car
{
/**
* #var User
*
* #ORM\ManyToOne(targetEntity="User", inversedBy="cars")
*/
private $user;
Now I'm trying to fetch all users who have cars rented.
My current query fetches all the users regardless of whether they have cars rented or not.
$query = $this->createQueryBuilder('u')
->select()
->join('u.cars', 'c')
->andWhere(sprintf('%c.is_rent = :isRent', 'c'))
->setParameter('isRent', '1')
->groupBy('u.id')
->addOrderBy('u.id');
How can I fetch only User entities that have cars rented?
Try removing your andWhere() by this:
->having('c.isRent = 1')
Since you are using an integer in your query, no need to use bound parameters. You should also remove setParameter().

Symfony subquery for entity

Have a problem with subquery with symfony.
What I try to do - I have a table with users and a table with posts.
Posts Users
id|author|content id|username
I want create subquery to get user name by id.
/**
* #return array
*/
public function findAll()
{
return $this->getEntityManager()->createQuery(
'SELECT a, (SELECT u.username
FROM BackendBundle:User u WHERE u.id = a.author) as authorName
FROM BackendBundle:Article a'
)->getResult();
}
Result:
What am I doing wrong? What is the best way to join column from other table by id? Maybe i can use annotations?
Thx for any help.
You don't need a subquery here, what you need is a simple (INNER) JOIN to join Users with their Articles.
$em->createQuery("SELECT a FROM Article JOIN a.author'");
You don't even need an on clause in your join, because Doctrine should already know (through annotations on your entities or a separate yaml file), that the article.author field relates to user.id.
Edit:
I assume you have a User entity that is One-To-Many related to the Article entity.
class User
{
// ...
/**
* #OneToMany(targetEntity="Article", mappedBy="author")
*/
private $articles;
// ...
}
class Article
{
// ...
/**
* #var User
* #ManyToOne(targetEntity="User", inversedBy="articles")
*/
private $author;
// ...
}
Please refer to doctrines association mapping documentation: http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/association-mapping.html

How does inner join work on a many-to-many relationship using Doctrine and Symfony2

I recently worked out an issue with querying ManyToMany relationship join tables, the solution was same as this answer and was wondering how it works.
lets say i have a simple ManyToMany relationship between groups and team, there will be a groups_team tables that will automatically be created here
groups entity
/**
* Groups
*
* #ORM\Table(name="groups")
* #ORM\Entity(repositoryClass="AppBundle\Model\Repository\GroupsRepository")
*/
class Groups {
/**
* #ORM\ManyToMany(targetEntity="Team", inversedBy="group")
*/
protected $team;
public function __construct() {
$this->team = new ArrayCollection();
}
/**
* #var int
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="groupname", type="string", length=255)
*/
private $groupname;
//obligatory getters and setters :)
team entity
/**
* Team
*
* #ORM\Table(name="team")
* #ORM\Entity(repositoryClass="AppBundle\Model\Repository\TeamRepository")
*/
class Team {
/**
* #ORM\ManyToMany(targetEntity="Groups", mappedBy="team")
*/
protected $group;
public function __construct(){
$this->group = new ArrayCollection();
}
/**
* #var int
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="teamname", type="string", length=255)
*/
private $team;
//[setters and getters here]
in order to get all the teams in a group i would have to query the groups_team table.i would have directly queried the table in just mysql but in symfony i have to do this
$groups = $em->getRepository("AppBundle\Model\Entity\Groups")->findBy(array('tournament' => $tournament->getId()));
//get all teams with group id in groups_team table
foreach ($groups as $group) {
$teamsingroup = $em->getRepository("AppBundle\Model\Entity\Team")->createQueryBuilder('o')
->innerJoin('o.group', 't')
->where('t.id = :group_id')
->setParameter('group_id', $group->getId())
->getQuery()->getResult();
echo "</b>".$group->getGroupname()."</b></br>";
foreach ($teamsingroup as $teamingroup) {
echo $teamingroup->getTeam()."</br>";
}
}
Can someone explain to me how the innerJoin is working and what is the concept behind this, maybe a few documentation to learn about this. are there better way to do this with symfony and doctrine.
Using ManyToMany between 2 entities involves a third table generally called as a junction table in this type of relation when you build a DQL (doctrine query) doctrine automatically joins junction table depending on the nature of relation you have defined as annotation so considering your query
$teamsingroup = $em->getRepository("AppBundle\Model\Entity\Team")
->createQueryBuilder('o')
->innerJoin('o.group', 't')
You are joining Team entity with Group entity in innerJoin('o.group') part o is the alias for Team entity and o.group refers to property defined in Team entity named as group.
/**
* #ORM\ManyToMany(targetEntity="Groups", mappedBy="team")
*/
protected $group;
Which has a ManyToMany annotation defined for this type of relation doctrine joins your team table first with junction table and then joins your junction table with groups table and the resultant SQL will be something like
SELECT t.*
FROM teams t
INNER JOIN junction_table jt ON(t.id = jt.team_id)
INNER JOIN groups g ON(g.id = jt.group_id)
WHERE g.id = #group_id
Another thing related your way of getting team for each group you can minimize your code by excluding createQueryBuilder part within loop, once you have defined teams property as ArrayCollection i.e $this->team = new ArrayCollection(); on each group object you will get collections of teams associated to that particular group by calling getTeam() function on group object similar to below code.
foreach ($groups as $group) {
$teamsingroup = $group->getTeam();
echo "</b>".$group->getGroupname()."</b></br>";
foreach ($teamsingroup as $teamingroup) {
echo $teamingroup->getTeam()."</br>";
}
}
I guess it's literally select statement with INNER JOIN using key columns defined entity class as mappedBy or inversedBy.
Why don't you have a look of doctrine log and see what the native sql is composed?
How to get Doctrine to log queries in Symfony2 (stackoverflow)
http://vvv.tobiassjosten.net/symfony/logging-doctrine-queries-in-symfony2/ (some code examples)
I don't know your user story behind this, but I also heard that it is recommended to use one to many relationship instead of many to many, unless there is a strong reason to do so, as most of cases can be handled by one to many by reconsidering models.

Doctrine2 QB – how to select entities where OnetoOne entity doesn't exists

I have two entities:
class User extends BaseEntity {
/**
* #ORM\OneToOne(targetEntity="Profile", mappedBy="user", cascade={"persist", "remove"})
* #var Profile
*/
protected $profile;
}
class Profile extends BaseEntity {
/**
* #ORM\OneToOne(targetEntity="User", inversedBy="profile", cascade={"persist"})
* #ORM\JoinColumn(name="user_id", referencedColumnName="id", onDelete="CASCADE")
* #var User
*/
protected $user;
}
What I'm trying to do is to SELECT only users who don't have any profile (so there's no profile row with user_id=:user_id in profiles table). However, I have no idea how to make my QueryBuilder.
First, I tried something simple like
//u as user
$query = $this->er->createQueryBuilder('u');
$query
->join('u.profile', 'p')
->where('u.profile = 1');
But that returns A single-valued association path expression to an inverse side is not supported in DQL queries. Use an explicit join instead. so I suppose there's something wrong with my relationship? I tried to switch join() for leftJoin() but it didn't help either...
So what's up with this error and how to make proper condition with where() to tell Doctrine I want only users where there's no profile?
Here is how I was able to do it. Basically I had Domain & Hosting entities and I wanted to select Domains that had no Hosting attached to them in a one to one relationship.
$er->createQueryBuilder('d')
->leftJoin('d.hosting', 'h')
->where('h.id is NULL');
Credits for this solution goes to this Answer
Hope it works for you since our situations are almost identical.
Just check for null as value of profile
//u as user
$query = $this->er->createQueryBuilder('u');
$query
->where('u.profile is NULL');
Try to use repositories, example for UserRepository:
$qb = $this->createQueryBuilder('u');
$e = $qb->expr();
$qb->leftJoin('u.profile', 'p')
->where($e->isNull('p.id'))
Raw query will look like this:
SELECT * FROM users AS u LEFT JOIN u.profile AS p WHERE p.id IS NULL;

load related entiites from doctrine collection symfony2

I have category table and make table. Both tables related by third category_make table creating many to many relationship.
/**
* #var \Doctrine\Common\Collections\Collection
*
* #ORM\ManyToMany(targetEntity="Ladisi\MotorsBundle\Entity\Make", inversedBy="catogory", fetch="EAGER")
* #ORM\JoinTable(name="catogory_make",
* joinColumns={
* #ORM\JoinColumn(name="catogory_id", referencedColumnName="cat_id")
* },
* inverseJoinColumns={
* #ORM\JoinColumn(name="make_id", referencedColumnName="make_id")
* }
* )
*/
private $make;
I want to get makes that belongs to particular category. I have tried,
$query = $em
->createQuery(
'SELECT c, m FROM LadisiMotorsBundle:Catagory c
JOIN c.make m
WHERE c.catId= :id'
)->setParameter('id', $id);
$result = $query->getResult();
but every time I get only category fields, make entity is not available in result. I also tried to get makes just by calling getMakes method on catagory object, it also returns null (not entity, i guess proxy). How do i solve this. Any help would be great.
You've already created a linking table, so the only thing you have to do (if you have your entities configured correctly) in your controller:
$catagory = $this->getDoctrine()->getManager()->getRepository('LadisiMotorsBundle:Catagory')->findOneBy(['id' => $id]);
$catagory->getMakes();

Categories