API Platform version(s) affected: 3.0.0 | 3.0.1
Description
When upgraded from 2.7.0 to 3.0.0, I used the automatic tool provided by ApiPlatform.
It created some more operations on my entities but they seem not work.
For example, I have an Account entity and a Channel entity like below:
# Entity/Account #[ApiResource] #[Entity(repositoryClass: AccountRepository::class)] class Account implements Stringable, Timestampable { use TimestampableEntity; #[Column(name: 'id', type: Types::INTEGER)] #[Id] #[GeneratedValue(strategy: 'IDENTITY')] private int $id; #[OneToMany(targetEntity: Channel::class, mappedBy: 'account')] private Collection $channels; ... }
# Entity/Channel #[APIApiResource] #[APIApiResource(uriTemplate: '/accounts/{id}/channels.{_format}', uriVariables: ['id' => new APILink(fromClass: Account::class, identifiers: ['id'])], status: 200, operations: [new APIGetCollection()])] #[APIApiResource(uriTemplate: '/users/{id}/accounts/{accounts}/channels.{_format}', uriVariables: ['id' => new APILink(fromClass: User::class, identifiers: ['id']), 'accounts' => new APILink(fromClass: Account::class, identifiers: ['id'])], status: 200, operations: [new APIGetCollection()])] #[Entity(repositoryClass: ChannelRepository::class)] class Channel implements ConfigurableRemote, Stringable, Timestampable { use RemoteTrait; use TimestampableEntity; #[Column(name: 'id', type: Types::INTEGER)] #[Id] #[GeneratedValue(strategy: 'IDENTITY')] private int $id; #[ManyToOne(targetEntity: Account::class, inversedBy: 'channels')] private Account $account; ... }
When I call /api/accounts/1/channels
, I get the following error from Doctrine:
[Semantical Error] line 0, col 63 near ‘o_a1.id = :id_p1’: Error: ‘o_a1’ is not defined.
Additional Context
The query that causes the bug is this:
SELECT o FROM AppEntityChannel o WHERE o_a1.id = :id_p1 ORDER BY o.id ASC
As you can see, it uses o_a1
as table alias, while it sets the alias of the table to o
. So it seems there is a problem generating the correct DQL.
Digging into the code following the origin of the bug, I came to the trait ApiPlatformDoctrineOrmStateLinksHandlerTrait::57
:
This is the interested code:
if (!$link->getFromProperty() && !$link->getToProperty()) { $doctrineClassMetadata = $manager->getClassMetadata($link->getFromClass()); /* 57 */ $currentAlias = $link->getFromClass() === $resourceClass ? $alias : $queryNameGenerator->generateJoinAlias($alias); foreach ($identifierProperties as $identifierProperty) { $placeholder = $queryNameGenerator->generateParameterName($identifierProperty); $queryBuilder->andWhere("$currentAlias.$identifierProperty = :$placeholder"); $queryBuilder->setParameter($placeholder, $this->getIdentifierValue($identifiers, $hasCompositeIdentifiers ? $identifierProperty : null), $doctrineClassMetadata->getTypeOfField($identifierProperty)); } $previousAlias = $currentAlias; $previousJoinProperties = $doctrineClassMetadata->getIdentifierFieldNames(); continue; }
At line «57» the code is executed well: I have two classes:
Account
Channel
so the code correctly calls the method $queryNameGenerator->generateJoinAlias($alias)
.
The problem is that the query builder passed, in the DQL parts, for the select
part, has the o
value already set while the method $queryNameGenerator->generateJoinAlias($alias)
returns o_a1
.
Possible solution
The solution I tested is very simple: change ===
with !==
:
if (!$link->getFromProperty() && !$link->getToProperty()) { $doctrineClassMetadata = $manager->getClassMetadata($link->getFromClass()); - /* 57 */ $currentAlias = $link->getFromClass() === $resourceClass ? $alias : $queryNameGenerator->generateJoinAlias($alias); + /* 57 */ $currentAlias = $link->getFromClass() !== $resourceClass ? $alias : $queryNameGenerator->generateJoinAlias($alias); foreach ($identifierProperties as $identifierProperty) { $placeholder = $queryNameGenerator->generateParameterName($identifierProperty); $queryBuilder->andWhere("$currentAlias.$identifierProperty = :$placeholder"); $queryBuilder->setParameter($placeholder, $this->getIdentifierValue($identifiers, $hasCompositeIdentifiers ? $identifierProperty : null), $doctrineClassMetadata->getTypeOfField($identifierProperty)); } $previousAlias = $currentAlias; $previousJoinProperties = $doctrineClassMetadata->getIdentifierFieldNames(); continue; }
Doing this simple change, makes the DQL being generated correctly.
As I don’t know deeply the internals of Api Platform, I don’t know if this is an actual fix or simply a lucky shot.
UPDATE
Continuing to work on this, it seems the fix I provided is wrong: the problem is the logic is broken.
The fix I provided works with GetCollection
.
But with Get
it is completely broken (or, maybe, the documentation is wrong and I’m missing something in configuration of operations).
Anyway, the two DQL queries generated are wrong in any case:
$currentAlias = $link->getFromClass() !== $resourceClass ? $alias : $queryNameGenerator->generateJoinAlias($alias);
generates
SELECT o FROM CoommercioAppCoommercioEntityChannel o WHERE o.id = :id_p1 AND o_a1.id = :id_p2
while
$currentAlias = $link->getFromClass() === $resourceClass ? $alias : $queryNameGenerator->generateJoinAlias($alias);
generates
SELECT o FROM CoommercioAppCoommercioEntityChannel o WHERE o_a1.id = :id_p1 AND o.id = :id_p2
In both cases, the alias o_a1
is not defined.
More, the query seems to be wrong also for the fields it uses. The correct one should be similar to this:
SELECT o FROM CoommercioAppCoommercioEntityChannel o WHERE o.id = :id_p1 AND o.account_id = :id_p2
394 votes
1 answers
Get the solution ↓↓↓
your learning Symfony neighbor here,
I am calling a method from my controller through Ajax on a select change.
I manage to get the data I send to the controller, but I’m struggling as I try to make a Select with the data I get, for some reason, no matter the existing column I decide to return, I get a «specifiedColumn is not defined» and I can’t figure out why.
Here’s my method :
public function getPricesAction(Request $request)
{
$first = $request->request->get('first');
$final = $request->request->get('final');
$em = $this->getDoctrine()->getManager();
//not too sure about stacking 2 setParameter here, guess there's a better way. But even if I just use one parameter, I still get the same error 'id is not defined'.
$getPrice = $em->createQuery('SELECT id FROM CarsBundle:Travel t WHERE t.first = :tst and t.final = :tfi')->setParameter('tre', $first)->setParameter('tra', $final);
$result = $getPrice->getResult();
return new JsonResponse($result);
}
I am using Symfony 3.3
thanks for the help !
2022-08-2
Write your answer
578
votes
Answer
Solution:
$getPrice = $em->createQuery('SELECT id FROM CarsBundle:Travel t WHERE t.first = :tst and t.final = :tfi')->setParameter('tre', $first)->setParameter('tra', $final);
Should be:
$getPrice = $em->createQuery('SELECT id FROM CarsBundle:Travel t WHERE t.first = :tst and t.final = :tfi')->setParameter('tst', $first)->setParameter('tfi', $final);
You got the wrong parameter name
Share solution ↓
Additional Information:
Date the issue was resolved:
2022-08-2
Link To Source
Link To Answer
People are also looking for solutions of the problem: dompdf image not found or type unknown
Didn’t find the answer?
Our community is visited by hundreds of web development professionals every day. Ask your question and get a quick answer for free.
Similar questions
Find the answer in similar questions on our website.
229
March 23, 2018, at 7:02 PM
your learning Symfony neighbor here,
I am calling a method from my controller through Ajax on a select change.
I manage to get the data I send to the controller, but I’m struggling as I try to make a Select with the data I get, for some reason, no matter the existing column I decide to return, I get a «specifiedColumn is not defined» and I can’t figure out why.
Here’s my method :
public function getPricesAction(Request $request)
{
$first = $request->request->get('first');
$final = $request->request->get('final');
$em = $this->getDoctrine()->getManager();
//not too sure about stacking 2 setParameter here, guess there's a better way. But even if I just use one parameter, I still get the same error 'id is not defined'.
$getPrice = $em->createQuery('SELECT id FROM CarsBundle:Travel t WHERE t.first = :tst and t.final = :tfi')->setParameter('tre', $first)->setParameter('tra', $final);
$result = $getPrice->getResult();
return new JsonResponse($result);
}
I am using Symfony 3.3
thanks for the help !
Answer 1
$getPrice = $em->createQuery('SELECT id FROM CarsBundle:Travel t WHERE t.first = :tst and t.final = :tfi')->setParameter('tre', $first)->setParameter('tra', $final);
Should be:
$getPrice = $em->createQuery('SELECT id FROM CarsBundle:Travel t WHERE t.first = :tst and t.final = :tfi')->setParameter('tst', $first)->setParameter('tfi', $final);
You got the wrong parameter name
-
05:30
Trying to take the file extension out of my URL
-
04:00
display list that in each row 1 li
-
00:00
Read audio channel data from video file nodejs
-
10:30
session not saved after running on the browser
-
9:10
Best way to trigger worker_thread OOM exception in Node.js
-
07:40
Firebase Cloud Functions: PubSub, «res.on is not a function»
-
04:00
TypeError: Cannot read properties of undefined (reading ‘createMessageComponentCollector’)
-
9:20
AWS XRAY on Fargate service
-
7:40
How to resolve getting Error 429 Imgur Api
Posted By: Anonymous
I have a problem with getting data from the database using the expr()
method function. I would like to get data where isPublic = true
and objectType = $objectType
OR user = $user
and objectType = $objectType
, no matter what the value of isPublic
is.
I’m getting this error:
[Semantical Error] line 0, col 76 near 'user-avatar)': Error: 'user' is not defined.
My code in repository:
public function findByObjectType($objectType, $user)
{
$qb = $this->createQueryBuilder('s');
return $qb->where($qb->expr()->andX(
$qb->expr()->eq('s.isPublic', true),
$qb->expr()->eq('s.objectType', $objectType)
))
->orWhere($qb->expr()->andX(
$qb->expr()->eq('s.user', $user->getId()),
$qb->expr()->eq('s.objectType', $objectType)
))
->getQuery()
->getResult();
}
where: $objectType = 'user-avatar'; $user = UserInterface
Solution
expr()->eq()
will treat the expression as literals, trying to use them literally as they appear on method call.
As mentioned by the library author:
You are not using parameter binding. Expressions use string concatenation internally, so this outcome is actually expected.
In your case, you should be doing something like::
return $qb->where($qb->expr()->andX(
$qb->expr()->eq('s.isPublic', ':true'),
$qb->expr()->eq('s.objectType', ':objectType')
))
->orWhere($qb->expr()->andX(
$qb->expr()->eq('s.user', ':userId'),
$qb->expr()->eq('s.objectType', ':objectType')
))
->setParameter('true', true)
->setParameter('userId', $user->getId())
->setParameter('objectType', $objectType)
->getQuery()
->getResult();
This way your code is easier to read, safer and more portable.
Answered By: Anonymous
Related Articles
- How to prevent scrolling the whole page?
- error LNK2005: ✘✘✘ already defined in…
- Vue.JS & Spring Boot — Redirect to homepage on 404
- Fastest way to pad a dataframe with uneven columns
- How to map values in a dataframe to a key with values…
- C++ template,typename and operator
- Laravel Concat to get first name + middle name + last name
- How to filter a RecyclerView with a SearchView
- Unexpected end of JSON input while parsing
- Form field border-radius is not working only on the last…
Disclaimer: This content is shared under creative common license cc-by-sa 3.0. It is generated from StackExchange Website Network.