[Dbix-class] Fun with auto-restricted result sets

fREW Schmidt frioux at gmail.com
Fri Feb 20 22:37:42 GMT 2009


>
> David Ihnen wrote:
>>>
>>>> So in the wake of fREW showing a modification of the classes to allow
>>>> the modification of the delete, I utilized a corrollary concept...
>>>> restricting a result set automatically/transparently, as you would in =
case
>>>> of that delete - not normally showing the deleted rows.  In my case, my
>>>> alerts table has a possible action window, possibly has been dismissed=
, and
>>>> possibly might be inactive.  This made the resultset class more compli=
cated,
>>>> but not unmanageable. It was fun to work through the logic of modifyin=
g an
>>>> arbitrarily complex requested search clause.  If you specify values for
>>>> these, it is assumed you know what you're doing and this result set cl=
ass
>>>> won't further modify the terms.  I may have missed the handling of alr=
eady
>>>> defined alert_expire terms though... maybe I should make it inactive i=
f you
>>>> specify any of the terms anywhere in the \%where tree.
>>>>
>>>>
>>>> package DB::Schema::active_alert_resultset;
>>>> use base 'DBIx::Class::ResultSet';
>>>>
>>>> sub search {
>>>>   my $self =3D shift;
>>>>   $_[0]->{'inactive'}     ||=3D 0;
>>>>   $_[0]->{'dismissed'}    ||=3D \"IS NULL";
>>>>   $_[0]->{'alert_time'}   ||=3D { '<' =3D> \"NOW()" };
>>>>   my $aeor =3D [];
>>>>   push @{$aeor}, { alert_expire =3D> { '>' =3D> \'NOW()'} };
>>>>   push @{$aeor}, { alert_expire =3D> \'IS NULL' };
>>>>   if ($_[0]->{'-or'}) {  # If there is already an -or, we need to nest
>>>> it into an -and
>>>>                          # so we don't overwrite the term, or change t=
he
>>>> logic by orring with it.
>>>>     my $and =3D $_[0]->{'-and'} ||=3D [];  # Use an existing -and term=
 if
>>>> already supplied.
>>>>     push @{$and}, { -or =3D> delete $_[0]->{'-or'} }; # Move the old -=
and
>>>>     push @{$and}, { -or =3D> $aeor }; # add our alert expire or term
>>>>   } else {
>>>>     $_[0]->{'-or'} =3D $aeor;
>>>>   }
>>>>   return $self->next::method( @_ );
>>>> }
>>>>
>>>
>>> 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?
>>
>>
>>  Until DBIC goes Moose, it's tricky (and not very safe) to introspect an
>>> existing RS to see what's already been done to it...
>>>
>> If I have already restricted the resultset, then re-restricting it by the
>> same terms will filter out whats not there, right?  And asking for dismi=
ssed
>> =3D> \'IS NOT NULL' on an alerts (or derived) resultset will always give=
 you
>> empty set.  No active alerts have dismissed NOT NULL
>>
>
> 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 =3D $schema->resultset('alert')->search({inactive =3D> 1});
>
> Then another part of your code wants to take that same result set and lim=
it
> it to just those alerts from last year:
>
>  my $new_rs =3D $rs->search({alert_time =3D> {'<' =3D> \'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 referr=
ed
> 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   =3D> 0,
>          dismissed  =3D> \'is null',
>          alert_time =3D> { '<' =3D> \"NOW()" }
>          '-or' =3D> [
>              { alert_expire =3D> { '>' =3D> \'NOW()'} },
>              { alert_expire =3D> \'IS NULL' },
>          ],
>      });
>  }
>
> Then anywhere in your code that you want to enforce "activeness", just do:
>
>  my $rs =3D $schema->resultset('alerts')->active->search({foo =3D> 'bar'}=
);
>
> It's not *quite* as slick as having it defaulted automatically, but it
> protects you from unwittingly clobbering previously set criteria.


>From IRC conversations it appears that really the slickest way to do these
things is to define a ResultSource (not result set) as that can help you
with joins and whatnot as well.  I plan on using this for my Paranoid
Deletion setup, but I can't *just* work on the DB side at work, so it will
have to wait a bit :-)
-- =

fREW Schmidt
http://blog.afoolishmanifesto.com
-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://lists.scsys.co.uk/pipermail/dbix-class/attachments/20090220/e9c=
4025e/attachment.htm


More information about the DBIx-Class mailing list