Doctrine 2 Query Builder Many To Many collection filtered on Where clause - collections

I'm facing a problem with a query where I want to retrieve only a part from a collection. This problem is easy to solve with a leftJoin with a condition. The problem is when I want to make some where clause on this filtered collection.
Better with an example:
I have an OBJECT entity with a collection of childrens in a many to many relationship.
class Object{
/**
* #ORM\ManyToMany(targetEntity="App\Entity\Instancia", mappedBy="codes")
*/
private $childrens;
}
class Children{
/**
* #ORM\ManyToMany(targetEntity="App\Entity\Codes", inversedBy="childrens")
* #ORM\JoinTable(name="children_codes",
* joinColumns={#ORM\JoinColumn(name="childrenId", referencedColumnName="id",)},
* inverseJoinColumns={#ORM\JoinColumn(name="codeId", referencedColumnName="id")}
* )
*/
private $codes;
}
What I want: Get all the codes that have more than 1 children with more than 5 years. In addition, the query should only retrieve the part of the children collection with this condition. So...
$this->createQueryBuilder('object')
->leftJoin('object.children', 'children', 'WITH', 'children.age > 5')->addSelect(children) /*The collection is correctly filtered to retrieve only the colelction that I want*/
->where('SIZE(object.children) > 1') /*The size is applied to all the collection and not on the filter leftJoin collection with the age > 5 condition*/
->getQuery()>getResult();
Any idea for the where clause?
Thanks a lot!

Related

doctrine select/filter on child collections

I -roughly- have this hierarchy :-
class City {
/**
* #ORM\OneToMany(targetEntity="AppBundle\Entity\District", mappedBy="city")
*/
private $districts;
}
class District {
/**
* #ORM\Column(type="boolean")
*/
private $isRemoved;
}
and i am attempting to query Cities , but i don't want to view removed districts in the query.
my current solution involves looping over the districts and checking the removed attributes and removing the districts from the return object.
my other option was writing a detailed query from scratch using the query builder , but that -though may work in this case - becomes exponentially more complex as the hierarchy deepens.
Seems very simple to me, maybe I didn't understood well, but let give it a try
$cities = $em->getRepository('AppBundle:City')->createQueryBuilder('c')
->select('c', 'd')
->leftJoin('c.districts', 'd')
->where('d.isRemoved = 0')
->getQuery()->getResult();
this should give you cities with associated districts collections having isRemoved to false.
hope this will help you.

limit columns returned in relationnal entity symfony2

Is it possible to filter an entity and display only few columns in symfony2?
I think I can do a custom query for this, but it seems a bit dirty and I am sure there is a better solution.
For example I have my variable $createdBy below, and it contains few data that shouldnt be displayed in this parent entity such as password etc...
/**
* #var Customer
*
* #ORM\ManyToOne(targetEntity="MyCompany\Bundle\CustomerBundle\Entity\Customer")
* #ORM\JoinColumns({
* #ORM\JoinColumn(name="created_by", referencedColumnName="id", nullable=false)
* })
*/
protected $createdBy;
So I need to display my Customer entity, but only containing fields like id and name for example.
EDIT :
I already have an instance of Project, the entity with my createdBy field, and I want to grab my customer data 'formatted' for this entity and not returning too much fields like password ...
Thanks
It sounds like expected behavior to me. The doctrine documentation seems to imply that eager fetching is only one level deep.
According to the docs:
Whenever you query for an entity that has persistent associations and
these associations are mapped as EAGER, they will automatically be
loaded together with the entity being queried and is thus immediately
available to your application.
http://doctrine-orm.readthedocs.org/en/latest/reference/working-with-objects.html#by-eager-loading
The entity being queried has eager on createdBy so it will be populated.
to bypass you can create a method in your entity repository as following :
// join entities and load wanted fields
public function findCustom()
{
return $this->getEntityManager()
->createQuery(
'SELECT p FROM AppBundle:Product p ORDER BY p.name ASC'
)
->getResult();
}
hope this helps you
try this and let me know if it works, you should fill the right repository name
'XXXXBundle:CustomerYYYY', 'c'
public function findUser($user_id){
$qb = $this->_em->createQueryBuilder('c')
->select(array('c', 'cb.id', 'cb.name'))
->from('XXXXBundle:Customer', 'c')
->where('c.id <> :id')
->leftJoin('c.createdBy', 'cb')
->setParameter('id', $user_id)->getQuery();
if ($qb != null)
return $qb->getOneOrNullResult();
return null;
}

Symfony - access object of non related tables

