Newer Older
390 lines | 9.818kb
added experimental DBIx::Cus...
Yuki Kimoto authored on 2011-01-18
1
package DBIx::Custom::Where;
updatedd pod
Yuki Kimoto authored on 2011-06-12
2
use Object::Simple -base;
added experimental DBIx::Cus...
Yuki Kimoto authored on 2011-01-18
3

            
DBIx::Custom::Model type att...
Yuki Kimoto authored on 2011-06-17
4
use Carp 'croak';
5
use DBIx::Custom::Util '_subname';
added experimental DBIx::Cus...
Yuki Kimoto authored on 2011-01-18
6
use overload 'bool' => sub {1}, fallback => 1;
7
use overload '""' => sub { shift->to_string }, fallback => 1;
8

            
updated document
Yuki Kimoto authored on 2011-01-20
9
# Carp trust relationship
10
push @DBIx::Custom::CARP_NOT, __PACKAGE__;
11

            
sub module use DBIx::Custom ...
Yuki Kimoto authored on 2011-08-02
12
has [qw/dbi param/],
added map method(not complet...
Yuki Kimoto authored on 2011-08-09
13
    clause => sub { [] };
14

            
map cleanup
Yuki Kimoto authored on 2011-08-09
15
sub map {
16
    my ($self, %map) = @_;
17
    
added tests
Yuki Kimoto authored on 2011-08-09
18
    if ($self->if ne 'exists' || keys %map) {
19
        my $param = $self->_map_param($self->param, %map);
20
        $self->param($param);
21
    }
map cleanup
Yuki Kimoto authored on 2011-08-09
22
    return $self;
23
}
24

            
added map method(not complet...
Yuki Kimoto authored on 2011-08-09
25
sub _map_param {
26
    my $self = shift;
27
    my $param = shift;
28
    
29
    return $param if !defined $param;
30
    
31
    my %map = @_;
32
    
33
    # Mapping
34
    my $map_param = {};
35
    foreach my $key (keys %$param) {
36
    
37
        my $value_cb;
38
        my $condition;
39
        my $map_key;
40
        
41
        # Get mapping information
42
        if (ref $map{$key} eq 'ARRAY') {
43
            foreach my $some (@{$map{$key}}) {
44
                $map_key = $some unless ref $some;
45
                $condition = $some->{if} if ref $some eq 'HASH';
46
                $value_cb = $some if ref $some eq 'CODE';
47
            }
48
        }
cleanup
Yuki Kimoto authored on 2011-08-09
49
        elsif (defined $map{$key}) {
added map method(not complet...
Yuki Kimoto authored on 2011-08-09
50
            $map_key = $map{$key};
51
        }
cleanup
Yuki Kimoto authored on 2011-08-09
52
        else {
53
            $map_key = $key;
54
        }
55
        
added map method(not complet...
Yuki Kimoto authored on 2011-08-09
56
        $value_cb ||= sub { $_[0] };
57
        $condition ||= $self->if || 'exists';
fixed if is not converted to...
Yuki Kimoto authored on 2011-08-09
58
        $condition = $self->_if_to_sub($condition);
added map method(not complet...
Yuki Kimoto authored on 2011-08-09
59

            
60
        # Map parameter
61
        my $value;
62
        if (ref $condition eq 'CODE') {
63
            if (ref $param->{$key} eq 'ARRAY') {
64
                $map_param->{$map_key} = [];
65
                for (my $i = 0; $i < @{$param->{$key}}; $i++) {
66
                    $map_param->{$map_key}->[$i]
67
                      = $condition->($param->{$key}->[$i]) ? $param->{$key}->[$i]
68
                      : $self->dbi->not_exists;
69
                }
70
            }
71
            else {
72
                $map_param->{$map_key} = $value_cb->($param->{$key})
73
                  if $condition->($param->{$key});
74
            }
75
        }
76
        elsif ($condition eq 'exists') {
77
            if (ref $param->{$key} eq 'ARRAY') {
78
                $map_param->{$map_key} = [];
79
                for (my $i = 0; $i < @{$param->{$key}}; $i++) {
80
                    $map_param->{$map_key}->[$i]
81
                      = exists $param->{$key}->[$i] ? $param->{$key}->[$i]
82
                      : $self->dbi->not_exists;
83
                }
84
            }
85
            else {
86
                $map_param->{$map_key} = $value_cb->($param->{$key})
87
                  if exists $param->{$key};
88
            }
89
        }
90
        else { croak qq/Condition must be code reference or "exists" / . _subname }
91
    }
92
    
93
    return $map_param;
94
}
95

            
96
sub if {
97
    my $self = shift;
98
    if (@_) {
99
        my $if = $_[0];
100
        
fixed if is not converted to...
Yuki Kimoto authored on 2011-08-09
101
        $if = $self->_if_to_sub($if);
added map method(not complet...
Yuki Kimoto authored on 2011-08-09
102
        $self->{if} = $if;
103
        return $self;
104
    }
105
    $self->{if} = 'exists' unless exists $self->{if};
106
    return $self->{if};
107
}
added experimental DBIx::Cus...
Yuki Kimoto authored on 2011-01-18
108

            
fixed if is not converted to...
Yuki Kimoto authored on 2011-08-09
109
sub _if_to_sub {
110
    my ($self, $if) = @_;
111
    $if = $if eq 'exists' ? $if
112
            : $if eq 'defined' ? sub { defined $_[0] }
113
            : $if eq 'length'  ? sub { defined $_[0] && length $_[0] }
114
            : ref $if eq 'CODE' ? $if
115
            : undef;
116

            
117
    croak "You can must specify right value to C<if> " . _subname
118
      unless $if;
119
    
120
    return $if;
121
}
122

            
improved error messages
Yuki Kimoto authored on 2011-04-18
123
sub new {
124
    my $self = shift->SUPER::new(@_);
125
    
126
    # Check attribute names
127
    my @attrs = keys %$self;
128
    foreach my $attr (@attrs) {
cleanup
Yuki Kimoto authored on 2011-04-25
129
        croak qq{"$attr" is invalid attribute name (} . _subname . ")"
improved error messages
Yuki Kimoto authored on 2011-04-18
130
          unless $self->can($attr);
131
    }
132
    
133
    return $self;
134
}
135

            
changed DBIx::Custom::Where ...
Yuki Kimoto authored on 2011-01-19
136
sub to_string {
added test
Yuki Kimoto authored on 2011-01-19
137
    my $self = shift;
added experimental DBIx::Cus...
Yuki Kimoto authored on 2011-01-18
138
    
- update_param_tag is DEPREC...
Yuki Kimoto authored on 2011-06-07
139
    # Check if column name is safety character;
cleanup
Yuki Kimoto authored on 2011-08-02
140
    my $safety = $self->dbi->safety_character;
- update_param_tag is DEPREC...
Yuki Kimoto authored on 2011-06-07
141
    if (ref $self->param eq 'HASH') {
142
        foreach my $column (keys %{$self->param}) {
143
            croak qq{"$column" is not safety column name (} . _subname . ")"
144
              unless $column =~ /^[$safety\.]+$/;
145
        }
146
    }
many changed
Yuki Kimoto authored on 2011-01-23
147
    # Clause
added test
Yuki Kimoto authored on 2011-01-19
148
    my $clause = $self->clause;
changed DBIx::Custom::Where ...
Yuki Kimoto authored on 2011-01-19
149
    $clause = ['and', $clause] unless ref $clause eq 'ARRAY';
added test
Yuki Kimoto authored on 2011-01-19
150
    $clause->[0] = 'and' unless @$clause;
added EXPERIMENTAL DBIx::Cus...
Yuki Kimoto authored on 2011-08-09
151
    
152
    # Map condition
added map method(not complet...
Yuki Kimoto authored on 2011-08-09
153
    my $if = $self->if || '';
154
    $if = $if eq 'exists' ? $if
155
            : $if eq 'defined' ? sub { defined $_[0] }
156
            : $if eq 'length'  ? sub { length $_[0] }
157
            : ref $if eq 'CODE' ? $if
added EXPERIMENTAL DBIx::Cus...
Yuki Kimoto authored on 2011-08-09
158
            : undef;
159
    
added map method(not complet...
Yuki Kimoto authored on 2011-08-09
160
    croak "You can must specify right value to C<if> " . _subname
161
      unless $if;
162
    $self->{_if} = $if;
163
    
many changed
Yuki Kimoto authored on 2011-01-23
164
    # Parse
added test
Yuki Kimoto authored on 2011-01-19
165
    my $where = [];
166
    my $count = {};
many changed
Yuki Kimoto authored on 2011-01-23
167
    $self->_parse($clause, $where, $count, 'and');
changed DBIx::Custom::Where ...
Yuki Kimoto authored on 2011-01-19
168
    
many changed
Yuki Kimoto authored on 2011-01-23
169
    # Stringify
170
    unshift @$where, 'where' if @$where;
added test
Yuki Kimoto authored on 2011-01-19
171
    return join(' ', @$where);
added experimental DBIx::Cus...
Yuki Kimoto authored on 2011-01-18
172
}
173

            
added test
Yuki Kimoto authored on 2011-01-19
174
our %VALID_OPERATIONS = map { $_ => 1 } qw/and or/;
many changed
Yuki Kimoto authored on 2011-01-23
175
sub _parse {
added test
Yuki Kimoto authored on 2011-01-19
176
    my ($self, $clause, $where, $count, $op) = @_;
added experimental DBIx::Cus...
Yuki Kimoto authored on 2011-01-18
177
    
many changed
Yuki Kimoto authored on 2011-01-23
178
    # Array
changed DBIx::Custom::Where ...
Yuki Kimoto authored on 2011-01-19
179
    if (ref $clause eq 'ARRAY') {
many changed
Yuki Kimoto authored on 2011-01-23
180
        
181
        # Start
added test
Yuki Kimoto authored on 2011-01-19
182
        push @$where, '(';
changed DBIx::Custom::Where ...
Yuki Kimoto authored on 2011-01-19
183
        
many changed
Yuki Kimoto authored on 2011-01-23
184
        # Operation
changed DBIx::Custom::Where ...
Yuki Kimoto authored on 2011-01-19
185
        my $op = $clause->[0] || '';
improved error message
Yuki Kimoto authored on 2011-06-13
186
        croak qq{First argument must be "and" or "or" in where clause } .
187
              qq{"$op" is passed} . _subname . ")"
changed DBIx::Custom::Where ...
Yuki Kimoto authored on 2011-01-19
188
          unless $VALID_OPERATIONS{$op};
many changed
Yuki Kimoto authored on 2011-01-23
189
        
fixed DBIx::Custom::Where to...
Yuki Kimoto authored on 2011-06-27
190
        my $pushed_array;
many changed
Yuki Kimoto authored on 2011-01-23
191
        # Parse internal clause
changed DBIx::Custom::Where ...
Yuki Kimoto authored on 2011-01-19
192
        for (my $i = 1; $i < @$clause; $i++) {
many changed
Yuki Kimoto authored on 2011-01-23
193
            my $pushed = $self->_parse($clause->[$i], $where, $count, $op);
added test
Yuki Kimoto authored on 2011-01-19
194
            push @$where, $op if $pushed;
fixed DBIx::Custom::Where to...
Yuki Kimoto authored on 2011-06-27
195
            $pushed_array = 1 if $pushed;
changed DBIx::Custom::Where ...
Yuki Kimoto authored on 2011-01-19
196
        }
added test
Yuki Kimoto authored on 2011-01-19
197
        pop @$where if $where->[-1] eq $op;
198
        
many changed
Yuki Kimoto authored on 2011-01-23
199
        # Undo
added test
Yuki Kimoto authored on 2011-01-19
200
        if ($where->[-1] eq '(') {
201
            pop @$where;
fixed DBIx::Custom::Where to...
Yuki Kimoto authored on 2011-06-27
202
            pop @$where if ($where->[-1] || '') eq $op;
added test
Yuki Kimoto authored on 2011-01-19
203
        }
many changed
Yuki Kimoto authored on 2011-01-23
204
        # End
renamed DBIx::Custom::TagPro...
Yuki Kimoto authored on 2011-01-24
205
        else { push @$where, ')' }
fixed DBIx::Custom::Where to...
Yuki Kimoto authored on 2011-06-27
206
        
207
        return $pushed_array;
changed DBIx::Custom::Where ...
Yuki Kimoto authored on 2011-01-19
208
    }
many changed
Yuki Kimoto authored on 2011-01-23
209
    
210
    # String
changed DBIx::Custom::Where ...
Yuki Kimoto authored on 2011-01-19
211
    else {
DBIx::Custom::Where clause a...
Yuki Kimoto authored on 2011-04-18
212
        # Pushed
213
        my $pushed;
changed DBIx::Custom::Where ...
Yuki Kimoto authored on 2011-01-19
214
        
added test
Yuki Kimoto authored on 2011-01-19
215
        # Column
cleanup
Yuki Kimoto authored on 2011-08-02
216
        my $columns = $self->dbi->query_builder->build_query($clause)->columns;
DBIx::Custom::Where clause a...
Yuki Kimoto authored on 2011-04-18
217
        if (@$columns == 0) {
218
            push @$where, $clause;
219
            $pushed = 1;
220
            return $pushed;
221
        }
222
        elsif (@$columns != 1) {
- update_param_tag is DEPREC...
Yuki Kimoto authored on 2011-06-07
223
            croak qq{Each part contains one column name: "$clause" (}
cleanup
Yuki Kimoto authored on 2011-04-25
224
                  . _subname . ")";
DBIx::Custom::Where clause a...
Yuki Kimoto authored on 2011-04-18
225
        }
DBIx::Custom::Model type att...
Yuki Kimoto authored on 2011-06-17
226
        
227
        # Remove quote
changed DBIx::Custom::Where ...
Yuki Kimoto authored on 2011-01-19
228
        my $column = $columns->[0];
sub module use DBIx::Custom ...
Yuki Kimoto authored on 2011-08-02
229
        if (my $q = $self->dbi->_quote) {
added quote method's two cha...
Yuki Kimoto authored on 2011-07-29
230
            $q = quotemeta($q);
231
            $column =~ s/[$q]//g;
added EXPERIMENTAL reserved_...
Yuki Kimoto authored on 2011-03-30
232
        }
233
        
DBIx::Custom::Model type att...
Yuki Kimoto authored on 2011-06-17
234
        # Check safety
cleanup
Yuki Kimoto authored on 2011-08-02
235
        my $safety = $self->dbi->safety_character;
cleanup
Yuki Kimoto authored on 2011-04-25
236
        croak qq{"$column" is not safety column name (} . _subname . ")"
- remaned experimental safty...
Yuki Kimoto authored on 2011-03-10
237
          unless $column =~ /^[$safety\.]+$/;
changed DBIx::Custom::Where ...
Yuki Kimoto authored on 2011-01-19
238
        
many changed
Yuki Kimoto authored on 2011-01-23
239
        # Column count up
added test
Yuki Kimoto authored on 2011-01-19
240
        my $count = ++$count->{$column};
changed DBIx::Custom::Where ...
Yuki Kimoto authored on 2011-01-19
241
        
many changed
Yuki Kimoto authored on 2011-01-23
242
        # Push
map cleanup
Yuki Kimoto authored on 2011-08-09
243
        my $param = $self->param;
added experimental not_exist...
Yuki Kimoto authored on 2011-01-26
244
        if (ref $param eq 'HASH') {
renamed DBIx::Custom::TagPro...
Yuki Kimoto authored on 2011-01-24
245
            if (exists $param->{$column}) {
added map method(not complet...
Yuki Kimoto authored on 2011-08-09
246
                my $if = $self->{_if};
added EXPERIMENTAL DBIx::Cus...
Yuki Kimoto authored on 2011-08-09
247
                
renamed DBIx::Custom::TagPro...
Yuki Kimoto authored on 2011-01-24
248
                if (ref $param->{$column} eq 'ARRAY') {
cleanup
Yuki Kimoto authored on 2011-08-09
249
                    $pushed = 1 if exists $param->{$column}->[$count - 1]
250
                      && ref $param->{$column}->[$count - 1] ne 'DBIx::Custom::NotExists'
renamed DBIx::Custom::TagPro...
Yuki Kimoto authored on 2011-01-24
251
                }
cleanup
Yuki Kimoto authored on 2011-08-09
252
                elsif ($count == 1) { $pushed = 1 }
added experimental DBIx::Cus...
Yuki Kimoto authored on 2011-01-18
253
            }
renamed DBIx::Custom::TagPro...
Yuki Kimoto authored on 2011-01-24
254
            push @$where, $clause if $pushed;
255
        }
added experimental not_exist...
Yuki Kimoto authored on 2011-01-26
256
        elsif (!defined $param) {
renamed DBIx::Custom::TagPro...
Yuki Kimoto authored on 2011-01-24
257
            push @$where, $clause;
258
            $pushed = 1;
added experimental DBIx::Cus...
Yuki Kimoto authored on 2011-01-18
259
        }
improved error messages
Yuki Kimoto authored on 2011-04-18
260
        else {
cleanup
Yuki Kimoto authored on 2011-04-25
261
            croak "Parameter must be hash reference or undfined value ("
262
                . _subname . ")"
improved error messages
Yuki Kimoto authored on 2011-04-18
263
        }
added test
Yuki Kimoto authored on 2011-01-19
264
        return $pushed;
added experimental DBIx::Cus...
Yuki Kimoto authored on 2011-01-18
265
    }
fixed DBIx::Custom::Where to...
Yuki Kimoto authored on 2011-06-27
266
    return;
added experimental DBIx::Cus...
Yuki Kimoto authored on 2011-01-18
267
}
268

            
269
1;
270

            
271
=head1 NAME
272

            
removed EXPERIMETNAL flag fr...
Yuki Kimoto authored on 2011-03-25
273
DBIx::Custom::Where - Where clause
added experimental DBIx::Cus...
Yuki Kimoto authored on 2011-01-18
274

            
275
=head1 SYNOPSYS
276

            
many changed
Yuki Kimoto authored on 2011-01-23
277
    my $where = DBIx::Custom::Where->new;
DBIx::Custom::Model type att...
Yuki Kimoto authored on 2011-06-17
278
    my $string_where = "$where";
added experimental DBIx::Cus...
Yuki Kimoto authored on 2011-01-18
279

            
280
=head1 ATTRIBUTES
281

            
282
=head2 C<clause>
283

            
DBIx::Custom::Model type att...
Yuki Kimoto authored on 2011-06-17
284
    my $clause = $where->clause;
285
    $where = $where->clause(
286
        ['and',
287
            'title = :title', 
288
            ['or', 'date < :date', 'date > :date']
289
        ]
added test
Yuki Kimoto authored on 2011-01-19
290
    );
added experimental DBIx::Cus...
Yuki Kimoto authored on 2011-01-18
291

            
many changed
Yuki Kimoto authored on 2011-01-23
292
Where clause. Above one is expanded to the following SQL by to_string
293
If all parameter names is exists.
294

            
DBIx::Custom::Model type att...
Yuki Kimoto authored on 2011-06-17
295
    "where ( title = :title and ( date < :date or date > :date ) )"
added experimental DBIx::Cus...
Yuki Kimoto authored on 2011-01-18
296

            
map cleanup
Yuki Kimoto authored on 2011-08-09
297
=head2 C<param>
298

            
299
    my $param = $where->param;
300
    $where = $where->param({
301
        title => 'Perl',
302
        date => ['2010-11-11', '2011-03-05'],
303
    });
304

            
305
=head2 C<dbi>
306

            
307
    my $dbi = $where->dbi;
308
    $where = $where->dbi($dbi);
309

            
310
L<DBIx::Custom> object.
311

            
312
=head1 METHODS
313

            
314
L<DBIx::Custom::Where> inherits all methods from L<Object::Simple>
315
and implements the following new ones.
316

            
317
=head2 C<if EXPERIMENTAL>
318
    
319
    my $if = $where->if($condition);
320
    $where->if($condition);
321

            
322
C<if> is default of C<map> method C<if> option.
323

            
added map method(not complet...
Yuki Kimoto authored on 2011-08-09
324
=head2 C<map EXPERIMENTAL>
325

            
updated pod
Yuki Kimoto authored on 2011-08-09
326
Mapping parameter key and value. C<param> is converted based on,
map cleanup
Yuki Kimoto authored on 2011-08-09
327
so this method must be called after C<param> is set.
updated pod
Yuki Kimoto authored on 2011-08-09
328
Set C<if> if you need before C<map> method call.
added map method(not complet...
Yuki Kimoto authored on 2011-08-09
329

            
map cleanup
Yuki Kimoto authored on 2011-08-09
330
    $where->map(
added map method(not complet...
Yuki Kimoto authored on 2011-08-09
331
        'id' => 'book.id',
332
        'author' => ['book.author' => sub { '%' . $_[0] . '%' }],
333
        'price' => [
334
            'book.price', {if => sub { length $_[0] }
335
        ]
map cleanup
Yuki Kimoto authored on 2011-08-09
336
    );
added map method(not complet...
Yuki Kimoto authored on 2011-08-09
337

            
updated pod
Yuki Kimoto authored on 2011-08-09
338
The followin parameter
339

            
340
    {
341
        id => 1,
342
        auhtor => 'Ken',
343
        price => 1000
344
    }
345

            
346
is converted to
347

            
348
    {
349
        'book.id' => 1,
350
        'book.author' => '%Ken%',
351
        'book.price' => 1000
352
    }
353

            
added map method(not complet...
Yuki Kimoto authored on 2011-08-09
354
The following option is available.
added EXPERIMENTAL DBIx::Cus...
Yuki Kimoto authored on 2011-08-09
355

            
added map method(not complet...
Yuki Kimoto authored on 2011-08-09
356
=over 4
added EXPERIMENTAL DBIx::Cus...
Yuki Kimoto authored on 2011-08-09
357

            
added map method(not complet...
Yuki Kimoto authored on 2011-08-09
358
=item * C<if>
359

            
360
By default, if parameter key is exists, mapping is done.
added EXPERIMENTAL DBIx::Cus...
Yuki Kimoto authored on 2011-08-09
361
    
added map method(not complet...
Yuki Kimoto authored on 2011-08-09
362
    if => 'exists';
added EXPERIMENTAL DBIx::Cus...
Yuki Kimoto authored on 2011-08-09
363

            
364
In case C<defined> is specified, if the value is defined,
365
mapping is done.
366

            
added map method(not complet...
Yuki Kimoto authored on 2011-08-09
367
    if => 'defined';
added EXPERIMENTAL DBIx::Cus...
Yuki Kimoto authored on 2011-08-09
368

            
369
In case C<length> is specified, the value is defined
370
and the length is bigger than 0, mappting is done.
371

            
added map method(not complet...
Yuki Kimoto authored on 2011-08-09
372
    if => 'length';
added EXPERIMENTAL DBIx::Cus...
Yuki Kimoto authored on 2011-08-09
373

            
374
You can also subroutine like C<sub { defined $_[0] }> for mappging.
375

            
added map method(not complet...
Yuki Kimoto authored on 2011-08-09
376
    if => sub { defined $_[0] }
377

            
378
=back
added EXPERIMENTAL DBIx::Cus...
Yuki Kimoto authored on 2011-08-09
379

            
added experimental DBIx::Cus...
Yuki Kimoto authored on 2011-01-18
380
=head2 C<to_string>
381

            
382
    $where->to_string;
added test
Yuki Kimoto authored on 2011-01-19
383

            
DBIx::Custom::Model type att...
Yuki Kimoto authored on 2011-06-17
384
Convert where clause to string.
385

            
386
double quote is override to execute C<to_string> method.
387

            
388
    my $string_where = "$where";
added test
Yuki Kimoto authored on 2011-01-19
389

            
- update_param_tag is DEPREC...
Yuki Kimoto authored on 2011-06-07
390
=cut