Doctrine QueryBuilder indexBy on joined class - p is already defined Error - php

Using symfony2/doctrine2, I have a hard time defining an index for my query.
My code :
$queryBuilder = $this->_em
->createQueryBuilder()
->select('u, uis, cost, p, stock')
->from('AppBundle:FoodAnalytics\UserIngredient', 'u', 'p.id')
->leftJoin('u.product', 'p')
->leftJoin('u.numberObjects', 'stock')
->leftJoin('u.userIngredientSuppliers', 'uis')
->leftJoin('uis.numberObjects', 'cost')
->where('u.user = ?1')
->setParameter(1, $portfolioUser)
;
I get the following error :
[Semantical Error] line 0, col 110 near 'p LEFT JOIN u.numberObjects': Error: 'p' is already defined.
500 Internal Server Error - QueryException
1 linked Exception: QueryException »
[1/2] QueryException: SELECT u, uis, cost, p, stock FROM AppBundle:FoodAnalytics\UserIngredient u INDEX BY p.id LEFT JOIN u.product p LEFT JOIN u.numberObjects stock LEFT JOIN u.userIngredientSuppliers uis LEFT JOIN uis.numberObjects cost WHERE u.user = ?1 +
Using u.product I get the following error :
[Semantical Error] line 0, col 87 near 'product LEFT': Error: Invalid PathExpression. Must be a StateFieldPathExpression.
500 Internal Server Error - QueryException
1 linked Exception: QueryException »
Using just product, I get the following error:
[Semantical Error] line 0, col 85 near 'product LEFT': Error: 'product' does not point to a Class.
500 Internal Server Error - QueryException
1 linked Exception: QueryException »
It works fine if I use u.id
Can I only use index by a field from the same table ?
What can I do to make it work ?
Thansk a lot !
EDIT :
As a temporary fix I am using :
$result = $queryBuilder->getQuery()->getResult();
$result = array_combine(array_map(function(UserIngredient $userIngredient){
return $userIngredient->getProduct()->getId();
}, $result), $result);
return $result;

The attribute indexBy only apply to the entity you are currently selecting, as you have guessed (and AFAIK); so in your case you could only index by u.id.
This makes sense to me, since indexing from other entity really mess up the returned results. The only situation in which you'll get proper result is when:
all the join from the main entity and the "target" indexBy entity are one-to-one;
all the join are INNER JOIN;
the indexBy column of the resultset is unique.
In every other case you loose some instance reference during hydration.
In your example, you are using LEFT JOIN: what will happen to all entities without product? All discarded, so the LEFT JOIN would have worker like an INNER JOIN. Also, your workaround suggest that you want to combine entities with the same index, but this is not how Doctrine work: in Doctrine an indexBy column MUST be unique. See the official docs for more info.
Note that you could indexBy p.id in your JOIN clause, but this has different meaning. It means that when hydrating the UserIngredients instances, the collection of product should be indexed by id.
So, my suggestion is to follow one of this two solution:
give up on Doctrine indexBy, and use your workaraound;
If you have the inverse association Product -> UserIngredients, use Product as the main entity of the query (the from clause, and first to appear in select), and then index by p.id.
Which one to use depends on your business logic, but I generally prefer the second solution, since the first generate multiple-level array, which are better represented by entity relations.
Hope this help! :)

Related

How to get the number/count of user with exactly one order in Doctrine?

