Doctrine2 QueryBuilder select entity and count of associated entities - symfony

I'm having a huge problem with ORM QueryBuilder. What I need to do is:
I need to fetch order with count of its products and plenty of associated entities (associated with order), but I assume they're not relevant here. I also need to order result by that count.
Could anyone give me an example of how this can be achieved? I would like to avoid "inline" DQLs if possible.

You can get data via Doctrine Query Builder.
You are supposed to left join products from Order and then group by order id. You can have COUNT(product.id) in your select statement and use the alias in order by clause to make your orders sorted. Below is a small code snippet from Repository.
/**
* #return \Doctrine\ORM\Query
*/
public function getHotelAndRoomType()
{
$qb = $this->createQueryBuilder('order')
->select('partial order.{id, orderId} as order, count(product.id) as total_products_in_order')
->leftJoin('AppBundle:Product', 'product', 'WITH', 'product.order = order.id')
->groupBy('order.id')
->orderBy('total_products_in_order', 'DESC')
;
return $qb->getQuery()->execute();
}
Note : Code not tested.

Related

How to build this query on Symfony with QueryBuilder?

Please I am a beginner on symfony and I have 2 entities: category and professionnal with ORM many to many. And 'City' is an entity with ORM oneToMany with 'professionnal'.
In professionnel I have a property city. I want to show professionnal grouped by categories who have a special cityId putted on parameter in the url.
I put this query on sql
(for example city_id= 10873)
, it gives me the results.
SELECT * FROM sub_category AS a LEFT JOIN professionnel ON professionnel.city_id = 10873
but I don't know how to write with querybuilder.
I put this solution but I have errors:
$city = $paramFetcher->get('city');
$queryBuilder = $em->getRepository(SubCategory::class)
->createQueryBuilder('a')
->leftJoin('App\Entity\Professionnel','p')
->where('p.city = :city')
->setParameter('city', $city);
return $queryBuilder->getQuery()->getResult();
in log:
request.CRITICAL: Uncaught PHP Exception Doctrine\DBAL\Exception\SyntaxErrorException: "An exception occurred while executing 'SELECT c0_.id AS id_0, c0_.name AS name_1 FROM category c0_ LEFT JOIN professionnel p1_ WHERE p1_.city_id = ?' with params ["10873"]: SQLSTATE[42000]: Syntax error or access violation: 1064 You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near 'WHERE p1_.city_id = '10873'' at line 1"
I search on forums too and I found that I can replaced the 'ON' of leftJoin as following, but shows me all professionnals:
$city = $paramFetcher->get('city');
$queryBuilder = $em->getRepository(SubCategory::class)
->createQueryBuilder('a')
->leftJoin('App\Entity\Professionnel','p','WITH','p.city = :city')
->setParameter('city', $city);
return $queryBuilder->getQuery()->getResult();
For the mapping, I put it like this:
class Category
{
/**
* Many professionnels have Many categories.
* #ORM\ManyToMany(targetEntity="App\Entity\Professionnel", mappedBy="Categories")
* #ORM\JoinTable(name="professionnel_category")
*/
private $professionnels;
}
class Professionnel extends User
{
/**
* Many professionel have Many categories.
* #ORM\ManyToMany(targetEntity="App\Entity\Category", inversedBy="professionnels")
* #Groups({"Default", "professionnel", "user"})
*/
private $categories;
}
Thank you so much for your help.
So, assuming that you have your entity relation set up correctly (please update your question with those), I see one mistake above.
If you look at the official Doctrine Query Builder documentation, the leftJoin method's third argument is either WITH or ON constant and not join predicate. That one comes as a 4th argument. But, even then, that join predicate is only used is a very specific use-case, where you want to further define your join relation. By looking at your example, I doubt that is your case.
With that being said, I think your code should look like this:
$city = $paramFetcher->get('city');
$queryBuilder = $em->getRepository(Category::class)
->createQueryBuilder('a');
->leftJoin('App\Entity\Professionnel','p')
->where('p.city_id = :city')
->setParameter('city', $city);
return $queryBuilder->getQuery()->getResult();
Now the question: is that really just a city_id (plain integer) or is that relation to some entity named City?
Please update your question with those details and I will update the answer, if necessary...
Hope this helps...
Firstly, you need describe relations in entities. Read more about this here
Secondary, you must call in join field (read as property), not a Entity.
inside CategoryRepository:
/** #return ArrayCollection|Category[] */
public function getByCityId(int $cityId): ArrayCollection
{
$qb = $this->createQueryBuilder('category');
$qb->leftJoin('category.professional', 'professional')
->leftJoin('category.city', 'city')
->where($qb->expr()->eq('city.id', $cityId));
return $qb->getQuery()->getResult();
}
Inside SomeController:someAction()
$cityId = $paramFetcher->get('city');
$categoriesList = $this->get('doctrine.orm.entity_manager')
->getRepository(Category::class)
->getByCityId($cityId);
As result you'll be have list of category, with joined professional and city.
I suggest to read this article on symfony docs

