Using Count and Group By with Doctrine2 ,Symfony2 and AliDataBundle - symfony

I have an entity with three fields (id, user, location).
I want to display the number of users by location.
With SQL it's very easy, but I have an error when i want to use DQL and AliDatatableBundle
$qb = $this->createQueryBuilder('a')
->select("count(*) as myCount, a.location")->groupBy('a.location');
'myCount' does not point to a Class.
I've seen that AliDataBundle can use alias. It's should works.
Any Ideas ?

You can try this one in your entity repository:
public function getSth() {
$query = 'your sql query here';
$em = $this->getEntityManager();
$connection = $em->getConnection();
$stmt = $connection->prepare($query);
$stmt->execute();
return $stmt->fetchAll();
}
Or by query builder:
public function getSth() {
$qb = $this->createQueryBuilder();
$qb->select('count(a.id)');
$qb->from('YourBundleBundle:Entity','a');
$qb->groupBy('a.location');
$query = $qb->getQuery();
return $query->->execute();
}

Related

JOIN DQL SYMFONY

i want to count how many baby for a parent
class abonnementRepository extends \Doctrine\ORM\EntityRepository
{
public function SumEnfantDQL($id)
{
$entityManager = $this->getEntityManager();
$query = $entityManager->createQueryBuilder();
$query->select('sum(o.id) AS somme');
$query->from('AppBundle:enfant', 'o');
$query->join('AppBundle:User','p')->where('p.id = :id');
$rez = $query->getQuery()->getResult();
return $rez;
}
}
the entity enfant had matricul_prt and entity user had enfant_id and $id parameter is the parent id
i don't know how it work with join or innerJoin .So what i want to do is
SELECT SUM(*)
FROM enfant e
WHERE e.matricul_prt = $id;
Thank you so much
First of all, you should create a Repository class for AppBundle:enfant, this repo does not look like created fot 'enfant'.
Next the method should look like below, but only if there is valid association between 'enfant' and 'User'.
public function SumEnfant(int $id): int
{
return $this->createQueryBuilder('e') <- alias for 'enfant'
->select('sum(e.id)')
->join('e.user', 'u') <- join by entity property
->where('u.id = :id') <- condition for associated entity
->setParameter('id' , $id) <- parameter
->getQuery()
->getSingleScalarResult();
}
Try this, read doc once again and modify for your case.

Code Style: Doctrine2 queries from entity repository?

As I understand the best code style is to put the complex SQL/DQL queries to the entity repositories.
For example there is an entity named "News". It has an own entity repository named "NewsRepository".
In the controller there is this code:
/**
* #Route("/news", name="news")
*/
public function indexAction(Request $request)
{
$em = $this->getDoctrine()->getManager();
$paginator = $this->get('knp_paginator');
$news = $paginator->paginate(
$em->createQuery('SELECT n FROM AppBundle:News n'),
$request->query->getInt('page', 1),
10
);
return $this->render('app/news/list.html.twig', array(
'news' => $news,
));
}
Now I like to add further features (filtering, order by, ..). That because I think the query should be moved to any service or the entity repository. But how and what is the best coding style?
(And does anybody have nice generic ideas how to easily add filtering, order by ... ?)
Sure,
Every request should be in the repository if you want use it again. Not only complexiest.
I'll try to answer your first question and after give you a tip that could help you with your "generic filtering"
If you want to put your request in the repo just make in your NewsRepository :
public function findAllOrderedByDate() {
$qb = $this->createQueryBuilder('n');
$qb->orderBy('creationDate');
return $qb->getQuery()->getResult();
}
And in your controller :
public function indexAction(Request $request)
{
$newsRepo = $this->get('doctrine')->getRepository('AppBundle:News');
$em = $this->getDoctrine()->getManager();
$paginator = $this->get('knp_paginator');
$news = $newsRepo->findAllOrderedByDate();
$pagination = $paginator->paginate(
$news,
$request->query->getInt('page', 1),
10
);
return $this->render('app/news/list.html.twig', array(
'news' => $pagination,
));
}
For the filtering, you have a trick in your repor that consist by returning the qb and not directly results.
As well, you can make a function that add you orderBy with a given parameter (using addOrderBy() or andWhere) and which return the queryBuilder. After all of that you can process.
EDIT :
The solution i read on this thread :
public function findAllOrderedByDate(callable $func = null) {
$qb = $this->createQueryBuilder('n');
$qb->orderBy('creationDate');
if (is_callable($func)) {
return $func($qb);
}
return $qb->getQuery()->getResult();
}
and in your controller :
$func = function (QueryBuilder $qb) use ($paginator, $request) {
return $paginator->paginate($qb, $request->query->getInt('page', 1), 10);
};
$pagination = $em->getRepository('AppBundle:News')->findAllOrderedByDate($func);
Model part must be in model part. Move queries to repo's.
And what do you mean, saying this: "nice generic ideas how to easily add filtering, order by"?
OrderBy
Where - same link