I am working on a Symfony 2.8 based project that allows an user to place an order.
Now I would like to create a Doctrine query that returns the number of users which exactly one order. I thought this would be easy but I am on this for hours...
Select the users and their number of orders
Select all users with order_count = 1 from the result of step 1
Apply some more filters by selecting only orders from users in the result of step 2 and by adding condition (e.g. status = completed)
In SQL the following works fine to solve Step 1:
SELECT user_id, COUNT(*) as count FROM order WHERE status = 'completed' GROUP BY user_id
However when I try to translate this to Doctrine I get the following error:
$qb->select('o_inner.user, COUNT(*) as count')
->from('MyBundle:Order', 'o_inner')
->where('o_inner.status = :status')
->groupBy('o_inner.user')
->setParameter('status', 'completed');
[Doctrine\ORM\Query\QueryException]
[Syntax Error] line 0, col 30: Error: Expected Literal, got '*'
So I cannot even solve the first step. Any idea how to do this?
EDIT:
Did some more experiments with different selects:
$qb->select('o_inner.user, COUNT(o_inner.user) as count')
$qb->select('COUNT(o_inner.user) as count')
==> [Doctrine\ORM\Query\QueryException]
[Semantical Error] line 0, col 39 near 'FROM MyBundle:Order': Error: Class 'FROM' is not defined.
$qb->select('COUNT(o_inner.user)')
==> Works fine. So the "as count" statement seems to be a problem
$qb->select('o_inner.user')
==> [Doctrine\ORM\Query\QueryException]
[Semantical Error] line 0, col 12 near 'user FROM': Error: Invalid PathExpression. Must be a StateFieldPathExpression.
$qb->select('o_inner')
==> Workes fine
$qb->select('o_inner, COUNT(o_inner.user)')
==> Workes fine
So $qb->select('o_inner, COUNT(o_inner.user)') seems to work for Step 1. But since I cannot use a custom alias (... as count does not work), how can I reference the count result in a an outer select?
You are using an ORM so you need to specify which field you want to count for example:
$qb->select('o_inner.user, COUNT(o_inner.user) as count')
->from('MyBundle:Order', 'o_inner')
->where('o_inner.status = :status')
->groupBy('o_inner.user')
->setParameter('status', 'completed');
;
$tags = $qb->getQuery()->getResult();
You do not have to use ORM for complicated queries like reports/statistics.
Single optimized SQL query might have better performance and be easier to understand and maintain.

Doctrine Query Error for JOIN: Expected end of string, got 'I'

My PHP Code is:
$company = $qb->select("c")
->from("CRMBundle:TblCompanyDomain","cd")
->join("cd.company","c")
->leftJoin("CRMBundle:TblCompanyAddress",
"ca",
"WITH",
"ca.company = c")
->join("ca.country","cc")
->where($qb->expr()->andX(
$qb->expr()->in("cd.domain",":domain"),
$qb->expr()->eq("c.channel",":channel_id")
))
->setParameters($parameters)
->setMaxResults(1)
->getQuery()
->getOneOrNullResult();
End Received DQL code is:
SELECT
c
FROM
CRMBundle:TblCompanyDomain cd
INNER JOIN cd.company c
LEFT JOIN CRMBundle:TblCompanyAddress ca WITH ca.company = c
INNER JOIN ca.country cc
WHERE
cd.domain IN(:domain) AND
c.channel = :channel_id
When I execute this query, I've received this error:
[Syntax Error] line 0, col 44: Error: Expected end of string, got 'I'
enter code here
I could not see any syntax error. Where is the problem?
My Symfony version: 2.6.11
My Doctrine Version: 2.2
SELECT
c
FROM
CRMBundle:TblCompanyDomain cd
INNER JOIN cd.company c
LEFT JOIN CRMBundle:TblCompanyAddress ca WITH ca.company = c
INNER JOIN ca.country cc
WHERE
cd.domain IN(:domain) AND
c.channel = :channel_id
I think that you need c.something
I've changed from annotation to yml my doctrine orm mapping and fixed problems.
I've experienced to many times same problem and understand real problem. When I using Doctrine annotation with utf-8, this problem fired. May be, cause is utf-8 on Turkish. Because lower "ı" charachter's capital is "I" and lower "i" carachter's capital is "İ".

Cannot select entity through identification variables in repository

I have the following query. The query is inside my InstagramShopPicture
$queryBuilder = $this->createQueryBuilder('p')
->select('p.id, p.caption, p.price, p.lowresimageurl, p.medresimageurl, p.highresimageurl, p.numberoflikes, p.numberofdislikes, shop.id, shop.username, shop.fullname, contact, category')
->leftJoin('p.shop', 'shop')
->leftJoin('shop.contact', 'contact')
->leftJoin('p.category', 'category')
->leftJoin('category.pictureTopCategory', 'pictureTopCategory')
->leftJoin('category.pictureFirstLevelCategory', 'pictureFirstLevelCategory')
->leftJoin('category.pictureSecondLevelCategory', 'pictureSecondLevelCategory')
->where('p.isLocked = false')
->andWhere('p.isStyleInspiration = false')
->andWhere('shop.isLocked = false');
I am however getting the following error:
QueryException: [Semantical Error] line 0, col -1 near 'SELECT p.id,': Error: Cannot select entity through identification variables without choosing at least one root entity alias
any idea on how to solve this?
After some research here in SO I came to this solution. Try adding
->from('YourEntityNameForP', 'p')
->from('YourEntityNameForShop', 'shop')
to createQueryBuilder
Since I'm not familiar neither with Symfony 2, nor with Doctrine 2 and just trying to help!
Credits here: Doctrine: Cannot select entity through identification variables without choosing at least one root entity alias
Seems you are trying to make joins without using aliases for your tables.
This chould cause you a lot of problems, particularly when column names are identical
->leftJoin('p.category', 'category') // aliasing
->leftJoin('category.pictureTopCategory', 'pictureTopCategory') // table name
One time you are using table name, next you are using aliases !! Choose only one approach of them

