Jess Robinson wrote:
Thanks Jason! I was reading through this thread muttering about blind
leading blind here.. No offense meant, David, fREW.. But you guys missed
a few things, so I need to get into the DBIC docs and make sure the next
lot don't miss them..
On Fri, 20 Feb 2009, Jason Gottshall wrote:
David Ihnen wrote:
Jason Gottshall wrote:
> You're only checking to see if the given columns have specified
values > passed in, but what if ->search was called on a resultset
object that > *already* has constraints for these columns?
What? How can you be in a result class and not in it too? Can you
give
an example of how to write that in code?
The problem comes when you try to "derive" one resultset from another;
any criteria not present are clobbered by your default assumptions.
Let's say that one part of your code retrieves inactive alerts:
my $rs = $schema->resultset('alert')->search({inactive => 1});
Then another part of your code wants to take that same result set and
limit it to just those alerts from last year:
my $new_rs = $rs->search({alert_time => {'<' => \'current_yr()'}});
# replacing 'current_yr()' with the appropriate syntax for your dbms
Using your methodology, this invocation of ->search will set
'inactive' back to '0', because it's not passed in.
Of course, if you only call search once with both criteria, you don't
have a problem. But I've been finding the additive technique
(sometimes referred to as "resultset chaining") to make for very
clean, readable, maintainable code. In fact, you could use such an
approach as an alternative to overriding ->search with your defaults:
package DB::ResultSet::alerts;
use base 'DBIx::Class::ResultSet';
sub active {
return shift->search({
inactive => 0,
dismissed => \'is null',
alert_time => { '<' => \"NOW()" }
'-or' => [
{ alert_expire => { '>' => \'NOW()'} },
{ alert_expire => \'IS NULL' },
] ,
});
}
Then anywhere in your code that you want to enforce "activeness", just
do:
my $rs = $schema->resultset('alerts')->active->search({foo => 'bar'});
It's not *quite* as slick as having it defaulted automatically, but it
protects you from unwittingly clobbering previously set criteria.
In addition to what Jason said about chained rewsultsets/searches being
saner, the search() method overriding idea doesn't catch all the places
where you might want searches restricted.
For example:
$schema->resultset('Books');
Returns a perfectly usable Books resultset, and wont know about any
restriction in search to non-deleted books only.
Also:
$author->books;
Will return all books belonging to an author via a has_many
relationship, and also wont see any changes / modifications to the
"search" method in the Books resultset.
Writing your own explicit one to call as Jason showed is saner, then you
know when you're doing it.
If you want a permanent one for always, that also works across chained
resultsets and relationships, use the resultset_attributes class method,
eg:
__PACKAGE__->resultset_attributes({ where => { deleted => undef }});
"undef" in SQL::Abstract produces an "IS NULL" btw, so no need for those
literal strings.
Just to clarify, the ->resultset_attributes() technique Jess describes
should be placed inside the "Result" (i.e. table definition) class for
the table you're trying to constrain (NOT the corresponding ResultSet
class). I had seen this kind of recommendation many times on the list
and the irc channel before I figured out where to put it. :-)
I do like this technique; it's most appropriate when you want the
default criteria added for the vast majority of cases and don't have
many situations where you actually need the unfiltered data. Of course
you could consider extending this concept by putting the basic table
definition (columns, relationships, keys, etc.) in a base class and then
creating two subclasses: one with the defaults added via
->resultset_attributes() and one without (maybe with "_all" added to the
name?) Then you have an easy way to get at the unfiltered stuff. Of
course, it does mean that all related sources will need to define the
reverse relationship twice, with 2 different names, so you'll have to
gauge whether it's worth the added complexity. TIMTOWTDI.
--
Jason Gottshall
[email protected]
_______________________________________________
List: http://lists.scsys.co.uk/cgi-bin/mailman/listinfo/dbix-class
IRC: irc.perl.org#dbix-class
SVN: http://dev.catalyst.perl.org/repos/bast/DBIx-Class/
Searchable Archive: http://www.grokbase.com/group/[email protected]