include the related entities values in the result of a doctrine query - php

I'm doing a Doctrine query, and the results include the properties of the queried entity, but not it doesn't "follow" and fetch the values of the related entities.
E.g. I have this query inside of the OfertaRepository:
$query = $this->createQueryBuilder('o');
$query->select('o');
$query->leftJoin(Pais::class, 'p', 'with', 'o.idPais = p.id');
$query->leftJoin( Contrato::class, 'c', 'with', 'o.idTipoContrato = c.id');
$query->andWhere('p.nombreCorto = :pais');
$query->andWhere('o.activa = 1');
$query->andWhere('o.eliminado is NULL');
$query->andWhere('o.caducidad > :hoy');
$query->setParameter('pais', $pais)->setParameter('hoy', new \DateTime());
return $query->getQuery()->getArrayResult();
The entity Oferta has:
/**
* #var \Application\Entity\Pais
*
* #ORM\ManyToOne(targetEntity="Application\Entity\Pais")
* #ORM\JoinColumns({
* #ORM\JoinColumn(name="id_pais", referencedColumnName="id", nullable=true)
* })
*/
private $idPais;
/**
* #var \Application\Entity\TipoContrato
*
* #ORM\ManyToOne(targetEntity="Application\Entity\TipoContrato")
* #ORM\JoinColumns({
* #ORM\JoinColumn(name="id_tipo_contrato", referencedColumnName="id", nullable=true)
* })
*/
private $idTipoContrato;
All referenced tables ( tipo_contrato, pais, etc) exist and the relationships work for filtering data on queries, for example.
But my results from the $query->getQuery()->getArrayResult() expression do not include data from these relations.
It wont even include the ids from this fields. E.g., for any one record it will only include these fields:
id
alt
nombre"
nombreAlt
descripcion
poblacion
vacantes
horarioIni
horarioFin
url
tag
creado
caducidad
modificado
eliminado
activa
Which are all valid fields, but do not include any fields that are a many-to-one relationship.
How can I include these values for this query, and for this query only? E.g. without changing the entities definition so all queries are affected?

Just looking at: $query->select('o'); I'd say (think of it in terms of SQL) that is all you are selecting. Get some commas in there. I'd try something like this:
$query->select('o', 'p', 'c');
What I was trying to do first time should have been an array. Not a comma delimited string like I showed. See source:
$selects = is_array($select) ? $select : func_get_args();
So either an array or a list of arguments should work.
Update:
Maybe you are just missing the from? Totally missed that. Here I'll share some code that I know works. Maybe you will see something. Pretty sure you just need a from clause and the multiple obj references on the select part.
$qb = $this->_em->createQueryBuilder();
$qb->select('aimg', 'ai');
$qb->from('OAS\Entity\AuctionImage', 'aimg');
$qb->leftJoin('aimg.auctionItem', 'ai', \Doctrine\ORM\Query\Expr\Join::WITH, 'ai.lotNumber = aimg.lotNumber AND ai.auction = aimg.auction');
$qb->where('aimg.auction = :auction')->setParameter('auction', $this->_auction);
$qb->andWhere('aimg.isDeleted = :boolFalse')->setParameter('boolFalse', 0);
$qb->andWhere('aimg.auctionItem IS NULL');
$qb->orderBy('aimg.lotNumber', 'asc');
$results = $qb->getQuery()->getResult();

Related

Symonfy2, Doctrine: duplicate, default, or ignore DiscriminatorMap?

How do I enable duplicate Discriminator Maps, allow a "default" mapping, or completely ignore Discriminator mappings when running a DQL query?
Setup:
// DQL Query:
$this->createQueryBuilder('s')
->select(['s.serverId', 'p.projectName'])
->leftJoin('s.serverServices', 'ss')
->leftJoin('ss.serverServiceProjects', 'ps')
->leftJoin('ps.project', 'p')
->getQuery()
->getArrayResult()
// Mapping
/**
* #ORM\InheritanceType("SINGLE_TABLE")
* #ORM\DiscriminatorColumn(name="service_type_id", type="integer")
* #ORM\DiscriminatorMap({
* "1" = "AppBundle\Entity\ServerService",
* "2" = "AppBundle\Entity\ServerService",
* [...]
* "12" = "AppBundle\Entity\Service\SubService",
* "" = "AppBundle\Entity\ServerService"
* })
*/
The join portion that's generated:
LEFT JOIN server_services s2_ ON s0_.server_id = s2_.server_id AND s2_.service_type_id IN ('1', '12')
This is entirely incorrect. I have an entire mapping of 1 - 12. And an "" empty string match. If a row does not have a known mapping (in my code) OR if it's empty, it should be set to the default base ServerService.
I did find this post: Leave out discriminator part of Doctrine' generated SQL
However, it's back in 2014, made mention of a difference in version, and does not compensate for duplicate, or default Mappings.
EDIT: For the record, I tried the ignore route -- it didn't work.
$q->setHint(\Doctrine\ORM\Query::HINT_CUSTOM_OUTPUT_WALKER, MySqlWalker::class)
->setHint(MySqlWalker::IGNORE_DISCRIMINATION, array('ss'));
Still returns the above SQL Joint statement. It does call MySqlWalker; it does go into the setInheritanceType() call, but it does not ignore the Discriminator mapping. :/
Found an answer to one option: Ignoring
In Doctrine 2.2.x, my statements were in the JOIN, not WHERE clause. Using the example in Leave out discriminator part of Doctrine' generated SQL, I had to overload the ->walkJoin() method to get ignore to work properly:
/**
* {#inheritdoc}
*/
public function walkJoin($join)
{
$this->checkForHint();
return parent::walkJoin($join);
}
protected function checkForHint()
{
$ignoreDescription = $this->getQuery()->getHint(self::IGNORE_DISCRIMINATION);
if ($ignoreDescription) {
foreach ($this->getQueryComponents() as $k => $component) {
if (in_array($k, $ignoreDescription)) {
/** #var $meta ClassMetadata */
$meta = $component['metadata'];
$meta->setInheritanceType(ClassMetadata::INHERITANCE_TYPE_NONE);
}
}
}
}
Still have not resolved Duplicate or Default discriminator values...

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!

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

