Symfony Native Query - symfony

I work on a project under symfony, when I want to calculate the average product by month and year (date) and as doctrine doesn't include the Month or YEAR functions I used native sql but it didn't show the results, it return empty array.
If someone can help me,Thank you.
Repository:`
public function MonthEfficience()
{
$rsm = new ResultSetMappingBuilder($this->getEntityManager());
$rsm->addRootEntityFromClassMetadata('GP\PlatformBundle\Entity\Efficience', 'e');
$rsm->addJoinedEntityFromClassMetadata('GP\PlatformBundle\Entity\collectif', 'c', 'e', 'collectif', array('id' => 'collectif_id'));
$sql = 'SELECT (AVG(e.produit_real/e.produit_plan)*100) as moyenne,
MONTH(e.date) as mois, YEAR(e.date) as annee FROM efficience e, collectif c
where e.collectif_id=c.id group by mois, annee';
$query = $this->_em->createNativeQuery($sql, $rsm);
$resultats = $query->getResult();
return $resultats;
}
Controller:
public function effmonthAction()
{
$em = $this->getDoctrine()->getManager()
->getRepository('GPPlatformBundle:Efficience');
$efficiences = $em->MonthEfficience();
return $this->render('GPPlatformBundle:App:effmonth.html.twig',
array('efficiences'=>$efficiences));
}
Twig :
<table id="example" class="display" cellspacing="0" width="100%">
<thead>
<tr>
<th>Annee</th>
<th>Mois</th>
<th>Moyenne</th>
</tr>
</thead>
<tbody>
{% for efficiences in efficiences %}
<tr>
<td>{{ efficiences.produitplan }}</td>
<td>{{ efficiences.produitreal }}</td>
<td>{{ efficiences.produitplan }}</td>
</tr>
{% endfor %}
</tbody>
</table>
Resultats:
empty array

Related

Update only a single property of a given object

I have an entity called worker and each worker has a property called active which is boolean.
My twig is an index that shows the list of workers with active=true.
I have a button in front of each worker, when I press this button I want it to change that worker's active property to false.
The problem: I couldn't figure out how to change that value in the controller without making a form since I'm still an amateur when it comes to Symfony
Here's my twig:
<table id="file_export" class="table table-striped table-bordered">
<thead>
<tr>
<th>ID</th>
<th>First Name</th>
<th>Last name</th>
<th>Active</th>
<th>edit</th>
</tr>
</thead>
<tbody>
{% for worker in workers %}
<tr>
<td>{{ worker.id }}</td>
<td>{{ worker.Firstname }}</td>
<td>{{ woker.Lastname }}</td>
<td>{{ worker.active ? 'active' : 'inactive' }}</td>
<td>
<i class="fa fa-pencil"></i>
</td>
</tr>
{% endfor %}
</tbody>
</table>
and my controller (which doesn't work):
/**
* #Route("/{id}/edit", name="worker_edit", methods={"GET","POST"})
*/
public function edit(Request $request, Worker $worker): Response
{
if ($this->isCsrfTokenValid('edit'.$worker->getId(), $request->request->get('_token'))) {
$worker->setActive(false);
$entityManager = $this->getDoctrine()->getManager();
$entityManager->persist($worker);
$entityManager->flush();
}
return $this->redirectToRoute('index');
}
you actually have to add a csrf token to your path call:
path('worker_edit', {'id': worker.id, '_token': csrf_token('worker'~worker.id)})
or otherwise your check for the csrf token obviously cannot succeed.
however, since a link will trigger a GET request, you have to look into
$request->query->get('_token')
in the isCsrfTokenValid call.
As a hint: give your routes and actions semantically better names. Like ... "worker_deactivate", if it is used to deactivate a worker (which it apparently is). it's also quite common, to call the routed methods of a controller actionAction, so that would be deactivateAction.
If you want to make HTTP requests without reloading the web page, then you've to go for AJAX calls. A very simple implementation using fetch that doesn't require any additional packages (like jQuery) would look like this:
<script>
(function() {
document.getElementById({{worker.id}}).addEventListener('click', function(e) {
e.preventDefault();
fetch({{path('worker_edit', {'id': worker.id})}}, {method: 'POST'})
.then(function(response) {
// you can catch eventual errors here, and of course refresh your button or display a nice message..
});
});
})()
</script>
<table id="file_export" class="table table-striped table-bordered">
<thead>
<tr>
<th>ID</th>
<th>First Name</th>
<th>Last name</th>
<th>Active</th>
<th>edit</th>
</tr>
</thead>
<tbody>
{% for worker in workers %}
<tr>
<td>{{ worker.id }}</td>
<td>{{ worker.Firstname }}</td>
<td>{{ woker.Lastname }}</td>
<td>{{ worker.active ? 'active' : 'inactive' }}</td>
<td>
<i class="fa fa-pencil"></i>
</td>
</tr>
{% endfor %}
</tbody>
</table>
p.s: The javascript code above is not tested as I have to reproduce the twig and controller, but it could give you an idea on how to achieve the task.

Show multiple data from an Entity

I want to show the best 5 scores from each different Game I have. So I made this function :
public function records (){
$em = $this->getDoctrine()->getManager();
$games = $em->getRepository(Game::class)->findAll();
foreach($games as $g){
$records = new ArrayCollection;
$records = $em->getRepository(Game::class)->findAllRecords($g->getId());
}
return $this->render('game/records.html.twig', [
'games' => $games,
'records' => $records,
]);
}
Here is the repository function :
public function findAllRecords($id){
$qb = $this->createQueryBuilder('g');
$qb->select('g.name')
->innerJoin('g.Parties', 'p')
->innerJoin('p.playeds', 'y')
->innerJoin('y.joueur', 'j')
->addSelect('y.score')
->addSelect('j.nom, j.prenom')
->where('g.id = :id')
->setParameter('id', $id)
->orderBy('y.score', 'DESC')
->setMaxResults('5');
var_dump($qb->getDQL());
$query = $qb->getQuery();
return $query->getResult();
}
And finally the view :
{% for g in games %}
{{ g.name }}
<table class="table">
<tbody>
<tr>
<th>score</th>
</tr>
{% for r in records %}
<tr>
<td>{{ r.score }}</td>
</tr>
{% endfor %}
</tbody>
</table>
{% endfor %}
It doesn't completely works as I just get the data from the last game ID. How can I show the data for each game ?
foreach($games as $g){
$records = new ArrayCollection;
$records = $em->getRepository(Game::class)->findAllRecords($g->getId());
}
Here's your issue. This is always overwriting. You want to do something like:
$records = new ArrayCollection;
foreach($games as $g) {
$records[] = $em->......;
}
That should solve your issue

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

Symfony2 Doctrine+Twig query taking too long

I'm currently building a query to return a set of records between desired dates as it follows:
public function findBetweenDates(\Datetime $date1,\Datetime $date2)
{
$date1=$date1->setTime(07,00,00);
date_modify($date2,'+1 day');
$date2->setTime(06,59,00);
$qb = $this->getEntityManager()->createQueryBuilder()
->select('e')
->from("AppBundle:Movimento","e")
->andWhere('e.pesagem1 BETWEEN :from AND :to')
->setParameter('from', $date1 )
->setParameter('to', $date2)
->orderBy('e.id','DESC')
;
$result = $qb->getQuery()->getResult();
return $result;
}
the class Movimento has some ManyToOne connections as shown below:
class Movimento
{
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #ORM\ManyToOne(targetEntity="Service")
* #ORM\JoinColumn(name="service", referencedColumnName="id")
**/
private $service;
When i get the records and render them in twig:
{% for item in items %}
<tr>
<td>{{ item.id }} </td>
<td>{{ item.service.name }}</td>
//#MORE CODE BELOW //
by calling servico.name from another entity i get tons of non wanted queries as a result to display the name of the service instead of its id.
We are talking about something in the 6k range of records in every response.
I would like some help, if it's possible to optimize this query using the my query builder or should i remake the whole query more of a "SQL" example:
Select a.name, b.id
From service as a, movimento as b
Between bla bla bla
Any Help/suggestions are greatly appreciated.
EDIT 1
i changed my query builder after reading this post Symfony 2/Doctrine: How to lower the num of queries without losing the benefit of ORM?
I did reduce 175 queries to a single one
$qb = $this->createQueryBuilder('e')
->addSelect('service')->join('e.service','service')
->addSelect('motorista')->join('e.motorista','motorista')
->addSelect('residuo')->join('e.residuo','residuo')
// ->from("AppBundle:Movimento","e")
->andWhere('e.pesagem1 BETWEEN :from AND :to')
->setParameter('from', $date1 )
->setParameter('to', $date2)
->orderBy('e.id','DESC')
But still the page is taking around 8 seconds to load (its 6900 records) and after checking performance the response time for my new query is 177.79 ms, but my twig+ controller is taking the remaining 7.x seconds as it shows the pic
my controller is something really simple
public function getMovimentosAction(Request $request)
{
$startDate = $request->request->get('startDate');
$endDate = $request->request->get('endDate');
if (empty($startDate))
$startDate = date("Y-m-d") ;
if (empty($endDate))
$endDate = date("Y-m-d");
$em=$this->getDoctrine()->getRepository('AppBundle:Movimento');
$dados=$em->findBetweenDates(new \DateTime($startDate),new \DateTime($endDate));
return $this->render('AppBundle:Movimentos:logtable-movimento.html.twig', array(
'items' => $dados
));
}
and my twig just iterates over the rows and displays them on a table as i gave a partial example above.
Any help/suggestions would be greatly appreciated.
EDIT2
My view that is passed by ajax to be rendered as datatable.js
<table id="example" class="table table-striped table-bordered table-hover" cellspacing="0" width="100%">
<thead class="dataTableHeader">
<tr>
<th>Talão</th>
<th>Nº Talão</th>
<th>Motorista</th>
<th>Residuo</th>
<th>Serviço</th>
<th>Matricula</th>
<th>1º Pesagem</th>
<th>Peso Liquido</th>
<th>Fluxo</th>
<th>Circuito</th>
<th>Verificado</th>
<th></th>
</tr>
</thead>
<tfoot class="dataTableHeader">
<tr>
<th>Talão</th>
<th>Nº Talão</th>
<th>Motorista</th>
<th>Residuo</th>
<th>Serviço</th>
<th>Matricula</th>
<th>1º Pesagem</th>
<th>Liquido</th>
<th>Fluxo</th>
<th>Circuito</th>
<th>Verificado</th>
<th></th>
</tr>
</tfoot>
<tbody>
{% for item in items %}
<tr>
<td align="center"><a href="{{ path("_movimento_generate_pdf",{ id: item.id }) }}"> <i class="fa fa-print fa-2x" aria-hidden="true"></i>
</a></td>
<td>{{ item.id }} <a><i class="fa fa-eye" title="Visualizar Movimento" aria-hidden="true"></i></a>
</td>
<td>{{ item.motorista.idFuncionario }} - {{ item.motorista.nome }}</td>
<td>{{ item.residuo.nome }}</td>
<td>{{ item.servico.nome }}</td>
<td>{{ item.matricula }}</td>
<td>{{ item.pesagem1|date('Y-m-d h:m') }}</td>
<td>{{ item.liquido }} kg</td>
<td>{% if item.tipoMovimento == 1 %} Entrada {% else %} Saida {% endif %}</td>
<td>{{ item.circuito.code | default(" ") }}</td>
<td class="text-center">{% if item.enable==1 %}
<span style="color: transparent"> </span>
<i class="fa fa-circle" aria-hidden="true" style="color: green"></i>
{% else %}
<i class="fa fa-times" aria-hidden="true" style="color: red;"></i>
{% endif %}
</td>
<td class="text-center">
<a class="btn btn-default" href="{{ path('_movimentos_edit',{ 'id' : item.id}) }}">
<i class="fa fa-cog" title="Editar" aria-hidden="true"></i>
<span class="sr-only">Settings</span>
</a>
</td>
</tr>
{% endfor %}
</tbody>
</table>
and in my html
$("#submitButtonQuery").click(function(event){
event.preventDefault();
var l = Ladda.create(this);
l.toggle();
$.post( "/movimentos/getList",
$( "#formAjaxify" ).serialize())
.done(function(data)
{
$('#example').remove();
$("#tabelaLog").html(data);
oTable=$('#example').DataTable(
{
"scrollX": true,
responsive: true,
"language": {
"url": "http://cdn.datatables.net/plug-ins/1.10.11/i18n/Portuguese.json"
}
}
);
oTable.order( [ 0, 'desc' ] )
.draw();
})
.always(function(){
l.toggle()})
;
});
As long as you have a connection between 'Movimento' and 'Service' then for each 'movimento' that you get as a result a 'service' will be serialized together. This means that if you have a query that returns 100 'movimento' then together with it all 'service' objects (100) will be required to be fetched.
If you don't want to have the Service as an object in each item (AKA item.service.blahblah) then you need to have a more direct query.
if you do it with query builder then you will need something like:
$repository = $this->getDoctrine()
->getRepository('YourownBundle:Movimento'); //the main repo from which to get data
$query = $repository->createQueryBuilder('m') // query builder on repo
->join('m.service', 's') // join the second object to select from
->select('m.id') // select everything from m objet
->addSelect('s.name') // select everything from service (s) object
->where('e.pesagem1 BETWEEN :from AND :to')
->setParameter('from', $date1 )
->setParameter('to', $date2)
->orderBy('e.id','DESC')
the rest of your code should be as you have it... but then you don't have a serialized object but only the selects that you make (eg. m.id, s.name)

Which is best paginator for in doctrine symfony2?

I have written custom queries in my repository class and they returns arrays then I do some processing on those arrays then displays to twig.
So please suggest the best pagination method to apply paging on this custom queries resulting in arrays.
I am new to symfony2, does default paging will work and how? I mean what syntax, please provide example.
You should try Knp Paginator. It is simple and customizable.
Simple code example (Doctrine MongoDB ODM):
// Pay attention: query, not result.
$query = $this->getRepositoryOfferKind()->createQueryBuilder()
->field('is_private')->equals(false)
->field('is_deleted')->notEqual(true)
->sort('updated_at', 'DESC')->getQuery();
$paginator = $this->get('knp_paginator');
$pagination = $paginator->paginate($query, $request->get('page', 1), 20);
/* #var $pagination SlidingPagination */
$pagination->setUsedRoute('admin_offer_kind_index');
$pagination->setPageRange(10);
return array(
'objects' => $pagination,
);
And twig:
<table>
<thead>
<tr>
<th>Title</th>
</tr>
</thead>
<tbody>
{% for object in objects %}
<tr>
<td>
{{ object.title }}
</td>
</tr>
{% else %}
<tr>
<td>No data</td>
</tr>
{% endfor %}
</tbody>
<tfoot>
<tr>
<td>
{{ knp_pagination_render(objects) }}
</td>
</tr>
</tfoot>
</table>
You can try this native solution
public function getPagination($someValue, int $page = 1, $limit = 20, $sort = "createdAt", $sortOrder = 'DESC')
{
$qb = $this->createQueryBuilder();
$qb->field('some_filed')->equals($someValue);
// and so on
return $qb->sort($sort, $sortOrder)
->skip(($page - 1) * $limit)
->limit($limit)
->getQuery()->toArray();
}

Resources