How can i access the object of second table when joined (non-related tables)?
I have two table which are not related and I want to get the object of the second class (from below dump output)
My repository with dump
For example:
my controller:
$ProductSet_Repo = $em->getRepository('MyTestBundle:Product\ProductSet')->FindProductSet($productid);
Normally when the tables are related I can simple do
$productSet = $ProductSet_Repo->getproductid()->getProduct(); to get the object of Product class From ProductSet Class.
See My Dump
However since the tables are not in relationship and when i dump the data i get the objects of two classes is there a way I can access the Object My\TestBundle:Products\Entity\Product\ProductSet and \My\TestBundle\Entity\Product\Product?
Note: i don't want to do establish relationship between the two tables as I am working on already existing table for which i don't want to make any changes
Also I know I can select the fields which i want to retrieve. (I dont want to do that)
You write:
i don't want to do establish relationship between the two tables as I am working on already existing table for which i don't want to make any changes.
But with doctrine you are very well able to make a association between two entities without changing the tables. As far as I can see from your query you have a product_id column in your product_set table. That is all you need to make an association between Product and ProductSet.
In your ProductSet class you can do:
<?php
namespace My\TestBundle\Entity\Product;
class ProductSet
{
//... other properties
/**
* #var Product
* #ORM\Id
* #ORM\ManyToOne(targetEntity="My\TestBundle\Entity\Product\Product")
* #ORM\JoinColumn(name="product_id", referencedColumnName="id")
*/
protected $product;
/**
* Set the product.
*
* #param Product $product
* #return ProductSet
*/
public function setProduct(Product $product)
{
$this->product = $product;
return $this;
}
/**
* Get the product.
*
* #return Product
*/
public function getProduct()
{
return $this->product;
}
//... other setters and getters
}
Now you can do:
$repository = $em->getRepository('MyTestBundle:Product\ProductSet')
$productSets = $repository->findBy(array('product' => $productid));
foreach($productSets as $productSet){
$productSet->getProduct()->getId() === $productId; // true
}
You can still join them (despite of strange naming convention you have id of corresponding object in the other entity) using query builder or native sql, but it's a really bad way.
it was developed by previous webdeveloper and i dont want to spend more time as i work as free lancer
That's not an excuse. You should create a relation and migration for these data. Getting money for a poorly designed and developed app is not cool.
Probably additional work when working with that poor design will take your more time than doing it in a proper way.

Doctrine ManyToMany, find related and no related results

I have an entity called tournament to which users can register to participate in.
The relationship between the two entities is ManyToMany and need to create a view of Symfony2 in which list all tournaments, with or without registered users so that they can join.
This is my DoctrineQueryBuilder
$em->createQueryBuilder('d')
->select('d, i, u')
->leftJoin('d.item','i')
->leftJoin('d.users','u')
->where('d.active = 1')
->andWhere('d.state = 1')
->orderBy('d.dateStart', 'ASC');
I also need to get the number of users who have joined the tournament.
Preamble
There are various ways to achieve what you want. You can create a sub-query to do the count, however a simpler solution is to let doctrine handle this for you.
The solution described below is based on Doctrine lazy/eager loading capability. When doctrine loads an entity, it will also populate it's associations, either lazily or eagerly (default is lazy).
Solution
Assuming your Tournament entity maps the users association as a ManyToMany relation. You can create a new method which counts your Tournament->users.
ManyToMany associations would populate the entity property (in this cases $users) with an ArrayCollection.
A method called countUsers would do the trick, example implementation below:
...
class Tournament {
...
/**
* #ManyToMany(targetEntity="User")
* #JoinTable(name="tournament_users",
* joinColumns={#JoinColumn(name="user_id", referencedColumnName="id")},
* inverseJoinColumns={#JoinColumn(name="tournament_id", referencedColumnName="id"}
* )
**/
private $users;
...
public function countUsers(){
return $this->users->count();
}
In the view when iterating over the collection of tournaments, simply call the $tournament->countUsers() method to display the count.
References:
ArrayCollection: http://www.doctrine-project.org/api/common/2.1/class-Doctrine.Common.Collections.ArrayCollection.html

Doctrine Query Builder Where Count of ManyToMany is greater than

Im using the Doctrine Query Builder, and have a very specific requirement that came through.
I am using the ManyToMany field in my entity, related to User entity association (Array of User account entities).
/**
* #var ArrayCollection
*
* #ORM\ManyToMany(targetEntity="User", cascade={"persist"})
* #ORM\JoinTable(name="post_user_list")
*/
protected $userList;
Amongst the requirements of displaying "public posts" requires that the Entity have a published boolean set to true, a published date less than the current date, and two users associated with entity.
In my query builder, I have setup this:
$qb = $this->getEntityManager()->createQueryBuilder();
$qb->select($select)->from($this->getEntityName(), 'p');
$criteria = $qb->expr()->andX();
$criteria->add($qb->expr()->eq('p.editor_published', 1))
->add($qb->expr()->lte('p.datePublished', ':now'));
and that only handles the first two requirements, now I need a criteria entry for counting the amount of user entities in userList, and the where clause specifically for greater than or equal to two users.
Not exactly sure where to proceed..
Try this. The query uses HAVING to only display entities that are associated with 2 or more users.
$qb->select($select)
->from($this->getEntityName(), 'p')
->innerJoin('p.userList','u')
->where('p.editor_published = 1')
->andWhere('p.datePublished <= :now')
->groupBy($select) //not sure what's in $select may need to change this
->having('count(u.id) > 1'); //assuming user has an id column otherwise change it
->setParameter('now',new \DateTime());

Resources