How can I get Query::HYDRATE_OBJECT as a result, if I use different fields from different entities in a select clause

$query = $this->createQueryBuilder('C')
->select('C.id, C.name, P.id, P.name')
->leftJoin('C.Product', 'P')
->getQuery()
->getResult();
Above query returns HYDRATE_ARRAY, and I want HYDRATE_OBJECT.
$query = $this->createQueryBuilder('C')
->select('partial C.{id, name}, partial P.{id, name}')
->leftJoin('C.Product', 'P')
->getQuery()
->getResult();
your can get result
foreach($query as $q) {
echo $q->getId().' -- '.$q->getProduct()->getId();
}
If you want to use any mysql function like group_concat and get ids
$query = $this->createQueryBuilder('C')
->select('C, GROUP_CONCAT(C.id) AS ids, partial P.{id, name}')
->leftJoin('C.Product', 'P')
->getQuery()
->getResult();
foreach($query as $q) {
echo $q[0]->getId().' -- '.$q[0]->getProduct()->getId().' -- '.$q['ids'];
}
Here you can get all field of category table, group ids of category table and product table filed
Thanks
Ashok Chitroda
You can do it.
/**
* return all devices enabled to array
*
* #return array
*/
private function getAllDevicesToArray()
{
// return to array not object
return $this->getDoctrine()
->getRepository('AdminBundle:Device')
->createQueryBuilder('d')
->where('d.enabled = :enabled')
->setParameter('enabled', 1)
->getQuery()
->getResult(\Doctrine\ORM\Query::HYDRATE_ARRAY);
}

Symfony2. json output, serialize

a Symfony2 newbie question.
What do I miss in the following code (it returns a "Call to a member function getArrayResult() on a non-object)"
/**
* Lists all Post entities.
*
* #Route("/jsonout", name="Mario_Post_jsonout")
* #Method("GET")
* #Template()
*/
public function jsonoutAction()
{
$response = new Response();
$em = $this->getDoctrine()->getEntityManager();
$query = $this->getDoctrine()
->getRepository('MarioBlogBundle:Post')
->createQueryBuilder('e')
->select('e')
->getQuery();
$results = $query->execute();
$myArray = $results->getResult(Query::HYDRATE_ARRAY); // neither ->getArrayResult();
$response->setContent(json_encode($myArray));
$response->headers->set('Content-Type', 'application/json');
return $response;
}
Do I need to use a 'use Doctrine\ORM.....;' line? which one exactly?
Thanks
From my experience the methods getResult() and getArrayResult() coming with the $query object not with $result object. So we don't want to use both ->execute() and getResult() together. That's the mistake in your code. we can rewrite your code like,
public function jsonoutAction()
{
$response = new Response();
$em = $this->getDoctrine()->getEntityManager();
$query = $this->getDoctrine()
->getRepository('MarioBlogBundle:Post')
->createQueryBuilder('e')
->select('e')
->getQuery();
$results = $query->getArrayResult(); //or getResult(Doctrine\ORM\Query::HYDRATE_ARRAY);
$response->setContent(json_encode($results));
$response->headers->set('Content-Type', 'application/json');
return $response;
}
it will get worked.
Two suggestions:
1) You do not need to execute query, you can just say
$query->getResult() which will return array of objects. If you
want to have results as pure array you can use
$query->getArrayResult() which will return array of found values.
You also do not need to use select('e') if you want to select all
columns.
2) If you want to get all records without any criteria and condition you can use findAll(); which return array of all objects in your entity:
$em = $this->getDoctrine()->getEntityManager();
$results = $em->getRepository('MarioBlogBundle:Post')->findAll();
Now by installing and using this class EntitySerializer you can have array output or just JSON output (which you want). For your example it will be:
$entitySerializer = new Bgy\Doctrine\EntitySerializer($em);
$jsonOutput = $entitySerializer->toJson($results);

