Volltext-Suche: Tabelle für Texte aus Dateien im DMS. DB und Rose
[kivitendo-erp.git] / SL / DB / Helper / Filtered.pm
1 package SL::DB::Helper::Filtered;
2
3 use strict;
4 use SL::Controller::Helper::ParseFilter ();
5
6 require Exporter;
7 our @ISA    = qw(Exporter);
8 our @EXPORT = qw (filter add_filter_specs);
9
10 my %filter_spec;
11
12 sub filter {
13   my ($class, $key, $value, $prefix, $path, @additional_tokens) = @_;
14
15   my $filters = _get_filters($class);
16
17   return ($prefix . $key, $value, $path) unless $filters->{$key};
18
19   return $filters->{$key}->($key, $value, $prefix, @additional_tokens);
20 }
21
22 sub _get_filters {
23   my ($class) = @_;
24   return $filter_spec{$class} ||= {};
25 }
26
27 sub add_filter_specs {
28   my $class = shift;
29
30   my $filters = _get_filters($class);
31
32   while (@_ > 1) {
33     my $key          = shift;
34     $filters->{$key} = shift;
35   }
36 }
37
38 1;
39
40 __END__
41
42 =encoding utf-8
43
44 =head1 NAME
45
46 SL::DB::Helper::Filtered - Manager mixin for filtered results.
47
48 =head1 SYNOPSIS
49
50 In the manager:
51
52   use SL::DB::Helper::Filtered;
53
54   __PACKAGE__->add_filter_specs(
55     custom_filter_name => sub {
56       my ($key, $value, $prefix) = @_;
57       # code to handle this
58       return ($key, $value, $with_objects);
59     },
60     another_filter_name => \&_sub_to_handle_this,
61   );
62
63 In consuming code:
64
65   ($key, $value, $with_objects) = $manager_class->filter($key, $value, $prefix);
66
67 =head1 FUNCTIONS
68
69 =over 4
70
71 =item C<add_filter_specs %PARAMS>
72
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.
76
77 You can add multiple filters in one call, but only one filter per key.
78
79 =item C<filter $key, $value, $prefix>
80
81 Tells the manager to apply custom filters. If none are registered for C<$key>,
82 returns C<$key, $value>.
83
84 Otherwise the filter code is called.
85
86 =back
87
88 =head1 INTERFACE OF A CUSTOM FILTER
89
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:
92
93   __PACKAGE__->add_filter_specs(
94     all_notes => sub {
95       my ($key, $value, $prefix) = @_;
96
97       return or => [
98         $prefix . notes1 => $value,
99         $prefix . notes2 => $value,
100       ];
101     }
102   );
103
104 If someone filters for C<filter.model.all_notes:substr::ilike=telephone>, your
105 filter will get called with:
106
107   ->filter('all_notes', { ilike => '%telephone%' }, '')
108
109 and the result will be:
110
111   or => [
112     notes1 => { notes1 => '%telephone%' },
113     notes2 => { notes1 => '%telephone%' },
114   ]
115
116 The prefix is to make sure this also works when called on submodels:
117
118   C<filter.customer.model.all_notes:substr::ilike=telephone>
119
120 will pass C<customer.> as prefix so that the resulting query will be:
121
122   or => [
123     customer.notes1 => { notes1 => '%telephone%' },
124     customer.notes2 => { notes1 => '%telephone%' },
125   ]
126
127 which is pretty much what you would expect.
128
129 As a final touch consider a filter that needs to search somewhere else to work,
130 like this one:
131
132   __PACKAGE__->add_filter_specs(
133     name => sub {
134       my ($key, $value, $prefix) = @_;
135
136       return $prefix . person.name => $value,
137              $prefix . 'person';
138     },
139   };
140
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.
145
146
147 To summarize:
148
149 =over 4
150
151 =item *
152
153 You will get passed the name of your filter as C<$key> stripped of all filters
154 and escapes.
155
156 =item *
157
158 You will get passed the C<$value> processed with all filters and escapes.
159
160 =item *
161
162 You will get passed a C<$prefix> that can be prepended to all database columns
163 to make sense to Rose.
164
165 =item *
166
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.
169
170 =item *
171
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
174 an arrayref.
175
176 =back
177
178 =head1 BUGS
179
180 None yet.
181
182 =head1 AUTHOR
183
184 Sven Schöling E<lt>s.schoeling@linet-services.deE<gt>
185
186 =cut