How can I make a field in a Symfony2 form, using querybuilder, excluding entities without a many-to-many relation?

In my Symfony2 app, people can write texts that will be published on a webpage.
There is an option to write a new text, and you have to select the webpage it will belong to.
What I want to achieve, is that this list of webpages shows webpages that do not yet have a text only. Otherwise you will end up replacing an existing text.
So I would like to write something like this in my form type:
$qb->select('wp')
->from('MyBundle:Webpage', 'wp')
->where('wp.webtexts is null')
->orderBy('wp.id');
The problem arises around the "wp.webtexts is null" statement. This is a (fully functioning) many-to-many relationship, and I would like to test whether there are no relations here. The error I receive, is:
[Semantical Error] line 0, col 70 near 'webtexts is null': Error:
Invalid PathExpression. StateFieldPathExpression or
SingleValuedAssociationField expected.
How can I query for webpages, with no relations to any webtexts?
Added:
How could I count the amount of relations? This notation:
$qb->where($qb->expr()->count('wp.webtexts < 1'))
...gives me:
[Syntax Error] line 0, col 85: Error: Expected
Doctrine\ORM\Query\Lexer::T_CLOSE_PARENTHESIS, got '<'
If you are trying to check against the webtexts object you should query the webtexts id field rather than the webtexts object. Doctrine is still trying to write SQL at the end of the day and in SQL you would have to query a field:
$qb->select('wp')
->from('MyBundle:Webpage', 'wp')
->leftJoin('wp.webtexts', 'wt')
->where('wt.id IS NULL')
->orderBy('wp.id');
Like this:
$em->createQuery('SELECT wp FROM MyBundle:Webpage wp LEFT JOIN wp.webtexts wt WHERE wt.id IS NULL ORDER BY wp.id');
It takes Webpages that are joined or not with texts and then eliminates thoses which have a relation with text... So you've got webpages without relation !

How to JOIN [using Doctrine2 and CodeIgniter2]

New to Doctrine, and trying to get a JOIN to work.
I've banged my head on this for awhile, and am slowly beginning to get the whole picture - its not as simply as I thought it would be.
I have two Entities with corresponding sql tables. I'm attempting to run a query that will return data from both of them, linked. A JOIN.
If I was doing this in straight SQL, it would look like this:
SELECT *
FROM mail_links a
INNER JOIN mail_msgs b
ON a.msg_id = b.id
I've tried a few things to make this work, including:
$query = $this -> doctrine -> em -> createQuery ("SELECT a FROM ORM\Dynasties2\Maillinks a JOIN ORM\Dynasties2\Mailmsgs b ON a.msgId = b.id");
That of course did not work. I realized that this relies on having Entities created as Classes, or at least that's what some examples seemed to be doing.
And that's where I'm completely lost.
Is it correct that I need those entities to be Classes? Are there any complete code examples of this (using CI) that would help me understand how to write the classes necessary for this JOIN?
Edit:
It just dawned on me that I DO have classes setup. That's what my entities ARE.
So how do I make this work:
$query = $this -> doctrine -> em -> createQuery ("SELECT a FROM ORM\Dynasties2\Maillinks a JOIN ORM\Dynasties2\Mailmsgs b ON a.msgId = b.id");
It gives the following error:
Fatal error: Uncaught exception 'Doctrine\ORM\Query\QueryException' with message '[Semantical Error] line 0, col 70 near 'b ON a.msgId': Error: Identification Variable ORM\Dynasties2\Mailmsgs used in join path expression but was not defined before.' in /PATH/applicationFolder/libraries/Doctrine/ORM/Query/QueryException.php:47 Stack trace: #0 /PATH/applicationFolder/libraries/Doctrine/ORM/Query/Parser.php(413): Doctrine\ORM\Query\QueryException::semanticalError('line 0, col 70 ...') #1 /PATH/applicationFolder/libraries/Doctrine/ORM/Query/Parser.php(914): Doctrine\ORM\Query\Parser->semanticalError('Identification ...') #2 /PATH/applicationFolder/libraries/Doctrine/ORM/Query/Parser.php(1567): Doctrine\ORM\Query\Parser->JoinAssociationPathExpression() #3 /PATH/ in /PATH/applicationFolder/libraries/Doctrine/ORM/Query/QueryException.php on line 47

Categories