How to query doctrine for dependent entities in Symfony2 controller - php

I don't find an example here in SO, so i post my question:
I have a group entity, a shop entity, and a transaction entity.
A group has many shops, and a shop can belong to many groups. In Group.php:
/**
* #ORM\ManyToMany(targetEntity="Shop", inversedBy="groups")
* #ORM\JoinTable(name="group_shop",
* joinColumns={#ORM\JoinColumn(name="group_id", referencedColumnName="id")},
* inverseJoinColumns={#ORM\JoinColumn(name="shop_id", referencedColumnName="id")}
* )
**/
private $shops;
And in Shop.php
/**
* #ORM\ManyToMany(targetEntity="Group", mappedBy="shops")
*/
private $groups;
Then, a shop makes transactions. In Transaction.php:
/**
* #ORM\ManyToOne(targetEntity="Shop", inversedBy="transactions")
* #ORM\JoinColumn(name="shop_id", referencedColumnName="id")
* */
private $shop;
And in Shop.php:
/**
* #ORM\OneToMany(targetEntity="Transaction", mappedBy="shop")
**/
private $transactions;
What I want to query is all transactions from a group. This must be very simple, buy i'm blinded.
What I have:
$query4 = $em->createQuery("SELECT t FROM MGFAppBundle:Transaction t
WHERE t.date > :from AND t.date < :to AND t.shop IN (/* HERE I'M STUCK */)")- >setParameters(array(
'from' => $from
'to' => $to
));
I don't know if this is the correct approach or... well, dql is kinda hard to get for me.
How do I write this dql query?
Thanks in advance.

In your repository do something like this:
public function findTransactionsByGroup(GroupInterface $group)
{
return $this->createQueryBuilder('g')
->select('s.transactions')
->leftJoin('g.shops','s')
->where('s.group = :groupid')
->setParameter('groupid', $group->getId())
->getQuery()
->getResult();
}

Related

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?

Doctrine Query Builder - Search in collection

I'm trying to create fulltext search in Doctrine Query Builder. But I have here one problem.
In Post Entity I have this:
/**
* #ORM\Column(type="string")
*/
protected $title;
/**
* #ORM\ManyToMany(targetEntity="Tag", inversedBy="posts")
* #ORM\JoinTable(
* name="posts_tags",
* joinColumns={
* #ORM\JoinColumn(name="post_id", referencedColumnName="id")
* },
* inverseJoinColumns={
* #ORM\JoinColumn(name="tag_id", referencedColumnName="id")
* }
* )
*/
protected $tags;
And I'm searching in post with this code:
$searched = $this->em->createQueryBuilder();
$searched->select('p')
->from(Post::getClassName(), 'p');
$searched->where(
$searched->expr()->like('p.title', $searched->expr()->literal('%' . $values->search . '%'))
);
But now I don't know how to search in "tags" because it's collection. Can somebody put me on right journey? Thank you very much.
Try an inner join:
$qb = $em->createQueryBuilder();
$posts = $qb->select('p')
->from(Post::getClassName(), 'p')
->innerJoin('p.tags', 't')
->where($qb->expr()->like('p.title', ':postTitle'))
->andWhere('t.name = :tagName')
->setParameter("postTitle", "%{$values->search}%")
->setParameter("tagName", "foobar")
->getQuery()->getResult();
Some variable names are made up, as I don't know your exact schema, but this should get you going.

Doctrine many to many select

I have two entities, Group and User:
class Group
{
/**
* #ORM\ManyToMany(targetEntity="Group", inversedBy="groups")
* #ORM\JoinTable(name="admin_group_user",
* joinColumns={#ORM\JoinColumn(name="fk_group", referencedColumnName="id")},
* inverseJoinColumns={#ORM\JoinColumn(name="fk_user", referencedColumnName="id")}
* )
*/
protected $users;
...
}
class User
{
/**
* #ORM\ManyToMany(targetEntity="Group", inversedBy="users")
* #ORM\JoinTable(name="admin_group_user",
* joinColumns={#ORM\JoinColumn(name="fk_user", referencedColumnName="id")},
* inverseJoinColumns={#ORM\JoinColumn(name="fk_group", referencedColumnName="id")}
* )
*/
protected $groups;
...
}
I would like to get result like
Group 1 has user A, user B, user C
Group 2 has user D, user E, user F.
Generally something like
SELECT admin_group.id AS group_id, admin_group.name, agu.fk_user, fu.username
FROM admin_group
JOIN admin_group_user agu ON (admin_group.id = agu.fk_group)
JOIN front_user fu ON (agu.fk_user = fu.id);
Does anyone know how to achieve this with Doctrine?
Following documentation about many-to-many bidirectional mapping on doctrine helps you solve your problem:
http://docs.doctrine-project.org/en/latest/reference/association-mapping.html#many-to-many-bidirectional

