Flatten array structure in Doctrine QueryBuilder JOIN query in Symfony 3.4 - symfony

I have the following custom query in my PersonRepository in my Symfony 3 application. It joins the Person entity with the Log entity where I can grab the created time of the Person.
public function findAllPlayers()
{
$qb = $this->getEntityManager()->createQueryBuilder('p');
$qb
->select('p AS playerInfo', 'al.time AS createdTime')
->from('AppBundle:Player', 'p')
->join('AppBundle:Log', 'al', Expr\Join::WITH, $qb->expr()->eq('al.player', 'p.id'))
->where(
$qb->expr()->andX(
$qb->expr()->eq('p.type', ':type'),
$qb->expr()->eq('al.type', ':al_type')
)
)
->setParameter('type', 'midfielder')
->setParameter('al_type', 'log.player.created');
return $qb->getQuery()->getResult(Query::HYDRATE_ARRAY);
}
When I feed this through to my Twig template i'm left with this awkward index which i'd like to eliminate if possible.
{% for index, player in players %}
<tr>
<td>{{ player.playerInfo.playerShortname }}</td>
</tr>
{% endfor %}
Is there a way I can "flatten", for want of a better word, the structure to remove the superfluous index from the returned structure. It works, but it's a bit messy and the index is not needed or used anywhere.

Your twig template doesn't use index variable so you can feel free to remove it:
{% for player in players %}
<tr>
<td>{{ player.playerInfo.playerShortname }}</td>
</tr>
{% endfor %}

If you are looking solution for rendering multiple users:-
You can use the following snippet. It is not mandatory to use index in the for loop. You can refer the first example available in https://twig.symfony.com/doc/2.x/tags/for.html
{% for player in players %}
<tr>
<td>{{ player.playerInfo.playerShortname }}</td>
</tr>
{% endfor %}
If you are looking for a solution to render just one result:-
There is a method - "getOneOrNullResult". But, it throws an exception if the no. of rows are more than one. So, apply limit = 1 in the DQL and use the method "getOneOrNullResult" instead of "getResult"
To limit the no. of limits, you can use the method - "setMaxResults()"
Your method would like below
public function findAllPlayers()
{
$qb = $this->getEntityManager()->createQueryBuilder('p');
$qb
->select('p AS playerInfo', 'al.time AS createdTime')
->from('AppBundle:Player', 'p')
->join('AppBundle:Log', 'al', Expr\Join::WITH, $qb->expr()->eq('al.player', 'p.id'))
->where(
$qb->expr()->andX(
$qb->expr()->eq('p.type', ':type'),
$qb->expr()->eq('al.type', ':al_type')
)
)
->setParameter('type', 'midfielder')
->setParameter('al_type', 'log.player.created');
return $qb->getQuery()->setMaxResults(1)->getOneOrNullResult(Query::HYDRATE_ARRAY);
}
Note:-
But, I don't feel joining log to get the created_time is a ideal solution since that can cause serious performance issues as the log table grows. It is ideal to store the created_time in the table if there is a business around it.

Related

Translate doctrine query from default Symfony 3 translation file

In my AdRepository i'm getting an list of list conditions of the product. These have names like 'NEW', 'GOOD'... I use the following simple query to get the list:
$query = $this->createQueryBuilder('ad')
->select('ad.condition as name')
->getQuery()
->getResult();
Now I want to translate the name field from the standard Symfony translation files, so not from the database. What is the best way to do this?
I'm not sure if thats the best way to do it, but you could (I assume you're using Twig) use an if and give the translation variable depending on the condition name.
{% if condition.name === "GOOD" %}
{{ 'condition.good'|trans }}
{% else %}
...
{% endif %}
Of course thats a lot of ifs, if you have many conditions.

Symfony2, createQueryBuilder count one to many relationship

