File Coverage

blib/lib/Class/DBI/Search/Basic.pm
Criterion Covered Total %
statement 9 55 16.3
branch 0 8 0.0
condition 0 15 0.0
subroutine 3 14 21.4
pod 6 6 100.0
total 18 98 18.3


line stmt bran cond sub pod time code
1             package Class::DBI::Search::Basic;
2              
3             =head1 NAME
4              
5             Class::DBI::Search::Basic - Simple Class::DBI search
6              
7             =head1 SYNOPSIS
8              
9             my $searcher = Class::DBI::Search::Basic->new(
10             $cdbi_class, @search_args
11             );
12              
13             my @results = $searcher->run_search;
14              
15             # Over in your Class::DBI subclass:
16            
17             __PACKAGE__->add_searcher(
18             search => "Class::DBI::Search::Basic",
19             isearch => "Class::DBI::Search::Plugin::CaseInsensitive",
20             );
21              
22             =head1 DESCRIPTION
23              
24             This is the start of a pluggable Search infrastructure for Class::DBI.
25              
26             At the minute Class::DBI::Search::Basic doubles up as both the default
27             search within Class::DBI as well as the search base class. We will
28             probably need to tease this apart more later and create an abstract base
29             class for search plugins.
30              
31             =head1 METHODS
32              
33             =head2 new
34              
35             my $searcher = Class::DBI::Search::Basic->new(
36             $cdbi_class, @search_args
37             );
38              
39             A Searcher is created with the class to which the results will belong,
40             and the arguments passed to the search call by the user.
41              
42             =head2 opt
43              
44             if (my $order = $self->opt('order_by')) { ... }
45              
46             The arguments passed to search may contain an options hash. This will
47             return the value of a given option.
48              
49             =head2 run_search
50              
51             my @results = $searcher->run_search;
52             my $iterator = $searcher->run_search;
53              
54             Actually run the search.
55              
56             =head1 SUBCLASSING
57              
58             =head2 sql / bind / fragment
59              
60             The actual mechanics of generating the SQL and executing it split up
61             into a variety of methods for you to override.
62              
63             run_search() is implemented as:
64              
65             return $cdbi->sth_to_objects($self->sql, $self->bind);
66              
67             Where sql() is
68              
69             $cdbi->sql_Retrieve($self->fragment);
70              
71              
72             There are also a variety of private methods underneath this that could
73             be overriden in a pinch, but if you need to do this I'd rather you let
74             me know so that I can make them public, or at least so that I don't
75             remove them from under your feet.
76              
77             =cut
78              
79 5     5   28457 use strict;
  5         10  
  5         177  
80 5     5   29 use warnings;
  5         10  
  5         188  
81              
82 5     5   27 use base 'Class::Accessor::Fast';
  5         8  
  5         4164  
83             __PACKAGE__->mk_accessors(qw/class args opts type/);
84              
85             sub new {
86 0     0 1   my ($me, $proto, @args) = @_;
87 0           my ($args, $opts) = $me->_unpack_args(@args);
88 0   0       bless {
89             class => ref $proto || $proto,
90             args => $args,
91             opts => $opts,
92             type => "=",
93             } => $me;
94             }
95              
96             sub opt {
97 0     0 1   my ($self, $option) = @_;
98 0           $self->{opts}->{$option};
99             }
100              
101             sub _unpack_args {
102 0     0     my ($self, @args) = @_;
103 0 0         @args = %{ $args[0] } if ref $args[0] eq "HASH";
  0            
104 0 0         my $opts = @args % 2 ? pop @args : {};
105 0           return (\@args, $opts);
106             }
107              
108             sub _search_for {
109 0     0     my $self = shift;
110 0           my @args = @{ $self->{args} };
  0            
111 0           my $class = $self->{class};
112 0           my %search_for;
113 0           while (my ($col, $val) = splice @args, 0, 2) {
114             my $column = $class->find_column($col)
115 0   0 0     || (List::Util::first { $_->accessor eq $col } $class->columns)
  0            
116             || $class->_croak("$col is not a column of $class");
117 0           $search_for{$column} = $class->_deflated_column($column, $val);
118             }
119 0           return \%search_for;
120             }
121              
122             sub _qual_bind {
123 0     0     my $self = shift;
124 0   0       $self->{_qual_bind} ||= do {
125 0           my $search_for = $self->_search_for;
126 0           my $type = $self->type;
127 0           my (@qual, @bind);
128 0           for my $column (sort keys %$search_for) { # sort for prepare_cached
129 0 0         if (defined(my $value = $search_for->{$column})) {
130 0           push @qual, "$column $type ?";
131 0           push @bind, $value;
132             } else {
133              
134             # perhaps _carp if $type ne "="
135 0           push @qual, "$column IS NULL";
136             }
137             }
138 0           [ \@qual, \@bind ];
139             };
140             }
141              
142             sub _qual {
143 0     0     my $self = shift;
144 0   0       $self->{_qual} ||= $self->_qual_bind->[0];
145             }
146              
147             sub bind {
148 0     0 1   my $self = shift;
149 0   0       $self->{_bind} ||= $self->_qual_bind->[1];
150             }
151              
152             sub fragment {
153 0     0 1   my $self = shift;
154 0           my $frag = join " AND ", @{ $self->_qual };
  0            
155 0 0         if (my $order = $self->opt('order_by')) {
156 0           $frag .= " ORDER BY $order";
157             }
158 0           return $frag;
159             }
160              
161             sub sql {
162 0     0 1   my $self = shift;
163 0           return $self->class->sql_Retrieve($self->fragment);
164             }
165              
166             sub run_search {
167 0     0 1   my $self = shift;
168 0           my $cdbi = $self->class;
169 0           return $cdbi->sth_to_objects($self->sql, $self->bind);
170             }
171              
172             1;