Symfony2 and Doctrine: how to sort entity collection by dependant entity field

In my sf2 project I access entity collection by calling:
$user_payment_info_datas = $user->getUserPaymentInfoDatas();
In the User entity there is:
/**
* #ORM\OneToMany(targetEntity="UserPaymentInfoData", mappedBy="user")
* #ORM\OrderBy({"payment_info" = "ASC", "payment_info_data" = "ASC"})
*/
private $user_payment_info_datas;
So it's 1:n relation and user has many UserPaymentInfoData's. However, there is another entity called PaymentInfoData that contains the actually values for UserPaymentInfoData's. So the relation is
User -> UserPaymentInfoData -> PaymentInfoData.
So in terms of annotations in UserPaymentInfoData it is:
/**
* #ORM\ManyToOne(targetEntity="PaymentInfoData", inversedBy="user_payment_info_datas")
* #ORM\JoinColumn(name="payment_info_id", referencedColumnName="id")
* #ORM\OrderBy({"title"="ASC"})
*/
private $payment_info_data;
I need to sort the collection returned by
$user_payment_info_datas = $user->getUserPaymentInfoDatas();
ascending by a field from PaymentInfoData (let's call it 'title') and NOT UserPaymentInfoData.
Can I do this with Doctrine annotations? Or without writing DQL?
I know I can do it with:
$user_payment_info_datas = $em->getRepository('STMainBundle:UserPaymentInfoData')
->createQueryBuilder('upid')
->innerJoin ('upid.payment_info_data', 'pid')
->where('upid.user = :user')
->addOrderBy('upid.payment_info', 'ASC')
->addOrderBy('pid.title', 'ASC')
->setParameter('user', $user)
->getQuery()
->getResult();
but the question is, whether it's possible to stay with only annotations as I need to fix it in a few places and it would be convenient to just change annotations and not create query builder in two places.

Symfony2/Doctrine joined query

I need to create a simple query that produces a result set of a database entry plus the username of the person that posted it.
I've tried to setup the associations properly but I'm not sure if that's right either. I'm finding the whole idea of using these small string identifiers quite confusing. Surely there must be a simpler way of doing a join?
My two entities:
class Users
{
// ...
/**
* #ORM\Column(type="string")
* #ORM\OneToMany(targetEntity="Titles", mappedBy="addedBy")
*/
protected $username;
// ..
}
and
class Titles
{
// ....
/**
* #ORM\Column(type="string")
* #ORM\ManyToOne(targetEntity="Users", inversedBy="username")
*/
protected $addedBy;
// ....
}
with the following in the controller:
$titles = $em->createQueryBuilder()
->select('t.*', 'u.*')
->from('dvdLoggerdvdBundle:Titles', 't')
->leftJoin('t.addedBy', 'u')
->addOrderBy('t.title', 'DESC')
->getQuery()
->getResult();
I'm getting the following error:
[Semantical Error] line 0, col 69 near 'u ORDER BY t.title': Error: Class
dvdLogger\dvdBundle\Entity\Titles has no association named addedBy `
Update 1
I made all the changes suggested by Tom and did lots of reading!
It appears that in order to overcome the lazy loading feature I need to carry out a leftJoin. I have rewritten my query as follows:
public function getAllTitles()
{
// view all records in db
$titles = $this->createQueryBuilder('t')
->select('t, u')
->leftJoin('t.addedBy', 'u')
->addOrderBy('t.title', 'DESC');
return $titles->getQuery()->getResult();
}
I am getting a result set, but the addedBy is returning NULL when I dump the result set. As far as I'm aware shouldn't this pull the associated field in from the other table?
Best practice is to reference the entity by its id, you are trying to reference it using the username. The inversed field should also be a specific field not an existing one that holds data. And keep it mind this field is optional and defines the associations as bidirectional, for the specified use case you don't actually need it as you are joining from the Titles entity. I would advice reading the doc here http://symfony.com/doc/current/book/doctrine.html#entity-relationships-associations as well as here http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/association-mapping.html
Bidirectional association (w/ inversed field)
First get rid of that line:
#ORM\Column(type="string")
In your $addedBy annotations and change inverseBy="username" to inversedBy="titles" (note the typo)
You optionaly could add
#ORM\JoinColumn(name="user_id", referencedColumnName="id")
Then in your Users Entity add
/**
*
* #ORM\OneToMany(targetEntity="Titles", mappedBy="addedBy")
*/
protected $titles;
And get rid of
* #ORM\OneToMany(targetEntity="Titles", mappedBy="addedBy")
In your $username annotations
Last make sure you update the database schema
Then your query should return the expected result.
Unidirectional association (w/out inversed field)
Get rid of
* #ORM\OneToMany(targetEntity="Titles", mappedBy="addedBy")
In your $username annotations
Then get rid of that line in your $addedBy annotations:
#ORM\Column(type="string")
As well as inverseBy="username"
You optionaly could add
#ORM\JoinColumn(name="user_id", referencedColumnName="id")
Last make sure you update the database schema
Then your query should return the expected result.

Categories