Commit | Line | Data |
d2739840 |
1 | package Catalyst::Controller::DBIC::API::RequestArguments; |
2 | |
3 | #ABSTRACT: Provides Request argument validation |
4 | use MooseX::Role::Parameterized; |
5 | use Catalyst::Controller::DBIC::API::Types(':all'); |
6 | use MooseX::Types::Moose(':all'); |
7 | use Scalar::Util('reftype'); |
8 | use Data::Dumper; |
28098e5d |
9 | use Catalyst::Controller::DBIC::API::Validator; |
d2739840 |
10 | use namespace::autoclean; |
11 | |
12 | use Catalyst::Controller::DBIC::API::JoinBuilder; |
13 | |
d2739840 |
14 | =attribute_private search_validator |
15 | |
c0c8e1c6 |
16 | A Catalyst::Controller::DBIC::API::Validator instance used solely to validate |
17 | search parameters. |
d2739840 |
18 | |
19 | =cut |
20 | |
d2739840 |
21 | =attribute_private select_validator |
22 | |
c0c8e1c6 |
23 | A Catalyst::Controller::DBIC::API::Validator instance used solely to validate |
24 | select parameters. |
d2739840 |
25 | |
26 | =cut |
27 | |
d2739840 |
28 | =attribute_private prefetch_validator |
29 | |
c0c8e1c6 |
30 | A Catalyst::Controller::DBIC::API::Validator instance used solely to validate |
31 | prefetch parameters. |
d2739840 |
32 | |
33 | =cut |
34 | |
4e5983f2 |
35 | has [qw( search_validator select_validator )] => ( |
8ea592cb |
36 | is => 'ro', |
37 | isa => 'Catalyst::Controller::DBIC::API::Validator', |
38 | lazy => 1, |
28098e5d |
39 | builder => '_build_validator', |
40 | ); |
41 | |
42 | sub _build_validator { |
43 | return Catalyst::Controller::DBIC::API::Validator->new; |
44 | } |
d2739840 |
45 | |
46 | parameter static => ( isa => Bool, default => 0 ); |
47 | |
48 | role { |
d2739840 |
49 | my $p = shift; |
406086f3 |
50 | |
8ea592cb |
51 | if ( $p->static ) { |
52 | requires |
53 | qw( check_has_relation check_column_relation prefetch_allows ); |
d2739840 |
54 | } |
8ea592cb |
55 | else { |
56 | requires qw( _controller check_has_relation check_column_relation ); |
d2739840 |
57 | } |
58 | |
c0c8e1c6 |
59 | =attribute_public count |
d2739840 |
60 | |
c0c8e1c6 |
61 | The number of rows to be returned during paging. |
d2739840 |
62 | |
63 | =cut |
64 | |
8ea592cb |
65 | has 'count' => ( |
66 | is => 'ro', |
67 | writer => '_set_count', |
68 | isa => Int, |
d2739840 |
69 | predicate => 'has_count', |
70 | ); |
71 | |
c0c8e1c6 |
72 | =attribute_public page |
d2739840 |
73 | |
c0c8e1c6 |
74 | What page to return while paging. |
d2739840 |
75 | |
76 | =cut |
77 | |
8ea592cb |
78 | has 'page' => ( |
79 | is => 'ro', |
80 | writer => '_set_page', |
81 | isa => Int, |
d2739840 |
82 | predicate => 'has_page', |
83 | ); |
84 | |
c0c8e1c6 |
85 | =attribute_public offset |
33003023 |
86 | |
c0c8e1c6 |
87 | Specifies where to start the paged result (think SQL LIMIT). |
33003023 |
88 | |
89 | =cut |
90 | |
8ea592cb |
91 | has 'offset' => ( |
92 | is => 'ro', |
93 | writer => '_set_offset', |
94 | isa => Int, |
33003023 |
95 | predicate => 'has_offset', |
96 | ); |
97 | |
c0c8e1c6 |
98 | =attribute_public ordered_by |
d2739840 |
99 | |
c0c8e1c6 |
100 | Is passed to ->search to determine sorting. |
d2739840 |
101 | |
102 | =cut |
103 | |
8ea592cb |
104 | has 'ordered_by' => ( |
105 | is => 'ro', |
106 | writer => '_set_ordered_by', |
107 | isa => OrderedBy, |
d2739840 |
108 | predicate => 'has_ordered_by', |
8ea592cb |
109 | coerce => 1, |
110 | default => sub { $p->static ? [] : undef }, |
d2739840 |
111 | ); |
112 | |
3b3479e8 |
113 | =attribute_public grouped_by |
d2739840 |
114 | |
c0c8e1c6 |
115 | Is passed to ->search to determine aggregate results. |
d2739840 |
116 | |
117 | =cut |
118 | |
8ea592cb |
119 | has 'grouped_by' => ( |
120 | is => 'ro', |
121 | writer => '_set_grouped_by', |
122 | isa => GroupedBy, |
d2739840 |
123 | predicate => 'has_grouped_by', |
8ea592cb |
124 | coerce => 1, |
125 | default => sub { $p->static ? [] : undef }, |
d2739840 |
126 | ); |
127 | |
c0c8e1c6 |
128 | =attribute_public prefetch |
d2739840 |
129 | |
c0c8e1c6 |
130 | Is passed to ->search to optimize the number of database fetches for joins. |
d2739840 |
131 | |
132 | =cut |
133 | |
8ea592cb |
134 | has prefetch => ( |
135 | is => 'ro', |
136 | writer => '_set_prefetch', |
137 | isa => Prefetch, |
d2739840 |
138 | default => sub { $p->static ? [] : undef }, |
8ea592cb |
139 | coerce => 1, |
140 | trigger => sub { |
141 | my ( $self, $new ) = @_; |
142 | |
143 | foreach my $pf (@$new) { |
144 | if ( HashRef->check($pf) ) { |
145 | die |
146 | qq|'${\Dumper($pf)}' is not an allowed prefetch in: ${\join("\n", @{$self->prefetch_validator->templates})}| |
4e5983f2 |
147 | unless $self->prefetch_validator->validate($pf)->[0]; |
d2739840 |
148 | } |
8ea592cb |
149 | else { |
150 | die |
151 | qq|'$pf' is not an allowed prefetch in: ${\join("\n", @{$self->prefetch_validator->templates})}| |
152 | unless $self->prefetch_validator->validate( |
153 | { $pf => 1 } )->[0]; |
d2739840 |
154 | } |
155 | } |
d2739840 |
156 | }, |
157 | ); |
158 | |
c0c8e1c6 |
159 | =attribute_public search_exposes |
d2739840 |
160 | |
c0c8e1c6 |
161 | Limits what can actually be searched. If a certain column isn't indexed or |
162 | perhaps a BLOB, you can explicitly say which columns can be search to exclude |
163 | that one. |
d2739840 |
164 | |
c0c8e1c6 |
165 | Like the synopsis in DBIC::API shows, you can declare a "template" of what is |
166 | allowed (by using '*'). Each element passed in, will be converted into a |
167 | Data::DPath and added to the validator. |
d2739840 |
168 | |
169 | =cut |
170 | |
8ea592cb |
171 | has 'search_exposes' => ( |
172 | is => 'ro', |
173 | writer => '_set_search_exposes', |
174 | isa => ArrayRef [ Str | HashRef ], |
d2739840 |
175 | predicate => 'has_search_exposes', |
8ea592cb |
176 | default => sub { [] }, |
177 | trigger => sub { |
178 | my ( $self, $new ) = @_; |
d2739840 |
179 | $self->search_validator->load($_) for @$new; |
180 | }, |
181 | ); |
182 | |
c0c8e1c6 |
183 | =attribute_public search |
d2739840 |
184 | |
c0c8e1c6 |
185 | Contains the raw search parameters. Upon setting, a trigger will fire to format |
186 | them, set search_parameters and search_attributes. |
d2739840 |
187 | |
188 | Please see L</generate_parameters_attributes> for details on how the format works. |
189 | |
190 | =cut |
191 | |
8ea592cb |
192 | has 'search' => ( |
193 | is => 'ro', |
194 | writer => '_set_search', |
195 | isa => SearchParameters, |
d2739840 |
196 | predicate => 'has_search', |
8ea592cb |
197 | coerce => 1, |
198 | trigger => sub { |
199 | my ( $self, $new ) = @_; |
406086f3 |
200 | |
8ea592cb |
201 | if ( $self->has_search_exposes and @{ $self->search_exposes } ) { |
202 | foreach my $foo (@$new) { |
203 | while ( my ( $k, $v ) = each %$foo ) { |
d2739840 |
204 | local $Data::Dumper::Terse = 1; |
8ea592cb |
205 | die |
206 | qq|{ $k => ${\Dumper($v)} } is not an allowed search term in: ${\join("\n", @{$self->search_validator->templates})}| |
207 | unless $self->search_validator->validate( |
208 | { $k => $v } )->[0]; |
d2739840 |
209 | } |
210 | } |
211 | } |
8ea592cb |
212 | else { |
213 | foreach my $foo (@$new) { |
214 | while ( my ( $k, $v ) = each %$foo ) { |
215 | $self->check_column_relation( { $k => $v } ); |
d2739840 |
216 | } |
217 | } |
218 | } |
406086f3 |
219 | |
8ea592cb |
220 | my ( $search_parameters, $search_attributes ) = |
221 | $self->generate_parameters_attributes($new); |
d2739840 |
222 | $self->_set_search_parameters($search_parameters); |
223 | $self->_set_search_attributes($search_attributes); |
224 | |
225 | }, |
226 | ); |
227 | |
c0c8e1c6 |
228 | =attribute_public search_parameters |
d2739840 |
229 | |
c0c8e1c6 |
230 | Stores the formatted search parameters that will be passed to ->search. |
d2739840 |
231 | |
232 | =cut |
233 | |
8ea592cb |
234 | has search_parameters => ( |
235 | is => 'ro', |
236 | isa => SearchParameters, |
237 | writer => '_set_search_parameters', |
d2739840 |
238 | predicate => 'has_search_parameters', |
8ea592cb |
239 | coerce => 1, |
240 | default => sub { [ {} ] }, |
d2739840 |
241 | ); |
242 | |
c0c8e1c6 |
243 | =attribute_public search_attributes |
d2739840 |
244 | |
c0c8e1c6 |
245 | Stores the formatted search attributes that will be passed to ->search. |
d2739840 |
246 | |
247 | =cut |
248 | |
8ea592cb |
249 | has search_attributes => ( |
250 | is => 'ro', |
251 | isa => HashRef, |
252 | writer => '_set_search_attributes', |
253 | predicate => 'has_search_attributes', |
d2739840 |
254 | lazy_build => 1, |
255 | ); |
256 | |
c0c8e1c6 |
257 | =attribute_public search_total_entries |
d2739840 |
258 | |
c0c8e1c6 |
259 | Stores the total number of entries in a paged search result. |
d2739840 |
260 | |
261 | =cut |
262 | |
8ea592cb |
263 | has search_total_entries => ( |
264 | is => 'ro', |
265 | isa => Int, |
266 | writer => '_set_search_total_entries', |
d2739840 |
267 | predicate => 'has_search_total_entries', |
268 | ); |
269 | |
c0c8e1c6 |
270 | =attribute_public select_exposes |
d2739840 |
271 | |
c0c8e1c6 |
272 | Limits what can actually be selected. Use this to whitelist database functions |
273 | (such as COUNT). |
d2739840 |
274 | |
c0c8e1c6 |
275 | Like the synopsis in DBIC::API shows, you can declare a "template" of what is |
276 | allowed (by using '*'). Each element passed in, will be converted into a |
277 | Data::DPath and added to the validator. |
d2739840 |
278 | |
279 | =cut |
280 | |
8ea592cb |
281 | has 'select_exposes' => ( |
282 | is => 'ro', |
283 | writer => '_set_select_exposes', |
284 | isa => ArrayRef [ Str | HashRef ], |
d2739840 |
285 | predicate => 'has_select_exposes', |
8ea592cb |
286 | default => sub { [] }, |
287 | trigger => sub { |
288 | my ( $self, $new ) = @_; |
d2739840 |
289 | $self->select_validator->load($_) for @$new; |
290 | }, |
291 | ); |
292 | |
c0c8e1c6 |
293 | =attribute_public select |
d2739840 |
294 | |
c0c8e1c6 |
295 | Is the search attribute that allows you to both limit what is returned in the |
296 | result set and also make use of database functions like COUNT. |
d2739840 |
297 | |
298 | Please see L<DBIx::Class::ResultSet/select> for more details. |
299 | |
300 | =cut |
301 | |
8ea592cb |
302 | has select => ( |
303 | is => 'ro', |
304 | writer => '_set_select', |
305 | isa => SelectColumns, |
d2739840 |
306 | predicate => 'has_select', |
8ea592cb |
307 | default => sub { $p->static ? [] : undef }, |
308 | coerce => 1, |
309 | trigger => sub { |
310 | my ( $self, $new ) = @_; |
311 | if ( $self->has_select_exposes ) { |
312 | foreach my $val (@$new) { |
d2739840 |
313 | die "'$val' is not allowed in a select" |
314 | unless $self->select_validator->validate($val); |
315 | } |
316 | } |
8ea592cb |
317 | else { |
318 | $self->check_column_relation( $_, $p->static ) for @$new; |
d2739840 |
319 | } |
320 | }, |
321 | ); |
322 | |
c0c8e1c6 |
323 | =attribute_public as |
d2739840 |
324 | |
c0c8e1c6 |
325 | Is the search attribute compliment to L</select> that allows you to label |
326 | columns for object inflaction and actually reference database functions like |
327 | COUNT. |
d2739840 |
328 | |
329 | Please see L<DBIx::Class::ResultSet/as> for more details. |
330 | |
331 | =cut |
332 | |
8ea592cb |
333 | has as => ( |
334 | is => 'ro', |
335 | writer => '_set_as', |
336 | isa => AsAliases, |
d2739840 |
337 | default => sub { $p->static ? [] : undef }, |
8ea592cb |
338 | trigger => sub { |
339 | my ( $self, $new ) = @_; |
340 | if ( $self->has_select ) { |
341 | die |
342 | "'as' argument count (${\scalar(@$new)}) must match 'select' argument count (${\scalar(@{$self->select || []})})" |
343 | unless @$new == @{ $self->select || [] }; |
d2739840 |
344 | } |
8ea592cb |
345 | elsif ( defined $new ) { |
d2739840 |
346 | die "'as' is only valid if 'select is also provided'"; |
347 | } |
348 | } |
349 | ); |
350 | |
c0c8e1c6 |
351 | =attribute_public joins |
d2739840 |
352 | |
c0c8e1c6 |
353 | Holds the top level JoinBuilder object used to keep track of joins automagically |
354 | while formatting complex search parameters. |
d2739840 |
355 | |
c0c8e1c6 |
356 | Provides the method 'build_joins' which returns the 'join' attribute for |
357 | search_attributes. |
d2739840 |
358 | |
359 | =cut |
360 | |
8ea592cb |
361 | has joins => ( |
362 | is => 'ro', |
363 | isa => JoinBuilder, |
d2739840 |
364 | lazy_build => 1, |
8ea592cb |
365 | handles => { build_joins => 'joins', } |
d2739840 |
366 | ); |
367 | |
c0c8e1c6 |
368 | =attribute_public request_data |
d2739840 |
369 | |
c0c8e1c6 |
370 | Holds the raw (but deserialized) data for this request. |
d2739840 |
371 | |
372 | =cut |
373 | |
8ea592cb |
374 | has 'request_data' => ( |
375 | is => 'ro', |
376 | isa => HashRef, |
377 | writer => '_set_request_data', |
533075c7 |
378 | predicate => 'has_request_data', |
8ea592cb |
379 | trigger => sub { |
380 | my ( $self, $new ) = @_; |
d2739840 |
381 | my $controller = $self->_controller; |
533075c7 |
382 | return unless defined($new) && keys %$new; |
8ea592cb |
383 | $self->_set_prefetch( $new->{ $controller->prefetch_arg } ) |
384 | if exists $new->{ $controller->prefetch_arg }; |
385 | $self->_set_select( $new->{ $controller->select_arg } ) |
386 | if exists $new->{ $controller->select_arg }; |
387 | $self->_set_as( $new->{ $controller->as_arg } ) |
388 | if exists $new->{ $controller->as_arg }; |
389 | $self->_set_grouped_by( $new->{ $controller->grouped_by_arg } ) |
390 | if exists $new->{ $controller->grouped_by_arg }; |
391 | $self->_set_ordered_by( $new->{ $controller->ordered_by_arg } ) |
392 | if exists $new->{ $controller->ordered_by_arg }; |
393 | $self->_set_count( $new->{ $controller->count_arg } ) |
394 | if exists $new->{ $controller->count_arg }; |
395 | $self->_set_page( $new->{ $controller->page_arg } ) |
396 | if exists $new->{ $controller->page_arg }; |
397 | $self->_set_offset( $new->{ $controller->offset_arg } ) |
398 | if exists $new->{ $controller->offset_arg }; |
399 | $self->_set_search( $new->{ $controller->search_arg } ) |
400 | if exists $new->{ $controller->search_arg }; |
d2739840 |
401 | } |
402 | ); |
403 | |
8ea592cb |
404 | method _build_joins => sub { |
405 | return Catalyst::Controller::DBIC::API::JoinBuilder->new( |
406 | name => 'TOP' ); |
407 | }; |
d2739840 |
408 | |
409 | =method_protected format_search_parameters |
410 | |
c0c8e1c6 |
411 | Iterates through the provided arrayref calling generate_column_parameters on |
412 | each one. |
d2739840 |
413 | |
414 | =cut |
415 | |
8ea592cb |
416 | method format_search_parameters => sub { |
417 | my ( $self, $params ) = @_; |
406086f3 |
418 | |
d2739840 |
419 | my $genparams = []; |
420 | |
8ea592cb |
421 | foreach my $param (@$params) { |
422 | push( |
423 | @$genparams, |
424 | $self->generate_column_parameters( |
425 | $self->stored_result_source, |
426 | $param, $self->joins |
427 | ) |
428 | ); |
d2739840 |
429 | } |
430 | |
431 | return $genparams; |
432 | }; |
433 | |
434 | =method_protected generate_column_parameters |
435 | |
c0c8e1c6 |
436 | Recursively generates properly aliased parameters for search building a new |
437 | JoinBuilder each layer of recursion. |
d2739840 |
438 | |
439 | =cut |
440 | |
8ea592cb |
441 | method generate_column_parameters => sub { |
442 | my ( $self, $source, $param, $join, $base ) = @_; |
d2739840 |
443 | $base ||= 'me'; |
02b625cd |
444 | my $search_params = {}; |
d2739840 |
445 | |
4cb8623a |
446 | # return non-hashref params unaltered |
447 | return $param |
448 | unless ref $param eq 'HASH'; |
449 | |
d2739840 |
450 | # build up condition |
8ea592cb |
451 | foreach my $column ( keys %$param ) { |
4cb8623a |
452 | my $value = $param->{$column}; |
8ea592cb |
453 | if ( $source->has_relationship($column) ) { |
454 | |
11ba2ccc |
455 | # check if the value isn't a hashref |
4cb8623a |
456 | unless ( ref $value eq 'HASH' ) |
d2739840 |
457 | { |
8ea592cb |
458 | $search_params->{ join( '.', $base, $column ) } = |
4cb8623a |
459 | $value; |
d2739840 |
460 | next; |
461 | } |
462 | |
8ea592cb |
463 | $search_params = { |
464 | %$search_params, |
465 | %{ $self->generate_column_parameters( |
466 | $source->related_source($column), |
4cb8623a |
467 | $value, |
8ea592cb |
468 | Catalyst::Controller::DBIC::API::JoinBuilder->new( |
469 | parent => $join, |
470 | name => $column |
471 | ), |
472 | $column |
473 | ) |
474 | } |
475 | }; |
d2739840 |
476 | } |
8ea592cb |
477 | elsif ( $source->has_column($column) ) { |
478 | $search_params->{ join( '.', $base, $column ) } = |
479 | $param->{$column}; |
d2739840 |
480 | } |
4cb8623a |
481 | elsif ( $column eq '-or' || $column eq '-and' || $column eq '-not' ) { |
482 | # either an arrayref or hashref |
483 | if ( ref $value eq 'HASH' ) { |
484 | $search_params->{$column} = $self->generate_column_parameters( |
485 | $source, |
486 | $value, |
487 | $join, |
488 | $base, |
489 | ); |
490 | } |
491 | elsif ( ref $value eq 'ARRAY' ) { |
492 | push @{$search_params->{$column}}, |
493 | $self->generate_column_parameters( |
494 | $source, |
495 | $_, |
496 | $join, |
497 | $base, |
498 | ) |
499 | for @$value; |
500 | } |
501 | else { |
502 | die "unsupported value '$value' for column '$column'\n"; |
503 | } |
504 | } |
8ea592cb |
505 | |
11ba2ccc |
506 | # might be a sql function instead of a column name |
507 | # e.g. {colname => {like => '%foo%'}} |
8ea592cb |
508 | else { |
11ba2ccc |
509 | # but only if it's not a hashref |
4cb8623a |
510 | unless ( ref $value eq 'HASH' ) { |
8ea592cb |
511 | $search_params->{ join( '.', $base, $column ) } = |
512 | $param->{$column}; |
11ba2ccc |
513 | } |
514 | else { |
4cb8623a |
515 | die "unsupported value '$value' for column '$column'\n"; |
11ba2ccc |
516 | } |
517 | } |
d2739840 |
518 | } |
519 | |
520 | return $search_params; |
521 | }; |
522 | |
523 | =method_protected generate_parameters_attributes |
524 | |
c0c8e1c6 |
525 | Takes the raw search arguments and formats them by calling |
526 | format_search_parameters. Then builds the related attributes, preferring |
527 | request-provided arguments for things like grouped_by over statically configured |
528 | options. Finally tacking on the appropriate joins. |
529 | |
530 | Returns a list of both formatted search parameters and attributes. |
d2739840 |
531 | |
532 | =cut |
533 | |
8ea592cb |
534 | method generate_parameters_attributes => sub { |
535 | my ( $self, $args ) = @_; |
d2739840 |
536 | |
8ea592cb |
537 | return ( $self->format_search_parameters($args), |
538 | $self->search_attributes ); |
d2739840 |
539 | }; |
540 | |
8ea592cb |
541 | method _build_search_attributes => sub { |
542 | my ( $self, $args ) = @_; |
543 | my $static = $self->_controller; |
544 | my $search_attributes = { |
545 | group_by => $self->grouped_by |
546 | || ( |
547 | ( scalar( @{ $static->grouped_by } ) ) ? $static->grouped_by |
548 | : undef |
549 | ), |
550 | order_by => $self->ordered_by |
551 | || ( |
552 | ( scalar( @{ $static->ordered_by } ) ) ? $static->ordered_by |
553 | : undef |
554 | ), |
555 | select => $self->select |
556 | || ( |
557 | ( scalar( @{ $static->select } ) ) ? $static->select |
558 | : undef |
559 | ), |
560 | as => $self->as |
561 | || ( ( scalar( @{ $static->as } ) ) ? $static->as : undef ), |
d2739840 |
562 | prefetch => $self->prefetch || $static->prefetch || undef, |
8ea592cb |
563 | rows => $self->count || $static->count, |
564 | page => $static->page, |
565 | offset => $self->offset, |
566 | join => $self->build_joins, |
d2739840 |
567 | }; |
568 | |
8ea592cb |
569 | if ( $self->has_page ) { |
33003023 |
570 | $search_attributes->{page} = $self->page; |
571 | } |
8ea592cb |
572 | elsif (!$self->has_page |
573 | && defined( $search_attributes->{offset} ) |
574 | && defined( $search_attributes->{rows} ) ) |
33003023 |
575 | { |
8ea592cb |
576 | $search_attributes->{page} = |
577 | $search_attributes->{offset} / $search_attributes->{rows} + 1; |
fa2501f0 |
578 | delete $search_attributes->{offset}; |
33003023 |
579 | } |
33003023 |
580 | |
8ea592cb |
581 | $search_attributes = { |
582 | map {@$_} |
583 | grep { |
584 | defined( $_->[1] ) |
585 | ? ( ref( $_->[1] ) |
586 | && reftype( $_->[1] ) eq 'HASH' |
587 | && keys %{ $_->[1] } ) |
588 | || ( ref( $_->[1] ) |
589 | && reftype( $_->[1] ) eq 'ARRAY' |
590 | && @{ $_->[1] } ) |
591 | || length( $_->[1] ) |
592 | : undef |
593 | } |
594 | map { [ $_, $search_attributes->{$_} ] } |
595 | keys %$search_attributes |
d2739840 |
596 | }; |
597 | |
8ea592cb |
598 | if ( $search_attributes->{page} && !$search_attributes->{rows} ) { |
d2739840 |
599 | die 'list_page can only be used with list_count'; |
600 | } |
406086f3 |
601 | |
8ea592cb |
602 | if ( $search_attributes->{select} ) { |
603 | |
d2739840 |
604 | # make sure all columns have an alias to avoid ambiguous issues |
605 | # but allow non strings (eg. hashrefs for db procs like 'count') |
606 | # to pass through unmolested |
8ea592cb |
607 | $search_attributes->{select} = [ |
608 | map { ( Str->check($_) && $_ !~ m/\./ ) ? "me.$_" : $_ } |
609 | ( ref $search_attributes->{select} ) |
610 | ? @{ $search_attributes->{select} } |
611 | : $search_attributes->{select} |
612 | ]; |
d2739840 |
613 | } |
614 | |
615 | return $search_attributes; |
406086f3 |
616 | |
d2739840 |
617 | }; |
618 | |
619 | }; |
8ea592cb |
620 | |
d2739840 |
621 | =head1 DESCRIPTION |
622 | |
8ea592cb |
623 | RequestArguments embodies those arguments that are provided as part of a request |
624 | or effect validation on request arguments. This Role can be consumed in one of |
625 | two ways. As this is a parameterized Role, it accepts a single argument at |
626 | composition time: 'static'. This indicates that those parameters should be |
627 | stored statically and used as a fallback when the current request doesn't |
628 | provide them. |
d2739840 |
629 | |
630 | =cut |
631 | |
d2739840 |
632 | 1; |