Symfony: get result of both tables - php

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;
}

Related

Doctrine queryBuilder: return object not array

I have this query created with doctrine querybuilder, the return i get is an array of arrays.
I would like to get a return that is an array of objects, is this possible?
I know that normally Doctrine returns objects of an entity, bit since i have an inner join to get the name from another table it returns arrays.
Thanks in advance.
$qb->select('u', 'h.name')
->from('AppBundle:UserHose', 'u')
->innerJoin('AppBundle:Hose', 'h', 'WITH', 'u.hoseId = h.id')
->where('u.userId = :userId')
->orderBy('u.id', 'DESC')
->setParameter('userId', $userId);
return $qb->getQuery()->getResult();
you can use this:
return $qb->getQuery()->getResult(Query::HYDRATE_ARRAY);
Or this:
return $qb->getQuery()->getArrayResult();
This isn't possible this way. In other words, you are doing it wrong.
You are telling Doctrine to return a collection of collections containing an entity and a string so this is what you get. Doctrine won't make an object out of that since it does not know how to hydrate such result.
[
[entity, string],
[entity, string],
....
]
If you wish to receive a collection of objects only, you would need to create a new entity that has both fields (related entity and a string property), then use a ResultSet mapping to hydrate that.
if you want array of objects you have to set relation betwen Entities, and create a query by the owning side of relation.
example:
Tourney entity , Invite entity
Invite
/**
* #ORM\ManyToOne(targetEntity="Tourney", inversedBy="invites")
*/
protected $tourneys;
Tourney
/**
* #ORM\OneToMany(targetEntity="Invite", mappedBy="tourneys", cascade={"persist", "remove"})
* #ORM\JoinColumn(nullable=true, onDelete="CASCADE")
*/
protected $invites;
now you have to make query to the owning side of relation (Invite)
and it will be holding all your join object data with Tourneys in field $invites
and it gives you array of objects. based on your query
remeber of setter $invites as setInvites(Tourney $invites) and by inverse side of relation setTourneys(Invite $tourneys)
Just add \Doctrine\ORM\Query::HYDRATE_ARRAY on getResult() like this
$qb->select('u', 'h.name')
->from('AppBundle:UserHose', 'u')
->innerJoin('AppBundle:Hose', 'h', 'WITH', 'u.hoseId = h.id')
->where('u.userId = :userId')
->orderBy('u.id', 'DESC')
->setParameter('userId', $userId);
return $qb->getQuery()->getResult(\Doctrine\ORM\Query::HYDRATE_ARRAY);

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();

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

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.

Doctrine Inheritance mapping, selecting by type and subtype parameter

I have a superclass LibraryCommon and 4 subtypes: Library, PurchasedLibrary, PurchasableLibrary and PersonalLibrary.
Library is a simple extension of LibraryCommon with no additional fields, PurchasedLibrary has a user entity. In one instance, I want all Libraries and all PurchasedLibraries that belong to a certain user.
So I created a method in the LibraryCommonRepository:
public function findLibraries($user)
{
return $this->createQueryBuilder('l')
->where('l INSTANCE OF Library')
->orWhere('l INSTANCE OF PurchasedLibrary AND l.user = :user')
->setParameter(':user', $user)
->getQuery()
->getResult()
;
}
However, this errors out as [Semantical Error] line 0, col 182 near 'user = :user': Error: Class LibraryCommon has no field or association named user.
Am I missing something, or do I really need to join two seperate queries to get the result I want?
Additionally, if I do not provide the user and do like this:
public function findLibraries()
{
return $this->createQueryBuilder('l')
->where('l INSTANCE OF Library OR l INSTANCE OF PurchasedLibrary')
->getQuery()
->getResult()
;
}
The generated query looks this:
SELECT
...
FROM
library l0_
WHERE
(
l0_.type IN ('library')
OR l0_.type IN ('purchased')
)
AND l0_.type IN (
'library', 'personal', 'purchased',
'purchasable'
)
Is there any way to make the query just do WHERE l0_.type IN ('library', 'purchased')
You might want to add the User mapping to your LibraryCommon, just the private attribute, default to null, and keep getter and setter in your PurchasedLibrary.

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