Doctrine extension :: search entity by other field than ID - symfony

Using doctrine extension, when locale changes, searching an entity with, say ID=1, returns proper values for the current locale (stored in translations table), which is a great thing, but what if I need to search for entities by another field, like name or something..
Is there any way to get proper results using other entity field/property that ID?

Try use:
$query = $em->createQuery("SELECT pc FROM MyDomain\Model\ProductCategory pc WHERE pc.webName LIKE :name")
$query->setParameter("name", $name);
$query->getResult();

Related

Find the right DQL

I have some difficulties in DQL (and SQL !). I always write simple querys but now I need something more specific and I don't know how to do it.
I have an entity User in OneToMany with my entity Program which is in OneToMany with my entity Exercice which is in OneToMany with my Reps.
I would like to know for an specific User all the reps is done.
What would be the best way to do it ?
$this->createQueryBuilder('user')
->select('rep')
->join('user.programs', 'program')
->join('program.exercices', 'exercice')
->join('exercice.reps', 'rep')
->getQuery()
->getResult();
Explication:
This code must be in your repository. The user repository one.
$this->createQueryBuilder('user')
is equivalent to
$this->_em->createQueryBuilder()
->select('user')
->from('user')
You can override the select with the second line:
->select('rep')
Then do the join as described in your question.
programs must be the entity attribute you defined in user to join program
Same goes for exercices and reps. They correspond to the inversed side of the relationship.
The second paramter in join() function is the alias. It can be anything you want. You use aliases in select and in the next join()

Symfony - Doctrine QueryBuilder produces wrong sql

I am trying to build a query via query builder.
$photosQuery = $photoRepository->createQueryBuilder('p')
->join('AppBundle:User', 'u')
->where('LOWER(p.title) LIKE :phrase OR LOWER(u.username) LIKE :phrase AND p.isActive = :isActive AND p.isModerated = :isModerated')
->setParameter('phrase', '%'.strtolower($phrase).'%')
->setParameter('isActive', true)
->setParameter('isModerated', true)
->getQuery();
It gives me
SELECT p0_.id AS id_0,
p0_.title AS title_1,
p0_.description AS description_2,
p0_.name AS name_3,
p0_.creation_date AS creation_date_4,
p0_.edit_date AS edit_date_5,
p0_.is_moderated AS is_moderated_6,
p0_.moderation_date AS moderation_date_7,
p0_.is_active AS is_active_8,
p0_.user_id AS user_id_9,
p0_.category_id AS category_id_10
FROM photos p0_
INNER JOIN users u1_ ON (LOWER(p0_.title) LIKE ?
OR LOWER(u1_.username) LIKE ?
AND p0_.is_active = ?
AND p0_.is_moderated = ?)
Why are my WHERE parameters in the join ON() portion and not a traditional WHERE?
Thank you!
Doctrine provides a wrapper around lower-level database connections that to not necessary have all the features present in DQL. As such, it emulates some features (such as named parameters, or splitting array parameters into multiple separate values).
You're seeing that in action here: the named parameters are converted into positional parameters in the raw query. Doctrine still knows what the mapping is, though, and is able to correctly order them when the query and parameters are sent to the server.
So the answer from jbafford explained some information Doctrine but did not explicitly answer why the ON was used instead of the WHERE, and it stems from a common mistake (of which I recently made as well).
In your query, when you use
->join('AppBundle:User', 'u')
you are simply telling the query builder that you need to join on the User table, but it doesn't specifically know that you want to join on the user association of your Photo entity. You may think - why doesn't this happen automatically? Well, imagine if you had 2 another association on your table that linked to a User entity as well (maybe a createdBy field or similar). In that case Doctrine wouldn't know the association you wanted.
So, instead the proper thing to do is join directly on your association rather than generically on the entity, like so:
->join('p.user', 'u')
and then Doctrine will handle the rest. I couldn't actually tell you why it uses the where() condition for the join, unless it's just assuming that's what you wanted, since it needs to know how to join on something.
So just remember that when you are joining on an association you already defined, join on the association as described in your entities rather than thinking of it in a straight SQL format where you'd join on the table.