I have 2 entities linked by a one to many relation.
Recruitment and Candidat
You may have many candidats for one recruitment.
I want to list all recruitment and count how many candidat each recruitment has.
I use the recruitment repository and put the code:
public function myFindAllRecruitment()
{
$qb = $this->createQueryBuilder('r');
$qb->select('r');
$qb->Join('r.candidat', 'c');
$qb->addSelect("COUNT(c.id) as candidatCount");
$qb->groupBy('r.id');
$qb->orderBy('r.id', 'DESC');
return $qb
->getQuery()
->getResult()
;
}
In my RecruitmentController I have:
$listRecruitment = $repository->myFindAllRecruitment();
In my TWIG view something like:
{% for recruitment in listRecruitment %}
<tr>
{#(this is line 48)#}<td>{{ recruitment.id }}</td>
<td>{{ recruitment.titleFr }}</td>
<td>{{ recruitment.locationFr }}</td>......
And I get this error:
"Key "id" for array with keys "0, candidatCount" does not exist in MyBundle:Recruitment:index.html.twig at line 48"
If someone knows what wrong with my query it will be nice.
Thank you
So you have this Candidat entity like this
......
/**
* #ORM\ManyToOne(targetEntity="Recruitment", inversedBy="candidates")
*/
$recruitment;
....
Then your query should look like this:
public function myFindAllRecruitment()
{
$qb = $this->createQueryBuilder('r');
$qb->select('r');
->addSelect('(SELECT count(c) FROM PATHTO\Bundle\Entity\Candidat as c WHERE c.rectuitment = r.id group by c.rectuitment) as count)');
->orderBy('r.id', 'DESC');
return $qb->getQuery()->getResult();
}
you will get output like this:
[
0 => [
'rectuitment' => ...Object,
'count'=> ...,
...
]
Ok, thank you, I found the solution.
It was in the TWIG render:
Objects were accessible with [0] and [1] or ['candidatCount'] for the the count
Code exemple:
{% for recruitment in listRecruitment %}
<tr>
<td>{{ recruitment[0].id }}</td>
<td>{{ recruitment[0].titleFr }}</td>
<td>{{ recruitment[0].locationFr }}</td>
<td>{{ recruitment[0].typePoste }}</td>
<td>{{ recruitment[0].dateins|date('Y-m-d') | localizeddate('full', 'none') }}</td>
<td>
{{ recruitment['candidatCount'] }}........
And the query was good.
Thank you

How to implement two tables intersection on Doctrine2 Symfony2

Im pretty new on all this stuff so please excuse my foolness.
I have two tables, clients and orders.
I need to show on a template all clients with their total orders amount for each.
According to MCV model, I just want the controller to call a method of the Clients repository that returns an array with all the data ready for sending it to the template.
But I having problems with implementing this.
So in summary I have:
Entities:
Clients.php: with client.id
Orders.php: with a manyToOne relatioonship to clients, and a amount property.
DefaultController.php
public function indexAction()
{
$em = $this->getDoctrine()->getManager();
$clientsWithTotals= $em->getRepository('ACMEAppBundle:Client')->getClientsWithTotals();
return $this->render('ACMEAppBundle:Default:index.html.twig',$clientsWithTotals);
}
ClientRepository.php:
class ClientRepositoryextends EntityRepository
{
public function getClientsWithTotals()
{
???
}
}
index.html.twig
{% extends '::base.html.twig' %}
{% block content %}
<table>
<thead>
<tr>
<th>Id</th>
<th>Total</th>
</tr>
</thead>
<tbody>
{% for entity in entities %}
<tr>
<td><{{ entity.id }}</td>
<td>{{ entity.total }}</td>
</tr>
{% endfor %}
</tbody>
</table>
{% endblock %}
Many thanks indeed
First off the template is using a variable which hasn't been defined (entities):
return $this->render(
'ACMEAppBundle:Default:index.html.twig',
array(
'entities' => $clientsWithTotals
)
);
Implementing the Repository:
class ClientRepository extends EntityRepository
{
public function getClientsWithTotals()
{
$this
->createQueryBuilder('client')
->join('client.orders', 'order');
}
}
As long as you have the oneToMany association correctly defined from Client to Orders and it's on the $client->orders property then the join will know that it has to filter out only the orders associated with a particular user.
Implementing the totals virtual property:
Your Client object will have no way of knowing what the totals are unless you do one of the following:
select the value from the DB using an aggregation function like SUM(orders.total) AS clientTotals.
select the orders associated with a client from the DB (Doctrine2 will hydrate them into $client->orders) and then using a foreach loop to sum up all the values.
As described here.
Ex. for #1:
You can construct a DQL query such as:
SELECT
client,
SUM(orders.total) as clientTotal
FROM ACMEAppBundle:Client AS client
JOIN client.orders AS orders;
The above query would normally return an array containing a all of the rows in the clients table in the following format:
array(
'client' => $client // an ACMEAppBundle:Client entity object
'clientTotals' => $clientTotals // an integer value representing the aggregate sum
);
Implementing a custom hydrator will allow you to decide what to do with the raw array data which is returned by the DB query.
Ex. for #2:
class Client
{
...
public function getTotals()
{
$this->totals = 0;
foreach ($this->getOrders as $order) {
$this->totals += $order->getTotal();
}
return $clientTotals;
}
...
}

Using doctrine database object in a template

I am new to Symfony and am finally beginning to understand how to query a database using a Doctrine. However, I am lost as far understanding how to use the database object content in a Twig template.
Lets say my database object contains product Id's, names, prices, for 50 different products. After I am done querying the database in the controller, I do the following, to pass the database object into the Twig template:
public function searchAction($word)
{
//query database using the $word slug and prepare database object accordingly
$dataObject; // contains query results
return $this->render('GreatBundle:Default:search.html.twig', array('word' => $word));
}
This is where I am stuck. Now I have a Twig template, I would like to pass the DB object from the controller and then print out the database data in my Twig template.
I appreciate any suggestions as to how I can accomplish this.
Many thanks in advance!
I'll respond with an example (more easier for me to explain)
You want to search something with a slug (the var $word in your example). Let's say you want to find a article with that.
So your controller :
public function searchAction($word)
{
//query database using the $word slug and prepare database object accordingly
// Search the list of articles with the slug "$word" in your model
$articleRepository = $this->getDoctrine()->getRepositoy('GreatBundle:Article');
$dataObject = $articleRepository->findBySlug($word);
// So the result is in $dataObject and to print the result in your twig, your pass the var in your template
return $this->render('GreatBundle:Default:search.html.twig', array('result' => $dataObject));
}
The twig template 'GreatBundle:Default:search.html.twig'
{% for item in result %}
{{ item.title }} : {{ item.content }}
{% endfor %}
Just look the second example in the Symfony2 Book (Sf2 Book - templating), you have to use the function "for" to parse your object (like an array in php !)
Example in your twig template :
{% for item in word %}
{{ item.id }} - {{ item.name }} - {{ item.description }}{# etc... #}<br>
{% else %}
<h2>Aoutch ! No data !</h2>
{% endfor %}
Ah, and it's not the good var in your render method (but it's was for your example !)
public function searchAction($word)
{
//query database using the $word slug and prepare database object accordingly
$dataObject; // contains query results
return $this->render('GreatBundle:Default:search.html.twig', array('word' => $dataObject));
}

How do I output data from a related object in a Twig template?

I'm still very new to Symfony2 so please go easy on me. I'm trying to loop through a table of flights (for a flight ticket booking system), which have several related fields, such as airline, and airport. I'm using the following method in my custom repository:
public function getAllFlights($limit = 100)
{
$dql = 'SELECT f FROM Flightcase\BookingBundle\Entity\Flight f';
$query = $this->getEntityManager()->createQuery($dql);
$query->setMaxResults($limit);
return $query->getResult();
}
and the getAllFlights() is being passed to my Twig template like so:
$flights = $em->getRepository('FlightcaseBookingBundle:Flight')->getAllFlights();
return $this->render('FlightcaseBookingBundle:Flight:list.html.twig', array('flights' => $flights));
And the Twig template is simply looping through the items inside the $flights collection like this:
{% for flight in flights %}
<tr>
<td>{{ flight.airline }}</td>
<td>{{ flight.origin }}</td>
<td>{{ flight.destination }}</td>
<td>{{ flight.dateFrom }}</td>
<td>{{ flight.timeFrom }}</td>
<td>{{ flight.dateTo }}</td>
<td>{{ flight.timeTo }}</td>
</tr>
{% endfor %}
But I get a ugly, cryptic exception telling me "Object of class Proxies\FlightcaseBookingBundleEntityAirlineProxy could not be converted to string" which leads me to believe I need to fetch a specific property inside the Airline object such as the IATA code to output as string. But how can I access the $airline->getIataCode() inside the Twig template? Or is there a way in my repository to convert the related objects into strings?
I am assuming that Airline is a separate entity, which has an association to the Flight entity in Doctrine. Something like:
class Airline
{
private $id;
private $name;
private $flights;
...
}
Is that correct? If so, then that's the reason you're seeing that specific error. You're giving Twig an object, and telling it to print it out... but what does that mean, exactly?
Let's assume that your class looks like the above, and you're just trying to print out the name of the Airline.
You could do one of two things:
First, you could give your object a toString() method:
class Airline
{
public function toString()
{
return $this->getName();
}
}
Alternatively, you can give Twig something scalar to work with: Replace {{ flight.airline }} with {{ flight.airline.name }}.
Edit:
Just saw that your Airline object has a property called $IataCode. In that case, you'd render it in Twig using {{ flight.airline.IataCode }}.

Resources