SitePoint Sponsor |
|
User Tag List
Results 101 to 118 of 118
Thread: ActiveRecords
-
Sep 28, 2007, 13:04 #101
- Join Date
- Aug 2007
- Posts
- 92
- Mentioned
- 0 Post(s)
- Tagged
- 0 Thread(s)
Okey, I worked on this some more, and got the whole column-meta business out of the way. Back to philosophizing about associations..
There is an interesting problem with eager loaded has many associations when combined with a limit. Since you only want the limit to affect your main objects, not the associated ones, you basically have to make one extra query, or at least that's how I solved it.
The first query selects all unique IDs with SELECT DISTINCT and applies the limit. It then supplies this list of IDs to the main query, which runs a condition that looks something like WHERE id IN (1, 2, 3, 4...) where the numbers are the list of IDs from the first query.
This brings out another matter. Eager loaded associations can't be limited. This of course only applies to one-to-many associations. I tried to think of a way around it but hasn't come up with anything so far. Maybe it doesn't matter. If you need limiting capabilities for a one-to-many, perhaps you are better off lazy loading it.
Speaking of normal loading, or lazy loading which I think is the correct term (I hope). 33degrees wrote something similar to this a while back:
PHP Code:$user = new User();
$user = findByPk(1);
echo $user->group;
echo $user->getProfile()->email;
You could of course override this method with a custom one in your model.
My current implementation of "lazy" loading is quite horrible, I just reused the standard syntax so I'd do:
PHP Code:$user->profile->email;
Just some food for thought.. As always, interested in hearing how others handle these things.
-
Sep 28, 2007, 14:37 #102
- Join Date
- Nov 2003
- Location
- Huntsville AL
- Posts
- 706
- Mentioned
- 4 Post(s)
- Tagged
- 1 Thread(s)
Might want to take a look at section 8.14 of the PHP Doctrine manual: http://www.phpdoctrine.net/doctrine/...query-language
It talks about some of the techniques used to apply LIMIT against records and not rows.
-
Sep 29, 2007, 07:48 #103
- Join Date
- Aug 2007
- Posts
- 92
- Mentioned
- 0 Post(s)
- Tagged
- 0 Thread(s)
Interesting read, but yeah, they're doing almost exactly what I'm doing. Running another query inside the main one, to select distinct IDs, and then do a WHERE id IN() in the main query. They do not seem to support limits on the joined rows, so I suspect there is no good way to do that.
-
Sep 29, 2007, 18:55 #104
From my understanding, what rails does when you retrieve a get many association is return a proxy that you can either iterate over, or use as a finder. So you'd do something like:
PHP Code:$user->profile->email->find( /* something */);
-
Oct 1, 2007, 06:20 #105
- Join Date
- Aug 2007
- Posts
- 92
- Mentioned
- 0 Post(s)
- Tagged
- 0 Thread(s)
Alright, but that brings up the question of what the default value would be, if you don't use a finder. Say you'd do:
PHP Code:foreach ($blog->comment as $comment)
Problem for me when doing
PHP Code:$user->profile->find( /* options */);
PHP Code:return $this = $objects;
This is what originally led me to the conclusion that the getter must not only instantiate a new object, it must also run a finder. And since you probably need to be able to supply it with values, it would be better to do getter methods for each association rather than relying on __get().
-
Oct 1, 2007, 08:56 #106
The finder way is for overriding the default behavior, which you define along with the association itself. What I do now is:
PHP Code:// set up the association
$this->has_many('comments', array('order'=>'posted_at DESC'));
// retrieve default
$blog->comments;
// override default
$blog->find('comments', array('limit'=>10));
You can't overwrite $this inside a class, no. But in this case I don't see why it's a problem for the finder to return an object?
Yes in the case of lazy loading, it makes sense for the getter to run a finder, but in the end doing $blog->getComments(array('limit'=>'10')) results in the same thing as $blog->comments->find(array('limit'=>'10')), it comes down to which syntax you prefer. The latter has the advantage of being able to do things like $blog->comments->add($comment), which would for example set the foreign key of the passed in comment object and save it, and may be harder to express using the getter syntax.
-
Oct 1, 2007, 09:11 #107
- Join Date
- Aug 2007
- Posts
- 92
- Mentioned
- 0 Post(s)
- Tagged
- 0 Thread(s)
Ah, I see.
That's a nice way of managing defaults.
You can't overwrite $this inside a class, no. But in this case I don't see why it's a problem for the finder to return an object?
PHP Code:$user = new User();
$user = $user->findByPk(1);
echo $user->id;
echo $user->group;
echo $user->profile->find( condition );
echo $user->profile->email; // wouldn't use the object found above
echo $user->profile->website; // neither would this..
// I'd have to do:
$user->profile = $user->profile->find( condition )
echo $user->profile->email; // etc
Yes in the case of lazy loading, it makes sense for the getter to run a finder, but in the end doing $blog->getComments(array('limit'=>'10')) results in the same thing as $blog->comments->find(array('limit'=>'10')), it comes down to which syntax you prefer. The latter has the advantage of being able to do things like $blog->comments->add($comment), which would for example set the foreign key of the passed in comment object and save it, and may be harder to express using the getter syntax.
-
Oct 1, 2007, 09:27 #108
-
Oct 1, 2007, 09:38 #109
- Join Date
- Aug 2007
- Posts
- 92
- Mentioned
- 0 Post(s)
- Tagged
- 0 Thread(s)
True, I just took that example since it was used previously. Hmm, so, say we need to update something in certain comments, how would you write that?
PHP Code:foreach ($blog->comment->find() as $comment)
{
$comment->name = 'something';
$comment->text = 'something';
// .. ?
}
// ...
$blog->save();
-
Oct 1, 2007, 11:07 #110
-
Oct 1, 2007, 12:14 #111
- Join Date
- Jan 2003
- Posts
- 5,748
- Mentioned
- 0 Post(s)
- Tagged
- 0 Thread(s)
Nothing but wouldn't you only need to call
PHP Code:// ...
$blog -> save();
-
Oct 1, 2007, 12:46 #112
- Join Date
- Aug 2007
- Posts
- 92
- Mentioned
- 0 Post(s)
- Tagged
- 0 Thread(s)
Nothing wrong, but the "loaded" association (or whatever one should call it) doesn't get "updated", right? Which means you've got to run a new finder if you want to display/do more a bit later?
PHP Code:foreach ($blog->comment->find() as $comment)
{
$comment->save();
}
// ...
foreach ($blog->comment->find() as $comment)
{
echo $comment->text;
}
-
Oct 1, 2007, 18:25 #113
If you store the results of your find in an array, changes made while iterating through the array will be present the next time you iterate through the array, so you could simply do:
PHP Code:$comments = $blog->comment->find();
foreach ($comments as $comment)
{
$comment->text = 'foo';
$comment->save();
}
// ...
foreach ($comments as $comment)
{
echo $comment->text;
}
-
Oct 2, 2007, 01:16 #114
- Join Date
- Aug 2007
- Posts
- 92
- Mentioned
- 0 Post(s)
- Tagged
- 0 Thread(s)
So the difference between doing that and doing:
PHP Code:$comment = new Comment();
$comment = $comment->find();
foreach (...)
Thing is, I consider it poor form to use the same action for modifying data and displaying result; I would have one action do the saving, then redirect to another action that does the displaying.
-
Oct 2, 2007, 09:20 #115
It also uses the default parameters given when declaring the association. It's the middle ground between using a finder and relying on the default association results.
I don't store lazy loaded has_many associations, but that's not because I don't think it's a good idea; it's simply an optimization that I haven't needed yet.
-
Oct 3, 2007, 06:40 #116
- Join Date
- Aug 2007
- Posts
- 92
- Mentioned
- 0 Post(s)
- Tagged
- 0 Thread(s)
Hmm, I see. But one question, if these two do the same thing:
PHP Code:$comment = $blog->comment;
$comment = $blog->comment->find();
Also, in my case that doesn't even work because has_many associations are arrays containing active record objects. So I wouldn't be able to do $blog->comment->find() (comment in this case is an array). But I recall you mentioning that you didn't use arrays for this?
-
Oct 3, 2007, 08:11 #117
The way it would work is that the first returns a special association object; it implements a find method (which is used in the second example), and it implements array access, so that when you use it as an array, it'll run the default find query.
The point of the ->find() syntax is to override the default values; let's say by default $blog->comment returns all the comments for the blog, in chronological order, $blog->comment->find(array('order'=>'posted_at DESC', 'limit'=>1)) would return you the latest one.
Actually I'm not currently using the syntax I'm describing, as my code was written to be PHP4 compatible; I'm basing my examples on Rails. What I use is $blog->find('comment', $conditions), but I'm currently planning a PHP5 rewrite which will implement what I'm describing.
-
Oct 6, 2007, 12:55 #118
- Join Date
- Aug 2007
- Posts
- 92
- Mentioned
- 0 Post(s)
- Tagged
- 0 Thread(s)
Ah, a special object! Okey but then I understand your code.
The point of the ->find() syntax is to override the default values; let's say by default $blog->comment returns all the comments for the blog, in chronological order, $blog->comment->find(array('order'=>'posted_at DESC', 'limit'=>1)) would return you the latest one.
By the by, anyone using Doctrine? I started to read their manual (thanks ahundiak) and it seems pretty nice. I dabbled a little in Propel a while ago but didn't really like all the generated code and stuff. Doctrine seems nicer..I also noticed they're using the same directory structure as the Zend framework, so I guess there's a chance they play well together, which is a bonus.
Bookmarks