Multiple join in symfony: Error: Expected =, <, <=, <>, >, >=, != - php

I'm trying to create and action that returns all Variantset for a given PrivateUser. It's a many-to-many relationship, so there's a connection table in between.
This is the query I'm trying to run
$variants = $repository->createQuery('SELECT variants
FROM AppBundle:PrivUser user
JOIN AppBundle:PrivUserVariantset uv
JOIN AppBundle:Variantset variants
WHERE user.iduser=:user_id')
->setParameter('user_id', $userid)
->getResult();
but it returns:
[Syntax Error] line 0, col 146: Error: Expected =, <, <=, <>, >, >=, !=, got 'variants'
If I try the very same query with a single join, getting for instance the number of Variantsets from the PrivUserVariantset table, it does work. It looks like a syntax error, but I don't manage to find it from the examples I have gathered.
Is this the right way to do this query?

I think better way to do this would be:
$variantsRepository = $this->getDoctrine()->getRepository('AppBundle:Variantset');
$qb = $variantsRepository->createQueryBuilder('variants'); //'variants' is just alias, it can be whatever you want
$qb->join('variants.privUsers', 'user')
->where('user.id = :user_id')->setParameter('user_id', $userid)
->getQuery()
->getResult();
Edit:
/** #Entity **/
class Variantset
{
// ...
/**
* #ORM\ManyToMany(targetEntity="PrivUser")
* #ORM\JoinTable(name="Variantset_privUser",
* joinColumns={#ORM\JoinColumn(name="iduser", referencedColumnName="id")},
* inverseJoinColumns={#ORM\JoinColumn(name="idvariantset", referencedColumnName="id")}
* )
**/
private $privUsers;
// ...
public function __construct() {
$this->privUsers = new \Doctrine\Common\Collections\ArrayCollection();
}
}

Finally I got it to work by changing the columns detailed in the JoinTable annotation: variantid should go on the first joinColumns whereas priv_userid must go on the inverseJoin
Variantset Class:
/**
* #var ArrayCollection
*
* #ORM\ManyToMany(targetEntity="PrivUser")
* #ORM\JoinTable(name="priv_user_variantset",
* joinColumns={#ORM\JoinColumn(name="variantset_varid", referencedColumnName="varid")},
* inverseJoinColumns={#ORM\JoinColumn(name="priv_user_priv_userid", referencedColumnName="priv_userid")}
* )
**/
private $privusers;
// ...
public function __construct() {
$this->privUsers = new \Doctrine\Common\Collections\ArrayCollection();
}
Controller:
$variantsRepository = $this->getDoctrine()->getRepository('AppBundle:Variantset');
$em = $variantsRepository->createQueryBuilder('variants'); //'variants' is just alias, it can be whatever you want
$variants = $em->join('variants.privusers', 'user')
->where('user.privUserid = :user_id')->setParameter('user_id', $userid)
->getQuery()->getResult();
I have only two entities, Varianset and PrivUser, whereas the DB has three tables, one for each entity plus priv_user_variantset, which connects both via ManyToMany relationship.
It works now for the purpose of my API, but if you have any comment, they'll be very welcomed.

Related

Symfony: get result of both tables

I want to use the following mysql query: select * from x join y on x.y_id = y.id where x.a= 0
I tried to put it in my Symfony project with the following doctrine syntax.
$repositoryX->createQueryBuilder('x')
->add('select', 'x')
->add('from', 'AppBundle:X x')
->leftJoin('AppBundle:Y', 'y', 'WITH', 'x.y_id = y.id')
->where('x.a = :a')
->setParameter('a', 0)
->getQuery()
->getResult();
My problem is that I only see the results of table x, but I also want the results of table y (because of the join on). When I put an extra select, I get Neither the property "y_id" nor one of the methods "y_id()", "gety_id()"/"isy_id()" or "__call()" exist and have public access in class
The '*' doesn't workin within doctrine .
You are only selecting x change:
->add('select', 'x')
to
->add('select', 'x,y')
//The second param might accept and array i dont remember off hand
->add('select', ['x','y'])
Or written better:
$repository->createQueryBuilder()
->select(['x','y'])
->from('AppBundle:X','x')
->join('x.y','y') // This assumes you have the mapping defined for this property
->where('x.a = :a')
->setParameter('a', 0)
->getQuery()
->getResult();
If you are using annotation mapping it should look something like
//X entity
//...
class X {
//...
/**
* #ORM\OneToOne(targetEntity="Y")
* #ORM\JoinColumn(name="y_id", referencedColumnName="id")
*/
protected $y;
}

Get relation of ManyToMany entities

I stuck with getting ManyToMany relation. I have two entities Offer and OfferType.
I defined ManyToMany relation only on OfferEntity and it looks like saving offers with multiple OfferType works fine in database I see correct joining table.
OfferEntity.php
/**
* Offer Entity
*
* #ORM\Table(name="offer")
* #ORM\Entity(repositoryClass="ProjectBundle\Repository\OfferRepository")
*/
class Offer
{
/**
* #Assert\Count(
* min = "1",
* minMessage = "You must specify at least one offer type."
* )
* #ORM\ManyToMany(targetEntity="OfferType")
*/
private $types;
public function __construct()
{
$this->types = new ArrayCollection();
}
/**
* #return ArrayCollection
*/
public function getTypes()
{
return $this->types;
}
(...)
Right now, I would like to get all types assigned to the offer. I tried it like this:
// $offer is an Entity
$query = $em->createQueryBuilder()
->select('offer.types')
->from('ProjectBundle:Offer', 'offer')
->where('offer = :offer')
->setParameters([
'offer' => $offer
]);
Unfortunately I getting error:
[2/2] QueryException: [Semantical Error] line 0, col 13 near 'types FROM ProjectBundle:Offer': Error: Invalid PathExpression. Must be a **StateFieldPathExpression**.
[1/2] QueryException: SELECT offer.types FROM ProjectBundle:Offer offer WHERE offer = :offer
Based on StackOverflow answers for similar questions tried it also to use IDENTITY():
// $offer is an Entity
$query = $em->createQueryBuilder()
->select('IDENTITY(offer.types)')
->from('ProjectBundle:Offer', 'offer')
->where('offer = :offer')
->setParameters([
'offer' => $offer
]);
But then I have:
[2/2] QueryException: [Semantical Error] line 0, col 22 near 'types) FROM ProjectBundle:Offer': Error: Invalid PathExpression. Must be a SingleValuedAssociationField.
[1/2] QueryException: SELECT IDENTITY(offer.types) FROM ProjectBundle:Offer offer WHERE offer = :offer
I would really appreciate any hints about getting ManyToMany relation, maybe my approach for this is incorrect?
Your approach is not quite right. You need to join the entity you want to load. You should spend a bit more time reading the doctrine documentation on entity relations & querybuilder.
Like so.
$query = $em->createQueryBuilder()
->select('offer', 'types')
->from('ProjectBundle:Offer', 'offer')
->join('offer.types', 'types')
->where('offer = :offer')
->setParameters([
'offer' => $offer
]);
Then your result will contain what you want.
Note: you don't even have to join types to get types from an offer, you could also use
$offer->getTypes()
And types will lazy load in standard doctrine manner.
But querying it as above will load types in advance - so you avoid lazy loading. This may or may not be a better approach depending on requirements.
If you have your many to many mappings set up only on one side, you'll only be able to query for the Offer and then retrieve the types from it:
$builder = $em->createQueryBuilder()
->select('o', 't')
->from('ProjectBundle:Offer', 'o')
->join('o.types', 't')
->where('o.id = :offer')
->setParameter('offer', $offerId);
$offer = $builder->getQuery()->getOneOrNullResult();
$types = $offer->getTypes();
That was assuming you only had $offerId to work with. If you already have the whole Offer instance, you might just as well just call getTypes() on it and doctrine will take care of the rest.
If you were to define the inverse mappings on the OfferType entity like so:
class OfferType
{
/**
* #ORM\ManyToMany(targetEntity="Offer", mappedBy="types")
*/
private $offers;
}
You could make queries for just the types:
$builder = $em->createQueryBuilder()
->select('t')
->from('ProjectBundle:OfferType', 't')
->join('t.offers', 'o')
->where('o = :offer')
->setParameter('offer', $offer);
$types = $builder->getQuery()->getResult();

Doctrine join a table with two

Good morning, as seen in the image below, I have some tables linked.
Using Doctrine (in Symfony2) I'm trying to get an array of Objects Issue which itself contains all IssueMessages and IssueStatusChanged objects but can not.
I have no idea how I can do to join two tables (IssueMessage and IssueStatusChanged) to through their identifiers.
The most we've done is get all Issue with an account of the messages that have:
$dql = 'SELECT x, COUNT(im.id) FROM PanelBundle:Issue x LEFT JOIN PanelBundle:IssueMessages im WITH x.id = im.idIssue';
Does anyone could give me a hand?
THANKS!
You want to use assication mapping; this will have Doctrine manage all the joins for you.
Once in place, $issue will always have the other associated models available automatically without you having to worry about joins.
For the example below (assuming you use annotation), to get messages for an issue just get the issue objects and then use $issue->getMessages();.
<?php
/** #Entity */
class issue
{
/**
* #ORM\Id
* #ORM\GeneratedValue
* #ORM\Column(type="integer")
*/
private $id;
// ...
/**
* #OneToMany(targetEntity="issueMessages", mappedBy="issue")
*/
private $messages;
// ...
public function __construct()
{
$this->messages = new Doctrine\Common\Collections\ArrayCollection();
}
}
/** #Entity */
class issueMessages
{
// ...
/**
* #ManyToOne(targetEntity="issue", inversedBy="messages")
* #JoinColumn(name="issue_id", referencedColumnName="id")
*/
private $issue;
// ...
}
If you using yml format for schema orm files than
first you need to write schema and mention oneToMany, manyToOne relationship with table fields & generate entity, repository class.
Than you can use join with two or more tables as below example:
Example of repository class file function:
----------------------------------------------------
public function getReportInfo($idUserDetail)
{
$query = $this->createQueryBuilder('UR')
->select("UR.report_period_start_date, UR.report_period_end_date")
->leftJoin('UR.UserReportDetail', 'URD')
->andWhere('UR.id_user_detail = :id')
->setParameter('id', $id)
->orderBy('UR.report_year', 'DESC')
->addOrderBy('UR.report_month', 'DESC')
->setMaxResults(1);
$resultArray = $query->getQuery()->getArrayResult();
return $resultArray;
}
You can call this function from controller action as below:
-------------------------------------------------------------
public function getUserDetailAction($idUserDetail)
{
$em = $this->getDoctrine()->getManager();
$userDetail = $em->getRepository(
'DemoBundle:UserDetail')
->getReportInfo($idUserDetail);
return $userDetail;
}
I hope this would be useful to you.
I think the problem reside in the DQL syntax (+ missing inverse relation?).
By writing this:
SELECT x, COUNT(im.id) FROM PanelBundle:Issue x
LEFT JOIN PanelBundle:IssueMessages im WITH x.id = im.idIssue
you are joining two "random" table based on the condition provided in the WITH clause. This should usually be ok, but it may confuse the Hydrator component.
In your case you should configure the OneToMany side of the relation in Issue entity, then write something like this:
SELECT x, COUNT(im.id) FROM PanelBundle:Issue x
LEFT JOIN x.issueMessages im
Hope it helps!

using querybuilder and DQL to pull specific attributes of entity

I have the following query:
$query = $em->createQueryBuilder()->select('p.id, p.name, p.price, p.cover')
->from("SiteMainBundle:Product", 'p')
->innerJoin('p.category', 'c')
->innerJoin('p.shop', 'shop')
;
And inside my product entity the cover is actually another entity, which looks like this:
/**
* #Accessor(getter="getCover")
*/
private $cover;
public function getCover()
{
if($this->pictures->count() > 0) {
return $this->pictures[0];
}
return new ProductPicture();
}
However when I do this it gives me an error:
"[Semantical Error] line 0, col 32 near 'cover FROM SiteMainBundle:Product': Error: Class Site\\MainBundle\\Entity\\Product has no field or association named cover"
I know this is because it is declared as private, but can I call the method getCover from the query builder?
If you want to load only specific fields, instead of the whole entity, you need to use the partial object syntax:
$em->createQueryBuilder()->select('p.{id,name,price,cover}')
This will select only the 4 specific columns you want.
It's not because of private visibility, but because cover is not defined as an entity field.
/**
* #ORM\Column(type="string")
*
* #Accessor(getter="getCover")
*/
private $cover;

ORM QueryBuilder with Entity subobjects

I have 2 entities: author and person.
In the author entity, there is a person field which is in fact the person object:
/**
* #ORM\ManyToOne(targetEntity="Person", inversedBy="submission_authors")
* #ORM\JoinColumn(name="person_id", referencedColumnName="id")
*/
protected $person;
Now, in the repository: AuthorRepository, I would like to search for some authors by their firstname. To do this, I need to access the person object for the corresponding author ans look on the person's firstname.
I tryed:
public function searchAuthors($q)
{
$authQB = $this->createQueryBuilder( 'a' )
->select('a')
->where("a.person.firstname LIKE '%".$q."%'");
return $authQB->getQuery()->getResult();
}
But the problem is that I am geting an error:
[Syntax Error] line 0, col 78: Error: Expected Doctrine\ORM\Query\Lexer::T_LIKE, got '.'
Coud you please help me on how to resolve that?
You'd have to access your person relation like this:
$authQB = $this->createQueryBuilder( 'a' )
->select('a')
->leftJoin('a.person', 'p')
//...
To learn a bit more about query builder and joint tables:
This SO post.
Try
$authQB = $this->createQueryBuilder( 'a' )
->select('a')
->innerJoin('a.person', 'p')
->where('p.firstname LIKE :myStuff')
->setParameter('myStuff', '%'.$q.'%');

Categories