1 package SL::DB::Helper::Filtered;
4 use SL::Controller::Helper::ParseFilter ();
7 our @ISA = qw(Exporter);
8 our @EXPORT = qw (filter add_filter_specs);
13 my ($class, $key, $value, $prefix, $path, @additional_tokens) = @_;
15 my $filters = _get_filters($class);
17 return ($prefix . $key, $value, $path) unless $filters->{$key};
19 return $filters->{$key}->($key, $value, $prefix, @additional_tokens);
24 return $filter_spec{$class} ||= {};
27 sub add_filter_specs {
30 my $filters = _get_filters($class);
34 $filters->{$key} = shift;
46 SL::DB::Helper::Filtered - Manager mixin for filtered results.
52 use SL::DB::Helper::Filtered;
54 __PACKAGE__->add_filter_specs(
55 custom_filter_name => sub {
56 my ($key, $value, $prefix) = @_;
58 return ($key, $value, $with_objects);
60 another_filter_name => \&_sub_to_handle_this,
65 ($key, $value, $with_objects) = $manager_class->filter($key, $value, $prefix);
71 =item C<add_filter_specs %PARAMS>
73 Adds new filters to this package as key value pairs. The key will be the new
74 filters name, the value is expected to be a coderef to an implementation of
75 this filter. See L<INTERFACE OF A CUSTOM FILTER> for details on this.
77 You can add multiple filters in one call, but only one filter per key.
79 =item C<filter $key, $value, $prefix>
81 Tells the manager to apply custom filters. If none are registered for C<$key>,
82 returns C<$key, $value>.
84 Otherwise the filter code is called.
88 =head1 INTERFACE OF A CUSTOM FILTER
90 Lets look at an example of a working filter. Suppose your model has a lot of
91 notes fields, and you need to search in all of them. A working filter would be:
93 __PACKAGE__->add_filter_specs(
95 my ($key, $value, $prefix) = @_;
98 $prefix . notes1 => $value,
99 $prefix . notes2 => $value,
104 If someone filters for C<filter.model.all_notes:substr::ilike=telephone>, your
105 filter will get called with:
107 ->filter('all_notes', { ilike => '%telephone%' }, '')
109 and the result will be:
112 notes1 => { notes1 => '%telephone%' },
113 notes2 => { notes1 => '%telephone%' },
116 The prefix is to make sure this also works when called on submodels:
118 C<filter.customer.model.all_notes:substr::ilike=telephone>
120 will pass C<customer.> as prefix so that the resulting query will be:
123 customer.notes1 => { notes1 => '%telephone%' },
124 customer.notes2 => { notes1 => '%telephone%' },
127 which is pretty much what you would expect.
129 As a final touch consider a filter that needs to search somewhere else to work,
132 __PACKAGE__->add_filter_specs(
134 my ($key, $value, $prefix) = @_;
136 return $prefix . person.name => $value,
141 Now you can search for C<name> in your model without ever knowing that the real
142 name lies in the table C<person>. Unfortunately Rose has to know about it to
143 get the joins right, and so you need to tell it to include C<person> into its
144 C<with_objects>. That's the reason for the third return value.
153 You will get passed the name of your filter as C<$key> stripped of all filters
158 You will get passed the C<$value> processed with all filters and escapes.
162 You will get passed a C<$prefix> that can be prepended to all database columns
163 to make sense to Rose.
167 You are expected to return exactly one key and one value. That can mean you
168 have to encapsulate your arguments into C<< or => [] >> or C<< and => [] >> blocks.
172 If your filter needs relationships that are not always loaded, you need to
173 return them in C<with_objects> style. If you need to return more than one, use
184 Sven Schöling E<lt>s.schoeling@linet-services.deE<gt>