Access entity methods with getArrayResult - symfony

I'm experiencing performance issues and I'm trying to retrieve hydrate array with return $qb->getQuery()->getArrayResult(); from my repository to improve it.
The performance is slightly better, but I can't access my custom entity methods anymore... I guess it's a normal behavior, but is there a way to go through it ?
EDIT:
Loading time of my controller is way to long on my local dev environment
My controller action is only retrieving a set of objects from my repository, here is the code :
public function findAllForAdmin()
{
$qb = $this
->createQueryBuilder('a')
->leftJoin('a.subcategory', 's')
->addSelect('s')
->leftJoin('s.category', 'c')
->addSelect('c')
->leftJoin('a.user', 'u')
->addSelect('u')
->leftJoin('a.advertpaidoptions', 'apo')
->addSelect('apo')
->leftJoin('a.photos', 'p')
->addSelect('p')
;
return $qb
->getQuery()
->getArrayResult()
;
}
a is for advert, I'm retrieving about 2k adverts. That's not so much IMO...
I don't know where to look...

getArrayResult will return result of select into an array, so it's not possibile anymore to have back your entities.
If you need to fetch all (or certain) infos of the entity, you can follow to strategies:
Implicit (at query builder level) make them part of select builder's part
Use fetch=EAGER on entity attribute

Well, I think I managed to improve my performance issue, the answer seems to be pretty obvious though...
Instead of loading the whole entities, I just load the fields I need...
public function findAllForAdmin()
{
$qb = $this
->createQueryBuilder('a')
->select(array(
'a.id',
'a.dateCreate',
'a.title',
'a.price',
'a.price_type',
'a.completed',
'a.deleted',
'a.slug',
))
->leftJoin('a.subcategory', 's')
->addSelect('s.slug AS s_slug')
->leftJoin('s.category', 'c')
->addSelect('c.slug AS c_slug')
->leftJoin('a.user', 'u')
->addSelect('u.username')
->leftJoin('a.advertpaidoptions', 'apo')
->addSelect('COUNT(apo.id) AS nb_apo')
->leftJoin('a.photos', 'p')
->addSelect('COUNT(p.id) AS nb_p')
->groupBy('a.id')
;
return $qb
->getQuery()
->getResult()
;
}
The improvement is huge...

Related

Doctrine sort by 2 different entities

I'm building a forum with Symfony and I'm currently wondering on how I'm about to list the latest activity using Doctrine.
I have a OneToMany relationship between Topic & Post.
I'd have to check for the latest things happened, which might be a post on a topic, or a topic itself.
How would you guys do this as clean as possible?
I would probably UNION both somehow sort by date and select last 5.
Thank you in advance!
You can try something like this (It's a function of repository post) :
public function lastTopics(){
// Build query
$qb = $this
->createQueryBuilder('p')
->select('p')
->leftJoin('p.topic', 't')
->addSelect('t')
->addGroupBy('p.id')
->addGroupBy('t.id')
->orderBy('p.dateCreation', 'DESC')
->setMaxResults(5)
;
// Get query
$query = $qb->getQuery();
// Return results
return $query->getResult();
}
Then just list the last 5 posts by viewing topics. Enjoy ;)

Symfony2 simple query: can't fetch an object, only array