Symfony2 flash messages from Entity Repository

Is there a way to write flash messages from entity repository. I have a Tag and Category entity.
I am adding tags via Category form, where i added a custom input field that receives tags separated with ", ". Every time i submit a form i check if the tags in the input already exist, if not i add them to the database and then i add them to the Category entity.
Here is my tag repository where im trying to write the flashmessage:
namespace Kpr\CentarZdravljaBundle\Entity;
use Doctrine\ORM\EntityRepository;
use Doctrine\Common\Collections\ArrayCollection;
use Kpr\CentarZdravljaBundle\Entity\Tags;
use Symfony\Component\HttpFoundation\Request;
class TagsRepository extends EntityRepository
{
public function findByTagInput($arg)
{
$request = Request::createFromGlobals();
$args = explode(", ", $arg);
$em = $this->getEntityManager();
$tagArray = array();
$addaedTags = "";
foreach($args as $name){
$tag = $this->findByName($name);
if(!$tag){
$addaedTags .= $name.", ";
$newTag = new Tags();
$newTag->setName($name);
$newTag->setApproved(1);
$em->persist($newTag);
$tagArray[] = $newTag;
}
}
if($addaedTags!="") $request->get('session')->getFlashBag()->add('info', substr($addaedTags,0,strlen($addaedTags)-2));
$qb = $em->createQueryBuilder();
$qb->add('select', 'tag')
->add('from', 'KprCentarZdravljaBundle:Tags tag')
->add('where', $qb->expr()->in('tag.name', $args));
// $qb instanceof QueryBuilder
$query = $qb->getQuery();
// Set additional Query options
$query->useResultCache('my_cache_id');
$results = $query->getResult();
/*
$sql = "SELECT * FROM categories WHERE $whereS";
$stmt = $this->getEntityManager()->getConnection()->prepare($sql);
$results = $stmt->execute();
*/
$totalResults = array_merge($tagArray, $results);
$tags = new ArrayCollection($totalResults);
return $tags;
}
public function findByName($name)
{
$qb = $this->getEntityManager()->createQueryBuilder();
$qb->add('select', 'tag')
->add('from', 'KprCentarZdravljaBundle:Tags tag')
->add('where', 'tag.name = :namearg')
->setParameter('namearg', $name);
// $qb instanceof QueryBuilder
$query = $qb->getQuery();
$result = $query->getResult();
return $result;
}
}
The error i get:
FatalErrorException: Error: Call to a member function getFlashBag()
on a non-object in /home/kprhr/public_html/CZ_Symfony/src/Kpr/CentarZdravljaBundle/Entity/TagsRepository.php line 31
I don't think you should handle the request from the EntityManager ; their purposes are different. The EntityManager manages the communication between the DB and your application, it's not its purpose to handle session management, neither is it to add flashes messages.
This job should go to a dedicated service, which will handle that.
Now, your error comes from the way you get your request. You are actually creating a brand new object, which is not properly instanciated, by using the static method createFromGlobals. Instead, you should handle the flash messages in your controller.
This would translate as something like that:
//Controller
public function mypageAction()
{
// Your logic
if (count($args) > 0) {
$this->getRequest()->get('session')->getFlashBag()->add('info', implode(',', $args));
}
// ...
}

Resources