ORM QueryBuilder with Entity subobjects - php

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.'%');

Related

Query builder cannot associate with category entity

I have 2 entities, MyItem and MyItemCategory. When I try to create a query with query builder I get the following error:
Error: Class App\Entity\MyItem has no
field or association named myitem_category_id (500 Internal Server Error)
This is the where part of my query builder:
$queryBuilder = $this->getDoctrine()
->getRepository('App\Entity\MyItem')
->createQueryBuilder('m');
// adds where for category_id:
$queryBuilder->where('m.myitem_category_id = :category_id')->setParameter('category_id',$category_id);
Here're first entities:
/**
* #ManyToOne(targetEntity="MyItemCategory")
* #JoinColumn(name="myitem_category_id", referencedColumnName="id")
*/
private $myItemCategory;
...and my category entity:
/**
* #ORM\OneToMany(targetEntity="MyItem", mappedBy="myItemCategory")
*/
private $myItemCategories;
The querybuilder actually doesn't care about database fields, but instead uses the object mapper, so there is no field my_item_category_id on your Entity, but instead a field myItemCategory
So, you can either do:
$querybuilder
->where('m.myItemCategory = :category')
->setParameter('category', $category) // <-- an actual MyItemCategory object
or you can join it in and check for the id:
$querybuilder
->leftJoin('m.myItemCategory', 'mic')
->where('mic.id = :micid')
->setParameter('micid', $category_id)
(I actually don't know if m.myItemCategory.id = :micid might work ........ you could try ;o))

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

Symfony2 - Doctrine - Filtering arrayCollection to get only one object

I am new in Doctrine. I have entities and associations works fine.
My issue:
I have three entities User, Comment, Comment_status.
Comment is connected to main table Finding but this doesnt matter in this case.
Association: Comment <---1:n---> Comment_status <---n:1---> User
That should handle relation between users wathing list of comments and setting like/dislike for every of them.
I am using TWIG to show a list of comments on the page and thats enough for all data like "date", "like count" etc but if I want to get a comment_status for specified user (loged in wathing a page) I dont know how to get this.
if I return array to TWIG "comments" , then loop it for every one of them as "comment" i can access using {{ comment.content }} to data but with comment_status I have no idea bacause it is an array and I just want one element of this array which match to logged in user ID.
I suppose I have to make some DQL ask to DB but have no idea how to do this.
I read some about Criteria in Doctrine but still no idea how to use it.
Can anybody give an step by step solution?
parts of my code:
Controller
> $comments = $em->getRepository('MySpecialBundle:Comment')
> ->findBy(array('finding' => $finding));
>
> return array('comments' => $comments );
Comment entity (part which create association)
> /** * #ORM\OneToMany(targetEntity="Comment_status",
> mappedBy="comment", cascade={"persist", "remove"}, orphanRemoval=true)
> */ protected $commentStatuses;
Comment_status entity (part which create association)
> /** * #ORM\ManyToOne(targetEntity="Comment",
> inversedBy="commentStatuses") * #ORM\JoinColumn(name="comment_id",
> referencedColumnName="id", nullable=false) */ protected $comment;
/**
> * #ORM\ManyToOne(targetEntity="My\MainBundle\Entity\User",
> inversedBy="commentStatuses") * #ORM\JoinColumn(name="user_id",
> referencedColumnName="id", nullable=false) */ protected $user;
User entity (part which create association)
/**
* #ORM\OneToMany(targetEntity="My\SpecialBundle\Entity\Comment_status", mappedBy="user", cascade={"persist"})
*/
protected $commentStatuses;
I want to have a way to get (instead an array collection of all "commentStatuses") only one object which contains an user id of user logged in.
Thank you for help.
You can select and hydrate only the entities matching your criteria in a doctrine querybuilder query. This way you get an already filtered collection. E.g. Something like:
$qb = $this->getEntitymanager()->createQueryBuilder();
$user = $qb->select('u, cs, c')
->from('YourBundle:User', 'u')
->join('u.commentStatuses', 'cs')
->join('cs.comment', 'c')
->where($qb->expr()->eq('u', ':user')
->setParameter('user', $yourLoggedInUser)
->getQuery()->getResult();
This will get you your user and comments/statuses only applicable to that user.
Thanks to #Richard I found a way to solve this.
The right answer is here:
$qb = $this->getEntitymanager()->createQueryBuilder();
$user = $qb->select('c, cs')
->from('MyBundle:Comment', 'c')
->leftjoin('c.commentStatuses', 'cs', 'WITH', 'cs.user = :user')
->setParameter('user', $myLoggedInUser)
->getQuery()->getResult();
Leftjoin - gives all records from Comment not only that contains commentStatus
WITH - choose only right commentStatuses
If I would use #Richard version with second join and WHERE it would show only Comments where user and commentStatus exists.
Anyway thans for help

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;

Categories