findBy with JOIN criteria in Symfony2 - php

I have 3 simple tables: user, role, user_x_role with Many-To-Many relation. I have 2 entities: User and Role. User entity has $userRoles property with relation annotation. In Controller I need to fetch all users with specific role. But I don't know how to use JOIN in controller. Current wrong code:
$role = $this->getDoctrine()->getRepository('TestBackEndBundle:Role');
$roles = $role->findBy(array('name' => 'ROLE_PARTNER'));
$user = $this->getDoctrine()->getRepository('TestBackEndBundle:User');
$partners = $user->findBy(array('userRoles' => $roles));
It thows "Undefined index: joinColumns in ...". But I have joinColumns in User entity:
/**
* #ORM\ManyToMany(targetEntity="Role")
* #ORM\JoinTable(name="user_x_role",
* joinColumns={#ORM\JoinColumn(name="user_id", referencedColumnName="id", onDelete="CASCADE", onUpdate="CASCADE")},
* inverseJoinColumns={#ORM\JoinColumn(name="role_id", referencedColumnName="id", onDelete="CASCADE", onUpdate="CASCADE")}
* )
* #var ArrayCollection
*/
protected $userRoles;

IMO the best way for it is create your own repository for User entity. Then in that repository create method like "getUsersByRole" where you make the query you want with query builder.
$qb = $this->getEntityManager()->createQueryBuilder();
$qb->select('u')
->from('\namespace\for\User', 'u')
->join('u.roles', 'r')
->where(...)
return $qb->getQuery()->getResult();

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().

Doctrine join entities

I'm trying to make some efficient improvements when querying for an entity which has a relationship to another entity.
Entity A:
/**
* #var integer
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #var string
* #ORM\Column(name="name", type="string", length=225)
* #Assert\NotBlank(message="Please enter a name for your profile.")
*/
protected $display_name;
/**
* #ORM\OneToOne(targetEntity="Posts", mappedBy="profile", cascade={"all"}, fetch="LAZY")
*/
protected $posts;
Entity B:
/**
* #var integer
* #ORM\Column(name="profile_id", type="integer")
* #ORM\Id
*/
protected $profile_id;
/**
* #ORM\OneToOne(targetEntity="Profile", inversedBy="posts")
* #ORM\JoinColumn(name="profile_id", referencedColumnName="id")
*/
protected $profile;
/**
* #var string
* #ORM\Column(name="content", type="string")
*/
protected $content;
When I count the number of queries being executed I get two, I guess being doctrine runs two separate queries for each entity even through they have a relationship.
I am currently fetching entity A like so:
public function fetchById($id)
{
return $this->createQueryBuilder('p')
->where('p.id = :id')
->setParameter('id', $id)
->getQuery()
->getOneOrNullResult();
}
And then calling entity B like so:
$profile = $profileRepository->fetchById($user->getUserId());
$lastpost = $profile->getPosts()[0];
But I want to be able to join the second entity in this query so that I can just call one query rather than two. I was hoping to do something like this:
public function fetchById($id)
{
return $this->createQueryBuilder('p')
->select('p','pp')
->leftJoin(Posts::class, 'pp', \Doctrine\ORM\Query\Expr\Join::WITH, 'p.id = pp.profile_id')
->where('p.id = :id')
->setParameter('id', $id)
->getQuery()
->getResults();
}
However the left join returns an array of two entities. This is not what I am wanting, because I want to still be able to call for example the getPosts() method in entity A.
I essentially want to populate entity A, including all the related entities. But by only executing one query rather than two, is this possible in doctrine?
Some time ago I had a similar scenario (not with 1-1 but 1-n though), which I solved like this:
// ...Repository
public function findAllForUserWithAll(\AppBundle\Entity\User $u)
{
$query = $this->getEntityManager()->createQueryBuilder('ca1')
->add('select', 'c, s, i, m')
->add('from', 'AppBundle:Contact c')
->leftJoin('c.skills', 's')
->leftJoin('c.interests', 'i')
->leftJoin('c.metAt', 'm')
->where('c.user = :user')
->orderBy('c.lastname', 'ASC')
->setParameters([
'user' => $u,
])
->getQuery();
return $query->getResult();
}
This was in Symfony 2.8.* with Doctrine ^2.4.8 - and the result is one query with 3 joins (instead of 4 queries). Probably not the best code and honestly all the magic happened under the hood. Also your code looks alot alike. But maybe this is expected due to fetch="LAZY" (on entity A) - which my entity does not have?

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.

Search within multiple values in Doctrine

I've got a Product Entity and it can have multiple attributes like applications. Where "applications" may have mutliple values too.
So the product has an array of values for "applications", how can I save that with Doctrine so I can do a findBy('applications' => 'whatever') or similar later?
Product 1
application1
application3
Product 2
application1
application2
So a later findBy('applications' => 'application1') should find both products.
Supposing you have configured a one to many relation between your entities, like this:
Product entity:
/**
* #ORM\OneToMany(targetEntity="Aplication", mappedBy="product", cascade={"persist"})
*/
protected $aplications;
Aplication entity:
* #ORM\ManyToOne(targetEntity="Product", inversedBy="aplications")
* #ORM\JoinColumns({
* #ORM\JoinColumn(name="product_id", referencedColumnName="id")
* })
protected $product;
you can create a DQL like this on your product repository:
$qb = $this->getEntityManager()->createQueryBuilder();
$qb->select('p')
->from('bundleNamespace:Product', 'p')
->leftJoin('p.applications', 'a')
->andWhere($qb->expr()->in('a.id', ':aplication'))
->setParameter('aplication', $aplication);
return $qb->getQuery()->getResult();
Try this:
public function findProductByApplication($app) {
$query = $this->em->createQuery('SELECT DISTINCT p FROM Product p JOIN p.applications app WHERE app = :app');
$query->setParameter('app', $app);
return $query->getResult();
}

Select many to many entity collection using DQL

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.

Categories