File Coverage

blib/lib/Teng/Plugin/SearchBySQLAbstractMore.pm
Criterion Covered Total %
statement 113 115 98.2
branch 46 54 85.1
condition 20 31 64.5
subroutine 19 19 100.0
pod 6 6 100.0
total 204 225 90.6


line stmt bran cond sub pod time code
1             package Teng::Plugin::SearchBySQLAbstractMore;
2              
3 14     14   313519 use strict;
  14         39  
  14         399  
4 14     14   71 use warnings;
  14         31  
  14         347  
5 14     14   728 use utf8;
  14         41  
  14         76  
6 14     14   334 use Carp ();
  14         28  
  14         225  
7 14     14   653 use Teng::Iterator;
  14         23296  
  14         335  
8 14     14   6134 use Data::Page;
  14         71601  
  14         118  
9 14     14   10770 use SQL::Abstract::More;
  14         369568  
  14         88  
10              
11             our @EXPORT = qw/search_by_sql_abstract_more install_sql_abstract_more create_sql_by_sql_abstract_more
12             sql_abstract_more_instance sql_abstract_more_new_option sql_abstract_more_instance/;
13             my %sql_abstract_more;
14             my %new_option;
15              
16             sub import {
17 2     2   26 my ($class) = @_;
18 2         15 my ($caller) = caller(0);
19 14     14   183464 no strict 'refs';
  14         56  
  14         1456  
20 2         7 *{$caller . '::install_sql_abstract_more'} = \&install_sql_abstract_more;
  2         17  
21 2         8 *{$caller . '::sql_abstract_more_new_option'} = \&install_sql_abstract_more_new_option;
  2         2007  
22             }
23              
24             sub _init {
25 14     14   122 no strict 'refs';
  14         35  
  14         20501  
26 12     12   42 my $class = shift;
27 12         28 *{$class . '::sql_abstract_more_instance'} = \&sql_abstract_more_instance
  12         81  
28             }
29              
30             sub sql_abstract_more_new_option {
31 2 50   2 1 1419 my $class = ref($_[0]) ? ref($_[0]) : shift;
32 2         9 $new_option{$class} = {@_};
33             }
34              
35             sub sql_abstract_more_instance {
36 37 100   37 1 7872 my $class = ref($_[0]) ? ref($_[0]) : shift;
37 37   66     195 $sql_abstract_more{$class} ||= SQL::Abstract::More->new(%{$new_option{$class}});
  9         88  
38             }
39              
40             sub create_sql_by_sql_abstract_more {
41 2     2 1 21 my ($self, $table_name, $where, $_opt) = @_;
42 2         7 ($table_name, my $args) = _arrange_args($table_name, $where, $_opt);
43 2         6 return $self->sql_abstract_more_instance->select(%$args);
44             }
45              
46             sub search_by_sql_abstract_more {
47 16     16 1 100640 my ($self, $table_name, $where, $_opt) = @_;
48 16         46 ($table_name, my $args) = _arrange_args($table_name, $where, $_opt);
49 16 100       59 my $table = $self->schema->get_table($table_name) or Carp::croak("No such table $table_name");
50 15         184 my ($sql, @binds) = $self->sql_abstract_more_instance->select(%$args);
51              
52 15         11663 my $sth = $self->execute($sql, \@binds);
53 15         2624 my $itr = Teng::Iterator->new(
54             teng => $self,
55             sth => $sth,
56             sql => $sql,
57             row_class => $self->schema->get_row_class($table_name),
58             table_name => $table_name,
59             suppress_object_creation => $self->suppress_row_objects,
60             );
61              
62 15 100       495 return wantarray ? $itr->all : $itr;
63             }
64              
65             sub _arrange_args {
66 31     31   3522 my ($table_name, $where, $_opt) = @_;
67 31         76 my ($page, $rows, %args);
68 31 100       93 if (ref $table_name eq 'HASH') {
69 9         22 my $_opt = $table_name;
70             $table_name = ref $_opt->{-from} eq 'ARRAY' ? $_opt->{-from}->[0] eq '-join'
71             ? $_opt->{-from}->[1] : $_opt->{-from}->[0]
72 9 50       46 : $_opt->{-from};
    100          
73 9         62 $table_name =~s{(?: +)?\|.+$}{};
74 9         43 %args = %$_opt;
75 9 100       42 if ($page = delete $args{-page}) {
76 4         20 $rows = delete $args{-rows};
77 4         13 $args{-offset} = $rows * ($page - 1);
78 4         10 $args{-limit} = $rows;
79             }
80              
81             } else {
82 22   66     179 $_opt->{from} ||= ($_opt->{-from} || $table_name);
      66        
83 22         75 foreach my $key (keys %$_opt) {
84 75 100       238 my $_k = $key =~m{^\-} ? $key : '-' . $key;
85 75   66     266 $args{$_k} ||= $_opt->{$key};
86             }
87 22   100     90 $args{-where} = $where || {};
88              
89 22 100 66     120 if ($page = delete $args{page} || delete $args{-page}) {
90 8   33     38 $rows = delete $args{rows} || delete $args{-rows};
91 8         17 $args{-limit} = $rows;
92 8         24 $args{-offset} = $rows * ($page - 1);
93             }
94              
95 22         77 _arrange_order_by_and_columns(\%args);
96             }
97 31         119 return($table_name, \%args, $rows, $page);
98             }
99              
100             sub _arrange_order_by_and_columns {
101 22     22   45 my ($args) = @_;
102 22 100       86 if (exists $args->{-order_by}) {
103 14 100       61 if (ref $args->{-order_by} eq 'HASH') {
    100          
104 1         2 my ($column, $asc_desc) = each %{$args->{-order_by}};
  1         4  
105 1         4 $asc_desc = lc $asc_desc;
106 1 50 33     6 if ($asc_desc eq 'asc' or $asc_desc eq 'desc') {
107 1         3 $args->{-order_by} = {'-' . $asc_desc, $column};
108             }
109 1         2 $args->{-order_by} = [{%{$args->{-order_by}}}];
  1         3  
110             } elsif (ref $args->{-order_by} eq 'ARRAY') {
111 11         18 foreach my $def (@{$args->{-order_by}}) {
  11         27  
112 11 100       36 if (ref $def eq 'HASH') {
113 1         2 my ($column, $asc_desc) = each %{$def};
  1         3  
114 1         2 $asc_desc = lc $asc_desc;
115 1 50 33     6 if ($asc_desc eq 'asc' or $asc_desc eq 'desc') {
116 1         4 $def = {'-' . $asc_desc, $column};
117             }
118             }
119             }
120             }
121             }
122 22 100       64 if (exists $args->{-columns}) {
123 10         17 foreach my $col (@{$args->{-columns}}) {
  10         25  
124 18 50       44 $col = $$col if ref $col;
125             }
126             }
127             }
128              
129             sub replace_teng_search {
130 2     2 1 5587 undef &Teng::search;
131 2         22 *Teng::search = \*search_by_sql_abstract_more;
132             }
133              
134             sub install_sql_abstract_more {
135 10     10 1 25570 my ($self, %opt) = @_;
136 10 100       47 my $class = ref $self ? ref $self : $self;
137 10         22 my $alias = 'search';
138 10 100 100     1397 if (exists $opt{replace} and $opt{replace}) {
139 1         6 Teng::Plugin::SearchBySQLAbstractMore->replace_teng_search;
140             } else {
141 9   100     63 $alias = $opt{alias} || 'search';
142 9 50       64 $alias = 'search' if $alias eq '1';
143 9         1423 $class->load_plugin('SearchBySQLAbstractMore',
144             {alias => {search_by_sql_abstract_more => $alias}});
145             }
146              
147 10 100       1283 if (my $pager_plugin = $opt{pager}) {
148 9 100       43 $alias = $opt{pager_alias} ? $opt{pager_alias} : $alias . '_with_pager';
149 9 100       29 if ($pager_plugin eq '1') {
150 1         5 $class->load_plugin('SearchBySQLAbstractMore::Pager',
151             {alias => {search_by_sql_abstract_more_with_pager => $alias}});
152             } else {
153 8         34 my $pager_plugin_name = lc $pager_plugin;
154 8 100       37 if ($pager_plugin_name eq 'simple') {
    50          
    50          
155 1         2 $pager_plugin = 'Pager';
156             } elsif ($pager_plugin_name eq 'mysql_found_rows') {
157 0         0 $pager_plugin = 'Pager::MySQLFoundRows';
158             } elsif ($pager_plugin_name eq 'count') {
159 0         0 $pager_plugin = 'Pager::Count';
160             }
161 8         51 $class->load_plugin('SearchBySQLAbstractMore::' . $pager_plugin,
162             {alias => {search_by_sql_abstract_more_with_pager => $alias}});
163             #} elsif (ref $self and $self->dbh->{Driver}->{Name} eq 'mysql') {
164             # # object is passed and driver is mysql
165             # $class->load_plugin('SearchBySQLAbstractMore::Pager::MySQLFoundRows',
166             # {alias => {search_by_sql_abstract_more_with_pager => 'search_with_pager'}});
167             }
168             }
169             }
170              
171             1;
172              
173             =pod
174              
175             =head1 NAME
176              
177             Teng::Plugin::SearchBySQLAbstractMore - use L<SQL::Abstract::More> as Query Builder for Teng
178              
179             =cut
180              
181             our $VERSION = '0.13';
182              
183              
184             =head1 SYNOPSIS
185              
186             package MyApp::DB;
187             use parent qw/Teng/;
188             __PACKAGE__->load_plugin('SearchBySQLAbstractMore');
189             __PACAKGE__->sql_abstract_more_new_option(sql_dialect => 'Oracle'); # If you want to pass SQL::Abstract::More new options
190              
191             package main;
192             my $db = MyApp::DB->new(dbh => $dbh);
193              
194             my $itr = $db->search_by_sql_abstract_more('user' => {type => 3});
195             my @rows = $db->search_by_sql_abstract_more('user' => {type => 3}, {rows => 5});
196              
197             # use pager
198             my $page = $c->req->param('page') || 1;
199             my ($rows, $pager) = $db->search_by_sql_abstract_more_with_pager('user' => {type => 3}, {page => $page, rows => 5});
200            
201             If you want to replace Teng search
202              
203             package MyApp::DB;
204             use parent qw/Teng/;
205             __PACKAGE__->load_plugin('SearchBySQLAbstractMore');
206             __PACKAGE__->install_sql_abstract_more;
207             # now, search method is replaced by search_by_sql_abstract_more
208              
209             If you want to load pager at the same time
210              
211             # search_with_pager from SearchBySQLAbstractMore::Pager
212             __PACKAGE__->install_sql_abstract_more(pager => 'Pager');
213             # search_with_pager from SearchBySQLAbstractMore::Pager::MySQLFoundRows
214             __PACKAGE__->install_sql_abstract_more(pager => 'Pager::MySQLFoundRows');
215             # search_with_pager from SearchBySQLAbstractMore::Pager::Count
216             __PACKAGE__->install_sql_abstract_more(pager => 'Pager::Count');
217              
218             Create complex SQL using SQL::Abstract::More.
219              
220             Compatible usage with Teng's search method.
221              
222             $teng->search_by_sql_abstract_more
223             ('table1',
224             { name => { like => '%perl%'},
225             -and => [
226             {x => 1},
227             {y => [-or => {'>' => 2}, {'<' => 10}]},
228             ],
229             },
230             {
231             from => ['-join',
232             'table1|t1',
233             't1.id=t2.id',
234             'table2|t2',
235             't1.id=t3.table1_id,t2.id=t3.table2_id',
236             'table3|t3',
237             ],
238             columns => ['x', 'y', 'min(age) as min_age', 'max(age) as max_age'],
239             group_by => ['x', 'y'],
240             having => {'max_age' => {'<' => 10}},
241             },
242             );
243             # SELECT x, y, min(age) as min_age, max(age) as max_age
244             # FROM table1 AS t1
245             # INNER JOIN table2 AS t2 ON ( t1.id = t2.id )
246             # INNER JOIN table3 AS t3 ON ( ( t1.id = t3.table1_id AND t2.id = t3.table2_id ) )
247             # WHERE ( ( ( x = ? AND ( y > ? OR y < ? ) ) AND name LIKE ? ) )
248             # GROUP BY x, y HAVING ( max_age < ? );
249              
250             SQL::Abstract::More original usage(as first argument, use hash ref instead of table name):
251              
252             $teng->search_by_sql_abstract_more(
253             {
254             -columns => ['x', 'y', 'min(age) as min_age', 'max(age) as max_age'],
255             -from => [-join,
256             'table1|t1',
257             't1.id=t2.id',
258             'table2|t2',
259             't1.id=t3.table1_id,t2.id=t3.table2_id',
260             'table3|t3',
261             ],
262             -group_by => ['x', 'y'],
263             -having => {'max_age' => {'<' => 10'}},
264             -where => {
265             name => { like => '%perl%'},
266             -and => [
267             {x => 1},
268             {y => [-or => {'>' => 2}, {'<' => 10}]},
269             ],
270             },
271             },
272             );
273             # SQL is as same as the avobe code.
274              
275             Using pager.
276              
277             Compatible usage:
278              
279             $teng->search_by_sql_abstract_more(
280             'table', {
281             name => 1,
282             age => 10,
283             },
284             {
285             -columns => ['x', 'y'],
286             -from => ['table'],
287             -page => 2,
288             -rows => 20,
289             },
290             );
291              
292             Originaly usage:
293              
294             $teng->search_by_sql_abstract_more(
295             {
296             -columns => ['x', 'y'],
297             -from => ['table'],
298             -where => {
299             name => 1,
300             age => 10,
301             },
302             -page => 2,
303             -rows => 20,
304             },
305             );
306              
307             Generate SQL by SQLAbstractMore
308              
309             ($sql, @binds) = $teng->create_sql_by_sql_abstract_more($table, $where, $opt);
310              
311             It returns SQL and bind values with same args of C<search_bys_sql_abstract_more> method.
312              
313             =head1 METHODS
314              
315             =head2 search_by_sql_abstract_more
316              
317             see SYNOPSIS.
318              
319             =head2 create_sql_by_sql_abstract_more
320              
321             ($sql, @binds) = $teng->create_sql_by_sql_abstract_more($table, $where, $opt);
322              
323             This method returns SQL statement and its bind values.
324             It doesn't check table is in schema.
325              
326             =head1 CLASS METHOD
327              
328             =head2 sql_abstract_more_instance
329              
330             YourClass->sql_abstract_more_instance;
331              
332             return SQL::Abstract::More object.
333              
334             =head2 sql_abstract_more_new_option
335              
336             YourClass->sql_abstract_more_new_option(sql_dialect => 'Oracle');
337              
338             This method's arguments are passed to SQL::Abstract::More->new().
339             see L<SQL::Abstract::More> new options.
340              
341             =head2 replace_teng_search
342              
343             If you want to replace C<search> method of original Teng, call this.
344              
345             Teng::Plugin::SearchBySQLAbstractMore->replace_teng_search;
346              
347             It is useful when you wrap C<search> method in your module and call Teng's C<search> method in it
348             and you want to use same usage with SQL::Abstract::More.
349              
350             =head2 install_sql_abstract_more
351              
352             package YourClass;
353             use Teng::Plugin::SearchBySQLAbstract::More; # no need to use ->load_plugin();
354            
355             YourClass->install_sql_abstract_more; # search_sql_abstract_more is defined as YourClass::search
356             YourClass->install_sql_abstract_more(alias => 1); # same as the above
357              
358             YourClass->install_sql_abstract_more(replace => 1); # Teng::Search is replaced by search_sql_abstract_more
359            
360             YourClass->install_sql_abstract_more(alias => 'complex_search');
361             # sql_abstract_more is defined as YourClass::complex_search
362            
363             YourClass->install_sql_abstract_more(alias => 'complex_search', pager => 1);
364             # sql_abstract_more is defined as YourClass::complex_search
365             # sql_abstract_more_pager is defined as YourClass::complex_search_with_pager
366            
367             YourClass->install_sql_abstract_more(alias => 'complex_search', pager => 1, pager_alias => 'complex_search_paged');
368             # sql_abstract_more is defined as YourClass::complex_search
369             # sql_abstract_more_pager is defined as YourClass::complex_search_paged
370            
371             # use different pager
372             YourClass->install_sql_abstract_more(pager => 1); # or pager => 'simple' / 'Pager'
373             YourClass->install_sql_abstract_more(pager => 'mysql_found_rows'); # or pager => 'Pager::MySQL::FoundRows'
374             YourClass->install_sql_abstract_more(pager => 'count'); # or pager => 'Pager::Count'
375              
376             It call replace_teng_search if replace option is passed and it is true
377             and loads pager plugin with alias option if pager option is true.
378             C<search> and C<search_with_pager> are installed to your class.
379              
380             This method can take the following options.
381              
382             =head3 replace
383              
384             If you want to replace Teng's search method, pass this option.
385              
386             YourClass->install_sql_abstract_more(replace => 1);
387              
388             =head3 alias
389              
390             YourClass->install_sql_abstract_more(alias => 'complex_search');
391             YourClass->install_sql_abstract_more(pager => 'Pager', alias => 'complex_search');
392              
393             This is equals to:
394              
395             YourClass->load_plugin('Teng::Plugin::SearchBySQLAbstractMore', {
396             alias => 'search_by_sql_abstract_more' => 'complex_search',
397             });
398             YourClass->load_plugin('Teng::Plugin::SearchBySQLAbstractMore::Pager', {
399             alias => 'search_by_sql_abstract_more_with_pager' => 'complex_search_with_pager',
400             });
401              
402             =head3 pager_alias
403              
404             If you want to use different alias for pager search.
405              
406             YourClass->install_sql_abstract_more(pager => 'Pager', pager_alias => 'complex_search_with_pager');
407              
408             This is equals to:
409              
410             YourClass->load_plugin('Teng::Plugin::SearchBySQLAbstractMore', {
411             alias => 'search_by_sql_abstract_more' => 'search',
412             });
413             YourClass->load_plugin('Teng::Plugin::SearchBySQLAbstractMore::Pager', {
414             alias => 'search_by_sql_abstract_more_with_pager' => 'complex_search_with_pager',
415             });
416              
417             =head3 pager
418              
419             Pass pager plugin name or 1.
420              
421             YourClass->install_sql_abstract_more(pager => 1); # load SearchBySQLAbstractMore::Pager
422             YourClass->install_sql_abstract_more(pager => 'Pager'); # same as the above
423             YourClass->install_sql_abstract_more(pager => 'simple'); # same as the above
424              
425             YourClass->install_sql_abstract_more(pager => 'Pager::MySQLFoundRows');# load SearchBySQLAbstractMore::Pager::MySQLFoundRows
426             YourClass->install_sql_abstract_more(pager => 'mysql_found_rows'); # same as the above
427              
428             YourClass->install_sql_abstract_more(pager => 'Pager::Count'); # load SearchBySQLAbstractMore::Pager::Count
429             YourClass->install_sql_abstract_more(pager => 'count'); # same as the above
430              
431             =head1 AUTHOR
432              
433             Ktat, C<< <ktat at cpan.org> >>
434              
435             =head1 BUGS
436              
437             Please report any bugs or feature requests to C<bug-teng-plugin-searchbysqlabstractmore at rt.cpan.org>, or through
438             the web interface at L<http://rt.cpan.org/NoAuth/ReportBug.html?Queue=Teng-Plugin-SearchBySQLAbstractMore>. I will be notified, and then you'll
439             automatically be notified of progress on your bug as I make changes.
440              
441             =head1 SUPPORT
442              
443             You can find documentation for this module with the perldoc command.
444              
445             perldoc Teng::Plugin::SearchBySQLAbstractMore
446              
447             You can also look for information at:
448              
449             =over 4
450              
451             =item * RT: CPAN's request tracker
452              
453             L<http://rt.cpan.org/NoAuth/Bugs.html?Dist=Teng-Plugin-SearchBySQLAbstractMore>
454              
455             =item * AnnoCPAN: Annotated CPAN documentation
456              
457             L<http://annocpan.org/dist/Teng-Plugin-SearchBySQLAbstractMore>
458              
459             =item * CPAN Ratings
460              
461             L<http://cpanratings.perl.org/d/Teng-Plugin-SearchBySQLAbstractMore>
462              
463             =item * Search CPAN
464              
465             L<http://search.cpan.org/dist/Teng-Plugin-SearchBySQLAbstractMore/>
466              
467             =back
468              
469              
470             =head1 SEE ALSO
471              
472             =over 4
473              
474             =item L<Teng>
475              
476             =item L<SQL::Abstract::More>
477              
478             =back
479              
480             =head1 ACKNOWLEDGEMENTS
481              
482              
483             =head1 LICENSE AND COPYRIGHT
484              
485             Copyright 2012 Ktat.
486              
487             This program is free software; you can redistribute it and/or modify it
488             under the terms of either: the GNU General Public License as published
489             by the Free Software Foundation; or the Artistic License.
490              
491             See http://dev.perl.org/licenses/ for more information.
492              
493              
494             =cut
495              
496             1; # End of Teng::Plugin::SearchBySQLAbstractMore