The following query in Symfony2.6 works with getArrayResult(); BUT doesn't work with getResult(); or getOneOrNullResult();. This means that I can't fetch an object but only an array.
If I use this query, all I get is a white/blank page (not even the symfony debug toolbar). Note that in my twig template I just do a {{ dump() }} of the query result.
The table structure is easy (it's a list of books read by the users):
id, user, book, status, vote, review
(user, book and status are foreign keys)
public function selectOneRecordBy2Ids($user_id, $book_id)
{
/* #var $qb QueryBuilder */
$qb = $this->createQueryBuilder('l');
$qb->select('l');
$qb = $qb->Where($qb->expr()->eq('l.user', ':first'));
$qb = $qb->andWhere($qb->expr()->eq('l.book', ':second'));
$qb = $qb->setParameters(array('first' => $user_id, 'second' => $book_id));
return $qb->getQuery()->getOneOrNullResult();
}
I've noticed a few bad practices here, so let me correct them:
public function selectOneRecordBy2Ids(User $user, Book $book)
{
/* #var $qb QueryBuilder */
$qb = $this->createQueryBuilder('l');
$qb
->andWhere($qb->expr()->eq('l.user', ':first'))
->andWhere($qb->expr()->eq('l.book', ':second'))
->setParameters(array('first' => $user, 'second' => $book));
return $qb->getQuery()->getResult();
}
Select is not necessary if you work only with one entity and you don't fetch any relations. QB returns $this so you can chain the method calls.
Try to use entities as parameters instead of primitive types (if it is possible). If not, then you have to use primitive types as primitives in QB. In this case you'll need a few joins:
->select('l')
->join('l.user', 'u')
->join('l.book', 'b')
->andWhere($qb->expr()->eq('u.id', ':first'))
->andWhere($qb->expr()->eq('b.id', ':second'))
->setParameters(array('first' => $user_id, 'second' => $book_id));
If you want to fetch only one record, then you may have to limit the results by setting the max results:
->setMaxResults(1)
I hope this helps and solves your original problem as well.
getOneOrNullResult() does not return the record, but tells you if any record in database is found, or not.
You might want to use getSingleResult()

doctrine querybuilder limit and offset

i'm a symfony beginner and i want to make a blog with the framework. i use repository to get home articles with this method :
public function getHomeArticles($offset = null, $limit = null)
{
$qb = $this->createQueryBuilder('a')
->leftJoin('a.comments', 'c')
->addSelect('c')
->addOrderBy('a.created', 'DESC');
if (false === is_null($offset))
$qb->setFirstResult($offset);
if (false === is_null($limit))
$qb->setMaxResults($limit);
return $qb->getQuery()
->getResult();
}
so in my database i have 10 articles. In my BlogController i use :
$blog = $em->getRepository('TestBlogBundle:Article')
->getHomeArticles(3,4);
With this i want 4 articles. But in return i also have one article.
What is the problem?
This is a know issue where setFirstResult() and setMaxResults() need to be use with care if your query contains a fetch-joined collection.
As stated about First and Max Result Items:
If your query contains a fetch-joined collection specifying the result
limit methods are not working as you would expect. Set Max Results
restricts the number of database result rows, however in the case of
fetch-joined collections one root entity might appear in many rows,
effectively hydrating less than the specified number of results.
Instead, you can:
Lazy load
use the Paginator (as stated by #Marco here)
Use Doctrine\Common\Collections\Collection::slice()

Symfony/Doctrine: fetching data as object , still get array

I have in my controller $id it's a foreign key
$query = $em->getRepository('SurgeryPatientBundle:Patients')->findPatientByUserID($id);
And in my repository file this function
public function findPatientByUserID($id)
{
return $this->getEntityManager()
->createQuery('SELECT p FROM SurgeryPatientBundle:Patients p WHERE p.user ='.$id.'')
->execute();
}
I want get an instance of object but still get an array. Query with find($id) works good
edit
Problem solves , I'm so stupid , I had invoked to $query[0]
You can use $query->getSingleResult(); as well
see here
http://docs.doctrine-project.org/en/2.1/reference/dql-doctrine-query-language.html#query-result-formats
If you want to grab the object, you shouldn't be using DQL. Doctrine entities have a find function that takes care of this for you.
Instead of all that code, you can just use (in your controller):
$em->getRepository('SurgeryPatientBundle:Patients')->find($id);
DQL is very powerful, but for simple lookups like this using the built in find methods will be more efficient & provide the entities as doctrine objects.

How to list user based on their roles in Symfony2 + Doctrine effectively

I have done this with this code
$users = $this->getDoctrine()->getEntityManager()
->createQuery('SELECT tu FROM UserBundle:User tu')
->getResult();
$result = array();
foreach($users as $user){
if($user->hasRole('ROLE_CUSTOMER'))
$result[] = $user;
}
return $this->render('XBundle:Order:index.html.twig',
array('users' => $result));
But what is the simple way to achieve this same result ?
Thanks.
Update, additional info:
Our main problem is that user roles have a hierarchical structure.
So ROLE_EDITOR will automatically have ROLE_WRITER if I defined ROLE_WRITER under ROLE_EDITOR.
Thus I can't just use simple query, I think I also have to utilize security context component.
I don't know your schema, but I guess you could do an INNER JOIN and put your condition in the ON clause. Read this article about the query builder to figure out how to build your query.
Doctrine collections also have a filter method that takes a Closure as an argument.

Resources