How does Doctrine2 One-To-Many fetch=EAGER work?

I'm using Symfony 2.8 / Doctrine ORM 2.5.2.
I have 2 entities, Gallery OneToMany File
class Gallery
{
/**
* #var File[]
*
* #ORM\OneToMany(targetEntity="File", mappedBy="gallery", fetch="EAGER")
*/
private $files;
}
I see 2 things in the documentation.
First, now the OneToMany relationship does have the fetch=EAGER option (specified here). It was not there in previous versions.
Second, the manual setting for this fetch method per query seems not available for OneToMany but I don't know if the documentation is up-to-date as it states:
Changing the fetch mode during a query is only possible for one-to-one
and many-to-one relations.
I have anyway tried both, here is my query:
public function findWithEager()
{
$qb = $this->createQueryBuilder('g');
$query = $qb->getQuery();
$query->setFetchMode("CommonBundle\\Entity\\Gallery", "files", ClassMetadata::FETCH_EAGER);
return $query->getResult();
}
But when I do:
foreach ($galleryRepository->findWithEager() as $gallery) {
foreach ($gallery->getFiles() as $file) {
$file->getId();
}
}
Then I got 1+n queries. The first is SELECT * FROM Gallery and the n following ones are SELECT * FROM File WHERE id = :galleryId
I would like Doctrine to do 1+1 queries, the second one being SELECT * FROM File WHERE id IN (:galleryListIds)
Did I miss something? Is this behavior implemented in Doctrine?
The latest doctrine changelog states:
When marking a one-to-many association with fetch="EAGER" it will now
execute one query less than before and work correctly in combination
with indexBy.
It is not clear at all what is the expected behavior.
Any insight is welcome, thanks!
After much searching and some testing (using Doctrine ORM 2.5.6 on PHP 5.6) I have some results.
At this time it is not possible to get your Gallery entities with one query and all related File entities with a second query.
You have two options
Get Gallery and File entities in one query using Left Join.
This is what the ->find* methods do when you set fetch="EAGER" on your annotation.
You can do this manually with DQL: SELECT g, f FROM Gallery g LEFT JOIN g.files f
As noted in the docs, you cannot call ->setFetchMode('Gallery', 'files', ClassMetadata::FETCH_EAGER) on a DQL query to achieve the same result
... For one-to-many relations, changing the fetch mode to eager will cause to execute one query for every root entity loaded. This gives no improvement over the lazy fetch mode which will also initialize the associations on a one-by-one basis once they are accessed.
This will result in n additional queries being run immediately after your first query to fetch your Gallery entities.
Get Gallery entities with one query and lazy-load the File entities.
This is Doctrine's default behaviour (fetch="LAZY") and will result in 1 query plus an additional query for each set of Gallery#$files you access (1+n queries if you access them all).
Possible Future Option
There is a PR to add an EAGER_BATCHED fetch option which would do exactly what you want (fetch Gallery entities with one query then fetch all File entities with a second query) but there doesn't seem to be much happening with it, unfortunately.
Conclusion
If you're using the ->find* methods, fetch="EAGER" annotations will be respected. Doctrine will do this with a LEFT JOIN. Depending on your data set this could be ok or very expensive.
If you're writing DQL manually or using the query builder you must LEFT JOIN one-to-many relations AND remember to add any entities you want fetched to the select clause.
My preference is to write DQL whenever you want a one-to-many relation fetched eagerly because it makes your intention clear.

Doctrine2 (Doctrine 2.1) eager loading in Symfony2

