More on my github’s brute code.
Lazy Load is a simple design pattern that basically consists in making related data to be easily fetched by the domain model.
The implementation of the pattern happened through a behavior that will allow you to get related data with simple, readable and beauty method calls:
$this->User->getProfile();
and the User’s Profile data you shall have.
$this->User->getComments();
and the User’s Comments list will be returned.
“But I want the whole data man” – i hear you shout:
$this->User->getComments('all');
“Nope, I want it to use my custom method as you and Daniel shown” – ok:
$this->User->getEvents('expecteds');
All relationships are supported:
$this->Project->getMilestones(); $this->Project->getOwner(); $this->Child->getFriends(); $this->Post->getTags(); $this->Tag->get_posts(); // if you like this style, enjoy then
But be semantic, it can’t getTask if it actually hasMany Task:
$this->Project->getTask(); // Exception thrown here $this->Project->getTasks(); // yes sir, here they are.
Default behavior is to return a find(’first’) for belongsTo and hasOne associations, and a find(’list’) for hasMany and hasAndBelongsToMany, but you can override it, as shown above, and here again:
$this->Node->getComments(); // find('list') returned
$this->Node->getComments('threaded'); // find('threaded') will be returned
$this->Comment->getAuthor(); // find('first')
And you can even get the related model instance for belongsTo and hasOne associations, just pass a boolean true as param:
// get a Baker instance already setted with the related model data $Baker =& $this->Cake->getBaker(true); $cakes = $Baker->getCakes();
About the beauty mentioned, we can use controller’s actions as examples:
class ProjectsController extends AppController {
function view($id = null) {
if ($this->Project->exists()) {
$data = $this->Project->read();
$assignedUsers = $this->Project->getAssignedUsers();
$hotTickets = $this->Project->getTasks('hots');
$this->set(compact('data', 'assignedUsers', 'hotTickets'));
}
}
}
Pretty RAD prone in my opinion.
No configuration needed, it’s just plug and play.
Be sure to get the tests too, so you can assure it works well.
Thanks and “good night ladies, good night…”
22 responses so far ↓
Abhimanyu Grover // November 21, 2008 at 9:53 pm |
Hey, it looks pretty interesting. Hope it be handy in some project
Joel Moss // November 22, 2008 at 12:20 am |
Nice!
Jonah Turnquist // November 22, 2008 at 1:54 am |
This is beautiful. I’m starting to find that a lot of php5 only frameworks already have these beautiful types of functionalities such as “lazy loading” mostly because of php5’s better object overloading, and so I’m even thinking heavily of switching to Yii.
Jonah Turnquist // November 22, 2008 at 2:04 am |
BTW here is how Yii implants lazy loading, might be interesting to you:
// retrieve the post whose ID is 10
$post=Post::model()->queryByPk(10);
// retrieve the post’s author: the relational query is actually not performed until here
$author=$post->author; //uses object overloading
And if I were to try to access $post->author; again it would be cached of course.
Nik Chankov // November 22, 2008 at 11:29 am |
Really nice approach. I should try it
rafaelbandeira3 // November 22, 2008 at 12:26 pm |
Thanks everyone for your feedback.
@Jonah: Never seen Yii, and just as personal comment, I thought the approach you presented too tied to the db language – query, pk. Anyway, about the caching, wich is a sort of ‘mandatory’ process on the lazy loading pattern, it wasn’t implemented because 1) it would end up costing so much to the app performance, 2) as the app action will only live as long as the request will, you’ll probably never have use for the cached data on the same scenario, 3) as it is implemented in a very easy and simple way, even the implementation of caching would already cost it a bigger performance hit. so that’s why.
About the php5 stuff, it’s such a shame indeed, that using Cake we still have to coupe with some php4 useless and ugly approaches and problems. But it’s still the better choice in my opinion, and I’m very excited about 2.0.
Jonah Turnquist // November 24, 2008 at 3:21 am |
Yeah we’ll I’m just experimenting with Yii so I’ll blog about it when I get a hold on it.
BTW though, when I said”caching” I meant that it is cached across the script, not across different browser requests.
for instance:
$author=$post->author; //executes sql and stores the result in the model as well as returns to $author
$author2=$post->author; //returns the data already stored in the model
rafaelbandeira3 // November 24, 2008 at 9:51 am |
@Jonah Yes I got the caching stuff, what I meant is that you’ll probably never try to fetch the same data again, so there’s no why kill some performance by the cost of caching something that could be handled at the app level.
Thanks by the typo!
Signets remarquables du 21/11/2008 au 24/11/2008 | Cherry on the... // November 24, 2008 at 1:02 pm |
[...] Lazy Loader Behavior. What you need, When you need, The way you want [...]
Nachopitt // November 26, 2008 at 6:44 pm |
Im using for the first time your Behavior… its awesome. I just renamed all that model functions that did the same without the Behavior.
However, I have a couple of quiestions:
Does it supports PHP4?
Also, I have a problem where Posts has a relation with UltraTags involving hasMany or HABTM, because if I code:
$this->Post->getUltraTags();
The behavior give sme an error indicating that the model has to be associated with the loaded model.
If the Model has 2 words or more, I need to specify the method as $this->Post->get_ultra_tags();
I traced the problem placing pr’s inside the behavior and it sounds like must be a problem with the model name, and the inflections applied to it to retrieve the name.
rafaelbandeira3 // November 27, 2008 at 4:43 am |
@Nachopitt: Thanks for your feedback!
- No, actually none of my released codes will support php4 – as I’m not using it for at least 1 year and a half and I’m not just not used to it’s tweaks anymore as php5 has a much stronger support for OO concepts.
- Hum, let’s see, following cake conventions, all related models should be aliased as singular nouns, so your Post should hasMany or hasAndBelongsToMany UltraTag – and then LazyLoaderBehavior would expect you to call $this->Post->getUltraTags(); using the noun on it’s plural form. Now if you set your related model with a plural noun you might run on undesired behaviors, like in this case, that you would probably be able to access Post’s UltraTags using: $this->Post->getUltraTagses(); as that would be the way Inflector would pluralize the word. Now, why the underscored call does work, I don’t know, gotta digg on it, but you should use CakePHP’s convention, because the only way to keep the implementation short and consistent is to rely on standards that will allow your code to assume many things without test params and certain conditions.
I hope I was clear, but if you have any doubt be sure I’m here to support you!
cheers!
Nachopitt // November 27, 2008 at 11:45 pm |
Im sure the model names are following the conventions because I dont specify, for example, the UltraTag model in the Controller::uses array of the UltraTagsController. But thank you very much for your feedback.
Now I have another question. Does the behavior is able to handle new bindings added on the fly by using Model::bindModel or Model::bind methods? Because I was trying to do that with no success… the array returned by the Model::getAssociated() has the new binding if called outside the behavior, whereas Model::getAssociated() inside the behavior returns the array without the new binding…
Thanks for your time, and sorry about my english, I dont write it very frequently.
rafaelbandeira3 // November 28, 2008 at 2:53 am |
@Nachopitt : No problem, I’m Brazilian, so my english is filled with typos and rushy signs. About the feedback, I appreciate yours even more.
I’m working on both issues you presented and I’ll be pushing test cases, and proper patches if needed, til the end of the weekend.
Thanks again and stay tunned on github!
rafaelbandeira3 // December 2, 2008 at 2:14 am |
@Nachopitt the issue is gone, just ripped it out. Thanks for pointing it out. Added some test cases too, to prove that that’s no problem with on the fly associations. Thanks again for the feedback, and come back for support if it’s needed.
Nachopitt // December 3, 2008 at 7:11 pm |
@Rafael, hey! nice to hear that. This is my first time participating (more less) in the enhancement of a real-world software component.
Also, I have another question. Every time I perform a getter using this Behavior, I got a count(*) query followed by the one who actually gets the data. Is this the correct behavior ‘LazyLoader’ should have? Thanks in advance again.
rafaelbandeira3 // December 3, 2008 at 7:55 pm |
@Nachopitt Keep on this path then, is always nice to contribute… I think it’s so exciting!
The query with the COUNT is produced by Model::exists(), as lazy load requires your model to be associated with a table record.
Nachopitt // December 3, 2008 at 8:15 pm |
I have finally realized the origin of my problem… I think.
The count(*) query that happens before the main query resets the bindings if I dont set the ‘permament’ param to true in the Model::bind() method. Thats why I think if I bind a Model on the fly, the behavior doesn’t seems to work.
CakePHP plugins that love Git - Developing With Style // December 4, 2008 at 11:32 am |
[...] This will allow you to get related model data with simple, readable and nice method calls. His blog explains [...]
How is it going so far? « Something on Everything // December 19, 2008 at 6:59 pm |
[...] refactoring and fixing were made on LazyLoaderBehavior, thanks to some troubles Nachopitt pointed out that he was having. Also thanks to [...]
freenity // December 26, 2008 at 6:56 pm |
rafaelbandeira3: Thanks for this behavior, actually that’s what I’ve been looking for. Keep the nice work
links for 2009-02-18 « Richard@Home // February 19, 2009 at 5:01 am |
[...] Lazy Loader Behavior. What you need, When you need, The way you want. « Something on Everything Might come in handy, but you can achieve the same results with Containable behaviour (tags: cakephp model lazyload behaviour) Possibly related posts: (automatically generated)links for 2008-07-31 [delicious.com] [...]
gb // February 26, 2009 at 4:09 pm |
Is it possible to just get the model and then do your own find..
ie: $this->Post->getUser()->getProfile()->find(’first’ , …)
I like to do the find myself