Doctrine select many from the one (many-to-one unidirectional (different bundles))

Working on a legacy project which restricts the options available, has left me in a situation where I need to solve the following problem, ideally with doctrine.
I have two entities in different bundles that have a unidirectional many-to-one link.
BundleA has dependency on BundleB and the entities are linked similar to this:
BundleA/Entity/TheMany:
/**
* #var TheOne $theOne
* #ORM\ManyToOne(targetEntity="BundleB\Entity\TheOne")
* #ORM\JoinColumn(name="theone_id", referencedColumnName="id", onDelete="SET NULL")
*
*/
private $theOne;
From BundleB I now need to select all TheOne entities, and for each I need all of the TheMany entities.
The query also needs to be sortable on any property of TheOne entity, or the count of related TheMany entities.
It is fairly simple in Doctrine to build a query which brings back all TheOne entities and one of TheMany for each... however I am having some difficulty coming up with a Doctrine query that will bring back all of the related TheMany entities rather than just one.
I was hoping someone might have encountered a similar issue and therefore have some insight?
This may not have been explained clearly enough, in which case please direct me to explain further.
In the end I was able to achieve what I needed by using GROUP_CONCAT (which required inclusion of https://github.com/beberlei/DoctrineExtensions).
The query looks something like this:
$queryBuilder->select(
'to,
GROUP_CONCAT(DISTINCT tm.id SEPARATOR \',\') as theManyIds,
COUNT(DISTINCT tm.id) as HIDDEN theManyCount'
)
->from('BundleB\Entity\TheOne', 'to')
->leftJoin(
'BundleA\Entity\TheMany',
'tm',
Join::WITH,
'to.id = tm.theOne'
)
->groupBy('to.id')
->orderBy($sortString, $direction)
->setFirstResult($start)
->setMaxResults($limit);
I compromised by accepting the consequences of linking the two bundles - however that could have been avoided by making use of Native SQL and Result Set Mapping (http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/native-sql.html).
So what you are trying to do is to get all the ones and for each one find all the many. But you want to put all the many in one array or you want to create an array of array for the entities ? (what i did here)
$em = $this->getDoctrine()->getManager();
$theones = $em->getRepository('BundleA:theOne')
->createQueryBuilder('p')
->OrderBy(//your ordering)
->getQuery()
->getArrayResult()
$theManies = [];
for($theones as $theOne){
$theManies [] = $em->getRepository('BunbleB:theMany')
->createQueryBuilder('p')
->Where('p.theOne = :theOne')
->setParameter('theOne', $theOne)
->getQuery()
->getArrayResult();
$finalOnes[$theOne->getId()] = sizeof($theManies)
}
asort($finalOnes);
return array_keys($finalOnes);

One of listeners must count and slice given target

I'm getting the above error when performing a search in a Symfony2 CRM I've been working on. According to Google searches it seems this is an issue relating to the KNP Paginator bundle, but I cannot seem to find a solid solution.
In this instance, I am using data from an OpenCart database, and I need to be able to search by Postcode and Company name, both of which exist in the address table which is joined via a mapped value within the Customer entity, defaultaddress.
To this end, I've had to write a custom query in a function called findCustomerByPostcode like so:
$this->getEntityManager()
->createQuery('SELECT c FROM AppBundle:Oc49Customer c JOIN AppBundle:Oc49Address a WITH c.defaultaddress = a.id WHERE a.postcode LIKE :postcode')
->setParameter('postcode','%'.$postcode.'%')
->getResult();
However, when I perform a search on postcode, I get the following error in the browser:
One of listeners must count and slice given target
which refers to the Paginator.php file within the KNP bundle. I have updated to the most recent version, 2.5 yet I cannot seem to shake this error, and to me it does not even make sense.
Any help is much appreciated, as I cannot think of another way of search via a value within a joined table.
Which returns results from the postcode search, and then in the Controller:
$customers = $customer_repository->findCustomerByPostcode($filter_value);
$paginator = $this->get('knp_paginator');
$pagination = $paginator->paginate(
$customers,
$request->query->get('page', 1),
20
);
You can use the queryBuilder with the left join
public function findCustomerByPostcode($postcode) {
$query = $this->entityManager->createQueryBuilder();
$query
->select('c', 'a')
->from('AppBundle:Customer', 'c')
->leftJoin('c.address', 'a')
->setParameter('postcode', $postcode)
return $query->getQuery()->getResult();
}

Symfony2 QueryBuilder join ON and WITH difference

I'm new with Symfony2 and I built successfully my first join through QueryBuilder and Doctrine 2.
Probably this is a stupid question but both on-line and in the Symfony2's methods I was unable to find anything for understanding the difference between the join clauses "WITH" and "ON".
For example this is my join code:
->leftJoin('EcommerceProductBundle:ProductData', 'pdata', 'WITH', 'prod.id = IDENTITY(pdata.product)')
It works good but if I put ON instead of WITH I get the following error:
[Syntax Error] line 0, col 200: Error: Expected
Doctrine\ORM\Query\Lexer::T_WITH, got 'ON'
Why? I've seen among the objects that there are both the T_ON and T_WITH like join clauses, but which is their usage difference? What is their uses like?
#florian gave you the correct answer but let me try to explain it on example:
In sql, joins are done like this:
SELECT * FROM category
LEFT JOIN product ON product.category_id = category.id
(or something like this)
Now in Doctrine, you don't need to use ON clause because doctrine knows that from relations annotations in your entities. So above example would be:
// CategoryRepository.php
public function getCategoriesAndJoinProducts()
{
return $this->createQueryBuilder("o")
->leftJoin("o.products", "p")->addSelect("p")
->getQuery()->getResult() ;
}
Both would fetch all categories and join products associated with them.
Now comes the WITH clause. If you want to join only products with price bigger than 50, you would do this in SQL:
SELECT * FROM category
LEFT JOIN product ON product.category_id = category.id AND product.price>50
In Doctrine:
// CategoryRepository.php
public function getCategoriesAndJoinProductsWithPriceBiggerThan($price)
{
return $this->createQueryBuilder("o")
->leftJoin("o.products", "p", "WITH", "p.price>:price")
->setParameter("price", price)->addSelect("p")
->getQuery()->getResult() ;
}
So, in reality you should never, ever use ON if you are using Doctrine. If you have a need for something like that, you can be almost sure that you screwed something else.
In theory, ON permits you to give the full join criterias, while WITH permits to add additional criterias to the default ones (IMHO).
But, what DQL permits is to avoid giving the JOIN criterias:
You just have to say: $qb->leftJoin('prod.pdata', 'pdata');
And doctrine2 will handle the join correctly.
Here is a related question about that: Can I use "ON" keyword in DQL or do I need to use Native Query?

Symfony2 Select one column in doctrine

I'm trying to refine the query trying to select fewer possible values ​​..
For example I have an entity "Anagrafic" that contains your name, address, city, etc.,
and a form where I want to change only one of these fields, such as address.
I have created this query:
//AnagraficRepository
public function findAddress($Id)
{
$qb = $this->createQueryBuilder('r')
->select('r.address')
->where('r.id = :id')
->setParameter('id', $Id)
->getQuery();
return $qb->getResult();
}
there is something wrong with this query because I do not return any value, but if I do the query normally:
//Controller
$entity = $em->getRepository('MyBusinessBundle:Anagrafic')->find($id);
Return the right value.
How do I do a query selecting only one column?
Since you are requesting single column of each record you are bound to expect an array. That being said you should replace getResult with getArrayResult() because you can't enforce object hydration:
$data = $qb->getArrayResult();
Now, you have structure:
$data[0]['address']
$data[1]['address']
....
Hope this helps.
As for the discussion about performance in comments I generally agree with you for not wanting all 30 column fetch every time. However, in that case, you should consider writing named queries in order to minimize impact if you database ever gets altered.
You can use partial objects to only hydrate one field and still return a object.
This worked for me:
$qb = $repository->createQueryBuilder('i')
->select('i.name')
->...
Use partial objects like this to select fields
$qb = $this->createQueryBuilder('r')
->select(array('partial r.{id,address}'))
...
Put your field names between the brackets

Resources