Let's say I have two entities in my Symfony2 project : Category and Article (a category having many articles).
In my CategoryRepository, I have this method:
findAllDummy(){
return $this->createQueryBuilder('c')
->leftJoin('c.Articles a')
->getQuery()->getResult();
}
If I remember well, in Symfony1.4 (and the corresponding version of Doctrine), the returned objects would have their 'articles' attribute filled by the corresponding Article objects.
Now, in Symfony2, Proxy objects are returned.
So if I loop through a specific category's articles, As many queries as iterations will be executed.
foreach($category->getArticles() as $article){
echo $article->getDoctrine()
->getRepository('')getTitle();
}
I understand this is Doctrine2.1's default lazy loading behavior.
Question 1: how is this a better solution?
N queries instead of 1.
I tried to force eager loading by doing the following:
findAllDummy(){
return $this->createQueryBuilder('c')
->leftJoin('c.articles a')
->getQuery()
->setFetchMode('Category', 'articles', 'EAGER')
->getResult();
}
But the result remains the same.
Question 2: how to force eager loading in Doctrine2?
You're joining a table but you're not selecting anything from it. Add ->addSelect('a') to your query builder. Consider two following SQL queries to understand the difference:
SELECT a.id, a.title
FROM article a
JOIN category c ON a.category_id = c.id
WHERE a.id = 123;
SELECT a.id, a.title, c.id, c.name
FROM article a
JOIN category c ON a.category_id = c.id
WHERE a.id = 123;
Eager/lazy joining has nothing to do with DQL queries. It defines what should be loaded when you use $articleRepository->find(123).
In the part where you try to "force eager loading" the problem might be that you use the fetchMode method with the wrong variable type for the $fetchMode argument. You pass a string 'EAGER' but the method doesn't expect a string but an integer.
The method expects constants from the ClassMetadata class:
/**
* Specifies that an association is to be fetched when it is first accessed.
*/
const FETCH_LAZY = 2;
/**
* Specifies that an association is to be fetched when the owner of the
* association is fetched.
*/
const FETCH_EAGER = 3;
In the Doctrine documentation chapter 14.7.6.6. Temporarily change fetch mode in DQL you can see an example on how to use this:
$query->setFetchMode("MyProject\User", "address", \Doctrine\ORM\Mapping\ClassMetadata::FETCH_EAGER);
So pass either a reference to the constant or an integer that corresponds to the mode you want to use.
As it says in the doctrine docs, eager loading in this case won't make any difference because you have one-to-many relationship between Category and Article.
For one-to-many relations, changing the fetch mode to eager will cause to execute one query for every root entity loaded. This gives no improvement over the lazy fetch mode which will also initialize the associations on a one-by-one basis once they are accessed.
So contrary to what #Crozin has said, you can still do eager loading in DQL.
If you have a one-to-one or many-to-one relationship, eager loading will solve the problem of making extra queries. However to solve your problem in this case you should use ->addSelect('a') as #Crozin mentioned.
It is a better solution because joins are a much more expensive process than a simple query. While it may seem inefficient, it isn't much of a waste, and quickly becomes more efficient when you aren't loading every bit of every related object.

Entity Framework - Where Clause

Let's say i have a table named User.
When I use the Entity Framework to get the records i do like this:
var db = new Context();
var users = db.Users;
It return all the users in my table. OK. If i do this:
var fooUsers = db.Users.Where(u => u.Name == 'foo');
It will give me all the users with name 'foo'. OK. My question is:
The entity framework make a query like:
select * from user where name = 'foo'
Or it load all the users and filter them on the server with lambda expression?
The Sql submitted to your database will contain your where clause. You can use SQL Server Profiler to watch as queries are submitted to your DB.
Entity Framework will translate such a query into a "Store Expression." That is, it will generate a database query -- assuming your data storage is a database -- that will be somewhat similar to the query you included in your question. It very well may name the columns, however, and there are likely to be some other differences.
From here http://msdn.microsoft.com/en-us/library/cc853327.aspx
When you create an ObjectQuery or LINQ query, the query may not be executed immediately. Query execution is deferred until the results are needed, such as during a foreach (C#) or For Each (Visual Basic) enumeration or when it is assigned to fill a List collection. Query execution begins immediately when you call the Execute method on an ObjectQuery or when you call a LINQ method that returns a singleton query, such as First or Any. For more information, see Object Queries and Query Execution (LINQ to Entities).
So when your query has a WHERE clause it will just load the results filtered by the database with the where.

Resources