How to prevent redundant lookups when joining entity relationships?

I'm getting up to speed with Symfony 2 and Doctrine and having difficulties with the number of unnecessary database lookups being performed to hydrate with joined entities.
After performing a joined query with a child object, the child is automatically pulling its other mappings from the database. It's doing this despite that I'm not attempting to access any of its properties. It's as if they're being accessed inside the find query.
My example looks like the below - There are entities called Person and Building that both join an entity called Place:
class Person {
/**
* Where this person lives
* #var Place $Home
*
* #ORM\OneToOne(targetEntity="Place", cascade={"persist"}, inversedBy="Resident" )
* #ORM\JoinColumn(name="place_id", referencedColumnName="id")
*/
private $Home;
}
class Building {
/**
* Where this building stands
* #var Place $Site
*
* #ORM\OneToOne(targetEntity="Place", cascade={"persist"}, inversedBy="Landmark" )
* #ORM\JoinColumn(name="place_id", referencedColumnName="id")
*/
private $Site;
}
class Place {
/**
* Reverse mapping
* #var Person $Resident
*
* #ORM\OneToOne(targetEntity="Person", mappedBy="Home")
*/
private $Resident;
/**
* Reverse mapping
* #var Building $Landmark
*
* #ORM\OneToOne(targetEntity="Building", mappedBy="Site")
*/
private $Landmark;
}
My Person repository join looks like this:
/**
* #override
* #return Person
*/
public function find( $id ){
$query = $this->getEntityManager()
->createQuery('
SELECT p, h
FROM MyBundle:Person p
JOIN p.Home h
WHERE p.id = :id'
)->setParameter('id', $id);
return $query->getSingleResult();
}
How can I prevent the Place fetching its Building relationship separately during the find operation? Is there something I can pass into the Query instance to stop this?
add fetch option to your mapping.
Like so:
class Place {
/**
* Reverse mapping
* #var Person
*
* #ORM\OneToOne(targetEntity="Person", mappedBy="Home", fetch="EXTRA_LAZY")
*/
private $Resident;
}

Getting the users of an FOSUserBundle Group (Symfony2)

I have been struggling with this question for the past couple of hours and haven't been able to find anything.
I have configured the FOSUserBundle and have the groups activated correctly and working. However, the user-group relation is defined as follows in the User class :
/**
* #ORM\ManyToMany(targetEntity="Netlabs\UserBundle\Entity\Group")
* #ORM\JoinTable(name="fos_user_user_group",
* joinColumns={#ORM\JoinColumn(name="user_id", referencedColumnName="id")},
* inverseJoinColumns={#ORM\JoinColumn(name="group_id", referencedColumnName="id")}
* )
*/
protected $groups;
and in the Group class, I do not even have a $user list defined. I tried loads of "normal" things like creating a $users variable in my Group class and defining it's relation to the User object, but it is not playing nice with the FOSUserBundle (every time, it creates a NEW table to create the relation, when there is already one in place). For example:
/**
* #ORM\ManyToMany(targetEntity="Netlabs\UserBundle\Entity\Group", inversedBy="groups", cascade={"all"})
*
*/
protected $users;
Any suggestions ?
Again, my main goal is : I want to be able to do Group->getUsers()
In your Group class, your targetEntity must be User and not Group. Furthermore, try to add an inversedBy rule for the $groups attributs and modifie inversedBy in mappedBy for $users.
/**
* #ORM\ManyToMany(targetEntity="Netlabs\UserBundle\Entity\Group", inversedBy="users")
* #ORM\JoinTable(name="fos_user_user_group",
* joinColumns={#ORM\JoinColumn(name="user_id", referencedColumnName="id")},
* inverseJoinColumns={#ORM\JoinColumn(name="group_id", referencedColumnName="id")}
* )
*/
protected $groups;
And
/**
* #ORM\ManyToMany(targetEntity="Netlabs\UserBundle\Entity\User", mappedBy="groups")
*
*/
protected $users;

Categories