It looks like when I forget to put leftJoin in my queries, Doctrine will lazy-load relations after I pick them. Ie if I do something like:
$property = PropertyTable::getInstance()->createQuery('o')
->leftJoin('o.Features f')
->execute() ;
I will still be able to access $property->Attraction , even if I didn't load them in this query. So I guess Doctrine does it internally by calling another 'SELECT'. Is there a way to prevent this auto-loading by throwing some exception? This would help me create better and faster queries.
Have you looked at the actual sql it produces?
Try ->getSql(). It is likely it is doing another query when you attempt to access a related field.
If you want to avoid that, don't access related fields!
Related
I have a script that is passed a table name. I want to be able to load relations of relations for the records returned. For instance, I can get this by using the classname directly:
Item::find($id)->schedules->load('template')->toArray();
This gives me what I want, however, the table name passed is variable, plural and lowercase. To fix this I could just do something like:
$table_class = studly_case(str_singular($table));
$table_class::find($id)->schedules->load('template')->toArray();
However, this is a little clunky. I'd like to do it like so:
DB::table($table)->find($id)->schedules->load('template')->toArray();
But the above doesn't work and I haven't been able to figure out the correct syntax for it. How would I do this using DB::table()?
With DB::table() your are using the query builder. Eager loading is a feature of the eloquent orm. Afiak there is no way to do this via the query builder.
But imho your clunky solution is absolutly ok, I solved a similar problem the same way. If you are just worried about the code style, you could implement a simple ModelFactory that does this "ugly" job, something like this:
ModelFactory::create($table)->find($id)->...
I'm currently working on a Service in SF2 that queries the database with the QueryBuilder using a class variable set with a repository-specific QueryBuilder in the constructor of this Service.
Which means i would like to make use of this set QueryBuilder as much as possible for neater code and a clean feeling using it.
I want to avoid creating a query on the EntityManager, but instead solely query using this predefined Querybuilder.
I'm looking for something that would look/work like the following:
$query = $this->fooRepository->createQueryBuilder('f')->select('*');
return $query->getResult(Query::HYDRATE_ARRAY);
The above would (if it worked) return all the foo in the database as far as i know..
If you think i'm being stupid and should do something different in regard to the predefined QueryBuilders or just use the:
createQuery()
method because it simply isn't good practice or impossible, don't hesitate to tell me.
Thanks!
Try:
$qb = $this->fooRepository->createQueryBuilder('foo');
return $qb->getQuery()->getResult(Query::HYDRATE_ARRAY);
No need for a select(*). All the foo items will be selected since no where clauses were added.
Can someone provide me a couple clear (fact supported) reasons to use/learn DQL vs. SQL when needing a custom query while working with Doctrine Classes?
I find that if I cannot use an ORM's built-in relational functionality to achieve something I usually write a custom method in the extended Doctrine or DoctrineTable class. In this method write the needed it in straight SQL (using PDO with proper prepared statements/injection protection, etc...). DQL seems like additional language to learn/debug/maintain that doesn't appear provide enough compelling reasons to use under most common situations. DQL does not seem to be much less complex than SQL for that to warrant use--in fact I doubt you could effectively use DQL without already having solid SQL understanding. Most core SQL syntax ports fairly well across the most common DB's you'll use with PHP.
What am I missing/overlooking? I'm sure there is a reason, but I'd like to hear from people who have intentionally used it significantly and what the gain was over trying to work with plain-ole SQL.
I'm not looking for an argument supporting ORMs, just DQL when needing to do something outside the core 'get-by-relationship' type needs, in a traditional LAMP setup (using mysql, postgres, etc...)
To be honest, I learned SQL using Doctrine1.2 :) I wasn't even aware of foreign-keys, cascade operations, complex functions like group_concat and many, many other things. Indexed search is also very nice and handy thing that simply works out-of-the-box.
DQL is much simpler to write and understand the code. For example, this query:
$query = ..... // some query for Categories
->leftJoin("c.Products p")
It will do left join between Categories and Products and you don't have to write ON p.category_id=c.id.
And if in future you change relation from one-2-many to let's say many-2-many, this same query will work without any changes at all. Doctrine will take care for that. If you would do that using SQL, than all the queries would have to be changed to include that intermediary many-2-many table.
I find DQL more readable and handy. If you configure it correctly, it will be easier to join objects and queries will be easier to write.
Your code will be easy to migrate to any RDBMS.
And most important, DQL is object query language for your object model, not for your relational schema.
Using DQL helps you to deal with Objects.
in case inserting into databae , you will insert an Object
$test = new Test();
$test->attr = 'test';
$test->save();
in case of selecting from databae, you will select an array and then you can fill it in your Object
public function getTestParam($testParam)
{
$q=Doctrine_Query::create()
->select('t.test_id , t.attr')
->from('Test t ')
$p = $q->execute();
return $p;
}
you can check the Doctrine Documentation for more details
Zeljko's answer is pretty spot-on.
Most important reason to go with DQL instead of raw SQL (in my book): Doctrine separates entity from the way it is persisted in database, which means that entities should not have to change as underlying storage changes. That, in turn, means that if you ever wish to make changes on the underlying storage (i.e. renaming columns, altering relationships), you don't have to touch your DQL, because in DQL you use entity properties instead (which only happen to be translated behind the scenes to correct SQL, depending on your current mappings).
While using doctrine, I noticed that, to delete an entity, I need to retrieve that entity by given parameter(name,id etc) and then call the remove method. On the other hand, in query, I can just execute delete query.
So, seems like, using ORM style requires two operation and general sql operation require one operation. That's why, I am a little confusing, whether we should use delete(or update) operation in ORM? Isn't it worse in performance? Or Is there anything else I am missing? Can it be done in any other way in ORM style?
In Doctrine2 you can call the delete on a proxy object, which is not loaded from the database. Just create a "dummy" object, something like:
$user = $em->getPartialReference('model\User', array('id' => $id));
$em->remove($user);
It doesn't require the initial query, but I'm not quite sure if Doctrine still does it internally on flush. I don't see it in the SqlLog.
Just to add, I think this is expected behavior of any decent ORM. It deals with objects and relations. It has to know that something exists before deleting it. ORM is not just a query generator. Generally, a native query will always be faster in any ORM. Any ORM adds a layer of abstraction and it takes some time to execute it. It is a typical tradeoff, you get some fancy features and clean code, but loose some on performance.
EDIT:
I'm glad it worked out for you. Actually I stumbled on another problem, which made me realize that proxies and partial objects aren't actually the same thing. Partial objects instance the real model class, and fill it with values you want. After you initialize a partial object lazy-loading doesn't work on it anymore. So for instance, if you make a partial object with only the id, and want to delete only if another object field satisfies some condition, it will not work, because that other field will always be null.
On the other hand, proxies do work with lazy-loading, and don't share the problems that partial objects have. So I would strongly suggest not to use getPartialReference method, instead you can do something like:
$user = $em->getReference('model\User', $id);
$em->remove($user);
The getReference method returns the object if it is already loaded or a proxy if it is not. A proxy can lazy-load all the other values if/when you need them. As for your example, they will behave exactly the same, but proxies are surely a better way to go.
Done!
for me it worked like this add line 3:
$user = $em->getReference('model\User', $id);
$em->remove($user);
$em->flush();
I'd like some help from the community, if possible.
We have a project at work, and time has come for refactoring. We are using PHP 5 and Propel 1.6 as ORM layer. The idea, actually, is pretty simple: we are trying to obtain all relevant information at the same time.
However, this information is not always directly related to the main class (or, in other words, to the main table). For example:
return (
$this->leftJoin( "IES.Pessoa mant" )
->leftJoin( "mant.Papel subpapel" )
->where( "subpapel.tipo = ?", Tipo_papel::IES )
->leftJoin( "subpapel.RelacionamentoRelatedByIdSubPapel relm" )
->where( "relm.tipo = ?", Tipo_relacionamento::MANTENEDORA_IES )
->leftJoin( "relm.PapelRelatedByIdSuperPapel superpapel" )
->leftJoinWith( "superpapel.Pessoa iesm" )
->where( 'superpapel.tipo = ?', Tipo_papel::MANTENEDORA_IES )
);
This is the code of a function in a ModelCriteria from our model layer. The idea is to obtain 'iesm' related to one 'IES'.
But, here we have a problem. It happens that 'IES' is already related to one entity 'Pessoa'. So, when this code is applied, this object will be lost, giving space to the entity 'Pessoa' related to 'iesm'.
So the basic question is: I would like to set the property 'iesm' inside 'IES' object, and not Pessoa, which is set because of Propel mapping. That being said, how can I do that? Is it even possible? I'd like to set 'iesm' based on the alias created on leftJoinWith.
And another question, that showed immediately after this: if there is no direct relation in the database, how can I set this object, using the same idea? Trying to explain better: this 'iesm' is actually an object of type 'Pessoa_juridica'. And 'Pessoa_juridica' is not directly related to 'IES'. How could this 'Pessoa_juridica' be set inside 'IES' object?
I don't even know if this is the best way of using it, so any other ideas are welcome. If you want further explanation, just let me know.
Ok so i think i undestand what your are dealing with: You what to optimize database connections but found that, because you need to join two different unrelated table with another one that joins them in some way.
My advise in this situation is:
Try using UNION with a custom query. So create two criterions and use UNION to merge results, for this to work the selected fields en both tables should be the same in size and type. I'm no sure how to do this using the Query objets, but i'm quite sure it can be accomplished using Peer classes. Check this link UNION query with Propel ORM
Dont hidrate all objets. Sometimes its far more efficient and quite more clear to use statements (like doSelectStmt()) and fetching information as arrays from separate database queries, manipulate them and then only hydrate the objects you are going to use.
Remember that symfoyn also has a cache (APC for example), that's always a good idea when dealing with really big tables, complex information and repeated information.