I am wondering if there are any equivalent to $em->persist($entity) or $em->flush() with DBAL ?
I'm meaning, how DBAL work with entity ? I'm working with entity not handled by doctrine : data to hydrate entity is retrieved with DBAL using SELECT ..., and if I have to update stuff in database, do I have to use DBAL insert()/update() functions as stated here : http://docs.doctrine-project.org/projects/doctrine-dbal/en/latest/reference/data-retrieval-and-manipulation.html ?
Do you know a simple way to record changes made to an entity while using a form ?
I hope i was understandable, thank you for reading.
In DBAL you have no entities since this is just Database Abstraction Layer - its responsibility is to make sure that when you run a query it will be run properly on various DB engines.
Dealing with Entities is a responsibility of Object-Relational Mapping library (like DoctrineORM).
So there is no equivalent of flush or persist in DBAL. To perform update on DB you need to run query like
$conn->executeUpdate('UPDATE user SET username = ? WHERE id = ?', array('jwage', 1))
Related
I have a tree structure, it is managed by Gedmo\Tree. I want to make complex update for one field in subtree, this update requires join with another table, it is not supported by DQL. So I want to get DQL builder by Gedmo\Tree repository method childrenQueryBuilder, convert it to QueryBuilder and add update statement.
$dqlQueryBuilder = $repository->childrenQueryBuilder($node, ...);
$dqlQueryBuilder->resetDQLParts(['select', 'orderBy']);
$queryBuilder = convert($dqlQueryBuilder);
$queryBuilder->leftJoin('...', 'lj');
$queryBuilder->update('node.update', 'concat(node.field, lj.field)');
I know that I can write custom QueryBuilder, I just wonder if such conversions are possible by doctrine builtin tools or some 3-rd party libraries.
There's no such thing as SQLQueryBuilder in Doctrine, there's only QueryBuilder which is an DQL Query Builder. What you can do is to convert DQL to SQL by doing
$stringSql = $queryBuilder->getQuery()
->getSQL();
Once you have it you might play with native sql and then execute it as a raw sql.
Note: I'm not sure what exact DB Specific statement you mean, but there's a possibility to map DB Specific functions to DQL by making use of FunctionNode class. Once you have your function mapped to DQL you might accomplish it with DQL only.
Check documentation on how to work with custom DB functions in DQL
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.
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.
Can someone help me answer these questions on EntityFramework?
Does it do anything special to the database? (like extra tables)
Can I add data directly with SQL without breaking EF?
Can I add tables and fields without breaking EF?
Yes you can access database with plain SQL when using EF.
No. EF just uses database. There is one exception in code first approach where EF can create one additional table for its own purpose called EdmMetadata.
Yes you can add data directly with SQL. If both your entity model and database are defined correctly it will not break EF.
Yes you can add new tables directly but EF will not know about them. You should not change existing tables because it can break EF.
You can do it with:
var context = new YourObjectContext();
var s = context.ExecuteStoreCommand("some query");
if your query create a table, this only create a table on db and not effected on EF
By Linq, I mean Entity Framework and Linq. A further question, if the SELECT query is the same, but OEDER BY clause is different, does Linq have to access the database or the in-memory entities have enough information for the new SELECT query with different ORDER BY clause?
The short answer is no, once the Entity is created, the Entity itself contains all the information that will be used for data binding, unless you explicitly instruct the Entity to requery the underlying data store.