| line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
|
1
|
|
|
|
|
|
|
package DBIx::NinjaORM; |
|
2
|
|
|
|
|
|
|
|
|
3
|
69
|
|
|
69
|
|
2445984
|
use 5.010; |
|
|
69
|
|
|
|
|
261
|
|
|
|
69
|
|
|
|
|
3726
|
|
|
4
|
|
|
|
|
|
|
|
|
5
|
69
|
|
|
69
|
|
394
|
use warnings; |
|
|
69
|
|
|
|
|
147
|
|
|
|
69
|
|
|
|
|
1819
|
|
|
6
|
69
|
|
|
69
|
|
346
|
use strict; |
|
|
69
|
|
|
|
|
238
|
|
|
|
69
|
|
|
|
|
2029
|
|
|
7
|
|
|
|
|
|
|
|
|
8
|
69
|
|
|
69
|
|
399
|
use Carp; |
|
|
69
|
|
|
|
|
137
|
|
|
|
69
|
|
|
|
|
5007
|
|
|
9
|
69
|
|
|
69
|
|
67776
|
use Class::Load qw(); |
|
|
69
|
|
|
|
|
5613154
|
|
|
|
69
|
|
|
|
|
4901
|
|
|
10
|
69
|
|
|
69
|
|
52403
|
use DBIx::NinjaORM::StaticClassInfo; |
|
|
69
|
|
|
|
|
251
|
|
|
|
69
|
|
|
|
|
2638
|
|
|
11
|
69
|
|
|
69
|
|
53530
|
use DBIx::NinjaORM::Utils qw( dumper ); |
|
|
69
|
|
|
|
|
269
|
|
|
|
69
|
|
|
|
|
4947
|
|
|
12
|
69
|
|
|
69
|
|
438
|
use Data::Validate::Type; |
|
|
69
|
|
|
|
|
148
|
|
|
|
69
|
|
|
|
|
2175
|
|
|
13
|
69
|
|
|
69
|
|
62261
|
use Digest::SHA1 qw(); |
|
|
69
|
|
|
|
|
81878
|
|
|
|
69
|
|
|
|
|
2312
|
|
|
14
|
69
|
|
|
69
|
|
63002
|
use Log::Any qw( $log ); |
|
|
69
|
|
|
|
|
170284
|
|
|
|
69
|
|
|
|
|
399
|
|
|
15
|
69
|
|
|
69
|
|
72628
|
use MIME::Base64 qw(); |
|
|
69
|
|
|
|
|
56603
|
|
|
|
69
|
|
|
|
|
1859
|
|
|
16
|
69
|
|
|
69
|
|
82729
|
use Storable; |
|
|
69
|
|
|
|
|
282514
|
|
|
|
69
|
|
|
|
|
5118
|
|
|
17
|
69
|
|
|
69
|
|
778
|
use Try::Tiny; |
|
|
69
|
|
|
|
|
187
|
|
|
|
69
|
|
|
|
|
3161252
|
|
|
18
|
|
|
|
|
|
|
|
|
19
|
|
|
|
|
|
|
|
|
20
|
|
|
|
|
|
|
=head1 NAME |
|
21
|
|
|
|
|
|
|
|
|
22
|
|
|
|
|
|
|
DBIx::NinjaORM - Flexible Perl ORM for easy transitions from inline SQL to objects. |
|
23
|
|
|
|
|
|
|
|
|
24
|
|
|
|
|
|
|
|
|
25
|
|
|
|
|
|
|
=head1 VERSION |
|
26
|
|
|
|
|
|
|
|
|
27
|
|
|
|
|
|
|
Version 3.0.2 |
|
28
|
|
|
|
|
|
|
|
|
29
|
|
|
|
|
|
|
=cut |
|
30
|
|
|
|
|
|
|
|
|
31
|
|
|
|
|
|
|
our $VERSION = '3.0.2'; |
|
32
|
|
|
|
|
|
|
|
|
33
|
|
|
|
|
|
|
|
|
34
|
|
|
|
|
|
|
=head1 DESCRIPTION |
|
35
|
|
|
|
|
|
|
|
|
36
|
|
|
|
|
|
|
L was designed with a few goals in mind: |
|
37
|
|
|
|
|
|
|
|
|
38
|
|
|
|
|
|
|
=over 4 |
|
39
|
|
|
|
|
|
|
|
|
40
|
|
|
|
|
|
|
=item * |
|
41
|
|
|
|
|
|
|
|
|
42
|
|
|
|
|
|
|
Expand objects with data joined from other tables, to do less queries and |
|
43
|
|
|
|
|
|
|
prevent lazy-loading of ancillary information. |
|
44
|
|
|
|
|
|
|
|
|
45
|
|
|
|
|
|
|
=item * |
|
46
|
|
|
|
|
|
|
|
|
47
|
|
|
|
|
|
|
Have a short learning curve. |
|
48
|
|
|
|
|
|
|
|
|
49
|
|
|
|
|
|
|
=item * |
|
50
|
|
|
|
|
|
|
|
|
51
|
|
|
|
|
|
|
Provide advanced caching features and manage cache expiration upon database changes. |
|
52
|
|
|
|
|
|
|
|
|
53
|
|
|
|
|
|
|
=item * |
|
54
|
|
|
|
|
|
|
|
|
55
|
|
|
|
|
|
|
Allow a progressive introduction of a separate Model layer in a legacy codebase. |
|
56
|
|
|
|
|
|
|
|
|
57
|
|
|
|
|
|
|
=back |
|
58
|
|
|
|
|
|
|
|
|
59
|
|
|
|
|
|
|
|
|
60
|
|
|
|
|
|
|
=head1 SYNOPSIS |
|
61
|
|
|
|
|
|
|
|
|
62
|
|
|
|
|
|
|
=head2 Simple example |
|
63
|
|
|
|
|
|
|
|
|
64
|
|
|
|
|
|
|
Let's take the example of a C class that represents a book. You |
|
65
|
|
|
|
|
|
|
would start C with the following code: |
|
66
|
|
|
|
|
|
|
|
|
67
|
|
|
|
|
|
|
package My::Model::Book; |
|
68
|
|
|
|
|
|
|
|
|
69
|
|
|
|
|
|
|
use strict; |
|
70
|
|
|
|
|
|
|
use warnings; |
|
71
|
|
|
|
|
|
|
|
|
72
|
|
|
|
|
|
|
use base 'DBIx::NinjaORM'; |
|
73
|
|
|
|
|
|
|
|
|
74
|
|
|
|
|
|
|
use DBI; |
|
75
|
|
|
|
|
|
|
|
|
76
|
|
|
|
|
|
|
|
|
77
|
|
|
|
|
|
|
sub static_class_info |
|
78
|
|
|
|
|
|
|
{ |
|
79
|
|
|
|
|
|
|
my ( $class ) = @_; |
|
80
|
|
|
|
|
|
|
|
|
81
|
|
|
|
|
|
|
# Retrieve defaults from DBIx::Ninja->static_class_info(). |
|
82
|
|
|
|
|
|
|
my $info = $class->SUPER::static_class_info(); |
|
83
|
|
|
|
|
|
|
|
|
84
|
|
|
|
|
|
|
$info->set( |
|
85
|
|
|
|
|
|
|
{ |
|
86
|
|
|
|
|
|
|
# Set mandatory defaults. |
|
87
|
|
|
|
|
|
|
table_name => 'books', |
|
88
|
|
|
|
|
|
|
primary_key_name => 'book_id', |
|
89
|
|
|
|
|
|
|
default_dbh => DBI->connect( |
|
90
|
|
|
|
|
|
|
"dbi:mysql:[database_name]:localhost:3306", |
|
91
|
|
|
|
|
|
|
"[user]", |
|
92
|
|
|
|
|
|
|
"[password]", |
|
93
|
|
|
|
|
|
|
), |
|
94
|
|
|
|
|
|
|
|
|
95
|
|
|
|
|
|
|
# Add optional information. |
|
96
|
|
|
|
|
|
|
# Allow filtering SELECTs on books.name. |
|
97
|
|
|
|
|
|
|
filtering_fields => [ 'name' ], |
|
98
|
|
|
|
|
|
|
} |
|
99
|
|
|
|
|
|
|
); |
|
100
|
|
|
|
|
|
|
|
|
101
|
|
|
|
|
|
|
return $info; |
|
102
|
|
|
|
|
|
|
} |
|
103
|
|
|
|
|
|
|
|
|
104
|
|
|
|
|
|
|
1; |
|
105
|
|
|
|
|
|
|
|
|
106
|
|
|
|
|
|
|
Inheriting with C |
|
107
|
|
|
|
|
|
|
C (with a default database handle and a table name) |
|
108
|
|
|
|
|
|
|
are the only two requirements to have a working model. |
|
109
|
|
|
|
|
|
|
|
|
110
|
|
|
|
|
|
|
|
|
111
|
|
|
|
|
|
|
=head2 A more complex model |
|
112
|
|
|
|
|
|
|
|
|
113
|
|
|
|
|
|
|
If you have more than one Model class to create, for example C |
|
114
|
|
|
|
|
|
|
and C, you probably want to create a single class |
|
115
|
|
|
|
|
|
|
C to hold the defaults and then inherits from that main class. |
|
116
|
|
|
|
|
|
|
|
|
117
|
|
|
|
|
|
|
package My::Model; |
|
118
|
|
|
|
|
|
|
|
|
119
|
|
|
|
|
|
|
use strict; |
|
120
|
|
|
|
|
|
|
use warnings; |
|
121
|
|
|
|
|
|
|
|
|
122
|
|
|
|
|
|
|
use base 'DBIx::NinjaORM'; |
|
123
|
|
|
|
|
|
|
|
|
124
|
|
|
|
|
|
|
use DBI; |
|
125
|
|
|
|
|
|
|
use Cache::Memcached::Fast; |
|
126
|
|
|
|
|
|
|
|
|
127
|
|
|
|
|
|
|
|
|
128
|
|
|
|
|
|
|
sub static_class_info |
|
129
|
|
|
|
|
|
|
{ |
|
130
|
|
|
|
|
|
|
my ( $class ) = @_; |
|
131
|
|
|
|
|
|
|
|
|
132
|
|
|
|
|
|
|
# Retrieve defaults from DBIx::Ninja->static_class_info(). |
|
133
|
|
|
|
|
|
|
my $info = $class->SUPER::static_class_info(); |
|
134
|
|
|
|
|
|
|
|
|
135
|
|
|
|
|
|
|
# Set defaults common to all your objects. |
|
136
|
|
|
|
|
|
|
$info->set( |
|
137
|
|
|
|
|
|
|
{ |
|
138
|
|
|
|
|
|
|
default_dbh => DBI->connect( |
|
139
|
|
|
|
|
|
|
"dbi:mysql:[database_name]:localhost:3306", |
|
140
|
|
|
|
|
|
|
"[user]", |
|
141
|
|
|
|
|
|
|
"[password]", |
|
142
|
|
|
|
|
|
|
), |
|
143
|
|
|
|
|
|
|
memcache => Cache::Memcached::Fast->new( |
|
144
|
|
|
|
|
|
|
{ |
|
145
|
|
|
|
|
|
|
servers => |
|
146
|
|
|
|
|
|
|
[ |
|
147
|
|
|
|
|
|
|
'localhost:11211', |
|
148
|
|
|
|
|
|
|
], |
|
149
|
|
|
|
|
|
|
} |
|
150
|
|
|
|
|
|
|
), |
|
151
|
|
|
|
|
|
|
} |
|
152
|
|
|
|
|
|
|
); |
|
153
|
|
|
|
|
|
|
|
|
154
|
|
|
|
|
|
|
return $info; |
|
155
|
|
|
|
|
|
|
} |
|
156
|
|
|
|
|
|
|
|
|
157
|
|
|
|
|
|
|
1; |
|
158
|
|
|
|
|
|
|
|
|
159
|
|
|
|
|
|
|
The various classes will then inherit from C, and the inherited |
|
160
|
|
|
|
|
|
|
defaults will make C shorter in the other classes: |
|
161
|
|
|
|
|
|
|
|
|
162
|
|
|
|
|
|
|
package My::Model::Book; |
|
163
|
|
|
|
|
|
|
|
|
164
|
|
|
|
|
|
|
use strict; |
|
165
|
|
|
|
|
|
|
use warnings; |
|
166
|
|
|
|
|
|
|
|
|
167
|
|
|
|
|
|
|
# Inherit from your base model class, not from DBIx::NinjaORM. |
|
168
|
|
|
|
|
|
|
use base 'My::Model'; |
|
169
|
|
|
|
|
|
|
|
|
170
|
|
|
|
|
|
|
sub static_class_info |
|
171
|
|
|
|
|
|
|
{ |
|
172
|
|
|
|
|
|
|
my ( $class ) = @_; |
|
173
|
|
|
|
|
|
|
|
|
174
|
|
|
|
|
|
|
# Retrieve defaults from My::Model. |
|
175
|
|
|
|
|
|
|
my $info = $class->SUPER::static_class_info(); |
|
176
|
|
|
|
|
|
|
|
|
177
|
|
|
|
|
|
|
$info->set( |
|
178
|
|
|
|
|
|
|
{ |
|
179
|
|
|
|
|
|
|
# Set mandatory defaults for this class. |
|
180
|
|
|
|
|
|
|
table_name => 'books', |
|
181
|
|
|
|
|
|
|
primary_key_name => 'book_id', |
|
182
|
|
|
|
|
|
|
|
|
183
|
|
|
|
|
|
|
# Add optional information. |
|
184
|
|
|
|
|
|
|
# Allow filtering SELECTs on books.name. |
|
185
|
|
|
|
|
|
|
filtering_fields => [ 'name' ], |
|
186
|
|
|
|
|
|
|
} |
|
187
|
|
|
|
|
|
|
); |
|
188
|
|
|
|
|
|
|
|
|
189
|
|
|
|
|
|
|
return $info; |
|
190
|
|
|
|
|
|
|
} |
|
191
|
|
|
|
|
|
|
|
|
192
|
|
|
|
|
|
|
1; |
|
193
|
|
|
|
|
|
|
|
|
194
|
|
|
|
|
|
|
=cut |
|
195
|
|
|
|
|
|
|
|
|
196
|
|
|
|
|
|
|
# This hash indicates what argument names are valid in retrieve_list() calls, |
|
197
|
|
|
|
|
|
|
# and for each argument it specifies whether it should be included (1) or |
|
198
|
|
|
|
|
|
|
# ignored (0) when building the list cache keys that associate the arguments |
|
199
|
|
|
|
|
|
|
# passed to the result IDs. |
|
200
|
|
|
|
|
|
|
our $RETRIEVE_LIST_VALID_ARGUMENTS = |
|
201
|
|
|
|
|
|
|
{ |
|
202
|
|
|
|
|
|
|
allow_all => 1, |
|
203
|
|
|
|
|
|
|
dbh => 0, |
|
204
|
|
|
|
|
|
|
limit => 1, |
|
205
|
|
|
|
|
|
|
lock => 0, |
|
206
|
|
|
|
|
|
|
order_by => 1, |
|
207
|
|
|
|
|
|
|
pagination => 1, |
|
208
|
|
|
|
|
|
|
query_extensions => 1, |
|
209
|
|
|
|
|
|
|
show_queries => 0, |
|
210
|
|
|
|
|
|
|
skip_cache => 0, |
|
211
|
|
|
|
|
|
|
exclude_fields => 0, |
|
212
|
|
|
|
|
|
|
select_fields => 0, |
|
213
|
|
|
|
|
|
|
}; |
|
214
|
|
|
|
|
|
|
|
|
215
|
|
|
|
|
|
|
|
|
216
|
|
|
|
|
|
|
=head1 SUPPORTED DATABASES |
|
217
|
|
|
|
|
|
|
|
|
218
|
|
|
|
|
|
|
This distribution currently supports: |
|
219
|
|
|
|
|
|
|
|
|
220
|
|
|
|
|
|
|
=over 4 |
|
221
|
|
|
|
|
|
|
|
|
222
|
|
|
|
|
|
|
=item * SQLite |
|
223
|
|
|
|
|
|
|
|
|
224
|
|
|
|
|
|
|
=item * MySQL |
|
225
|
|
|
|
|
|
|
|
|
226
|
|
|
|
|
|
|
=item * PostgreSQL |
|
227
|
|
|
|
|
|
|
|
|
228
|
|
|
|
|
|
|
=back |
|
229
|
|
|
|
|
|
|
|
|
230
|
|
|
|
|
|
|
Please contact me if you need support for another database type, I'm always |
|
231
|
|
|
|
|
|
|
glad to add extensions if you can help me with testing. |
|
232
|
|
|
|
|
|
|
|
|
233
|
|
|
|
|
|
|
|
|
234
|
|
|
|
|
|
|
=head1 SUBCLASSABLE METHODS |
|
235
|
|
|
|
|
|
|
|
|
236
|
|
|
|
|
|
|
L is designed with inheritance in mind, and you can subclass |
|
237
|
|
|
|
|
|
|
most of its public methods to extend or alter its behavior. |
|
238
|
|
|
|
|
|
|
|
|
239
|
|
|
|
|
|
|
This group of method covers the most commonly subclassed methods, with examples |
|
240
|
|
|
|
|
|
|
and use cases. |
|
241
|
|
|
|
|
|
|
|
|
242
|
|
|
|
|
|
|
|
|
243
|
|
|
|
|
|
|
=head2 clone() |
|
244
|
|
|
|
|
|
|
|
|
245
|
|
|
|
|
|
|
Clone the current object and return the clone. |
|
246
|
|
|
|
|
|
|
|
|
247
|
|
|
|
|
|
|
my $cloned_book = $book->clone(); |
|
248
|
|
|
|
|
|
|
|
|
249
|
|
|
|
|
|
|
=cut |
|
250
|
|
|
|
|
|
|
|
|
251
|
|
|
|
|
|
|
sub clone |
|
252
|
|
|
|
|
|
|
{ |
|
253
|
1
|
|
|
1
|
1
|
42
|
my ( $self ) = @_; |
|
254
|
|
|
|
|
|
|
|
|
255
|
1
|
|
|
|
|
80
|
return Storable::dclone( $self ); |
|
256
|
|
|
|
|
|
|
} |
|
257
|
|
|
|
|
|
|
|
|
258
|
|
|
|
|
|
|
|
|
259
|
|
|
|
|
|
|
=head2 commit() |
|
260
|
|
|
|
|
|
|
|
|
261
|
|
|
|
|
|
|
Convenience function to insert or update the object. |
|
262
|
|
|
|
|
|
|
|
|
263
|
|
|
|
|
|
|
If the object has a primary key set, C is called, otherwise |
|
264
|
|
|
|
|
|
|
C is called. If there's an error, the method with croak with |
|
265
|
|
|
|
|
|
|
relevant error information. |
|
266
|
|
|
|
|
|
|
|
|
267
|
|
|
|
|
|
|
$book->commit(); |
|
268
|
|
|
|
|
|
|
|
|
269
|
|
|
|
|
|
|
Arguments: (none). |
|
270
|
|
|
|
|
|
|
|
|
271
|
|
|
|
|
|
|
=cut |
|
272
|
|
|
|
|
|
|
|
|
273
|
|
|
|
|
|
|
sub commit |
|
274
|
|
|
|
|
|
|
{ |
|
275
|
2
|
|
|
2
|
1
|
60
|
my ( $self ) = @_; |
|
276
|
2
|
|
|
|
|
178
|
my $data = Storable::dclone( $self ); |
|
277
|
|
|
|
|
|
|
|
|
278
|
2
|
100
|
|
|
|
13
|
if ( defined( $self->id() ) ) |
|
279
|
|
|
|
|
|
|
{ |
|
280
|
|
|
|
|
|
|
# If id() is defined, we have a value for the primary key name |
|
281
|
|
|
|
|
|
|
# and we need to delete it from the data to update. |
|
282
|
1
|
|
|
|
|
4
|
my $primary_key_name = $self->get_info('primary_key_name'); |
|
283
|
1
|
|
|
|
|
4
|
delete( $data->{ $primary_key_name } ); |
|
284
|
|
|
|
|
|
|
|
|
285
|
1
|
|
|
|
|
5
|
return $self->update( $data ); |
|
286
|
|
|
|
|
|
|
} |
|
287
|
|
|
|
|
|
|
else |
|
288
|
|
|
|
|
|
|
{ |
|
289
|
1
|
|
|
|
|
7
|
return $self->insert( $data ); |
|
290
|
|
|
|
|
|
|
} |
|
291
|
|
|
|
|
|
|
} |
|
292
|
|
|
|
|
|
|
|
|
293
|
|
|
|
|
|
|
|
|
294
|
|
|
|
|
|
|
=head2 get() |
|
295
|
|
|
|
|
|
|
|
|
296
|
|
|
|
|
|
|
Get the value corresponding to an object's field. |
|
297
|
|
|
|
|
|
|
|
|
298
|
|
|
|
|
|
|
my $book_name = $book->get('name'); |
|
299
|
|
|
|
|
|
|
|
|
300
|
|
|
|
|
|
|
This method will croak if you attempt to retrieve a private field. It also |
|
301
|
|
|
|
|
|
|
detects if the object was retrieved from the database, in which case it |
|
302
|
|
|
|
|
|
|
has an exhaustive list of the fields that actually exist in the database and |
|
303
|
|
|
|
|
|
|
it will croak if you attempt to retrieve a field that doesn't exist in the |
|
304
|
|
|
|
|
|
|
database. |
|
305
|
|
|
|
|
|
|
|
|
306
|
|
|
|
|
|
|
=cut |
|
307
|
|
|
|
|
|
|
|
|
308
|
|
|
|
|
|
|
sub get |
|
309
|
|
|
|
|
|
|
{ |
|
310
|
57
|
|
|
57
|
1
|
11072
|
my ( $self, $field_name ) = @_; |
|
311
|
|
|
|
|
|
|
|
|
312
|
57
|
100
|
100
|
|
|
354
|
croak "The name of the field to retrieve must be defined" |
|
313
|
|
|
|
|
|
|
if !defined( $field_name ) || ( $field_name eq '' ); |
|
314
|
|
|
|
|
|
|
|
|
315
|
|
|
|
|
|
|
# Create your own accessor for private properties. |
|
316
|
55
|
100
|
|
|
|
158
|
croak 'Cannot retrieve the value of a private object property, create an accessor on the class if you need this value' |
|
317
|
|
|
|
|
|
|
if substr( $field_name, 0, 1 ) eq '_'; |
|
318
|
|
|
|
|
|
|
|
|
319
|
|
|
|
|
|
|
# If the object was not populated by retrieve_list(), we know that the keys |
|
320
|
|
|
|
|
|
|
# on the object correspond to all the columns in the database and we can then |
|
321
|
|
|
|
|
|
|
# actively show errors in the log if the caller is requesting a field for |
|
322
|
|
|
|
|
|
|
# which the key doesn't exist. |
|
323
|
54
|
|
100
|
|
|
195
|
my $populated_by_retrieve_list = $self->{'_populated_by_retrieve_list'} // 0; |
|
324
|
54
|
50
|
66
|
|
|
251
|
croak "The property '$field_name' does not exist on the object" |
|
325
|
|
|
|
|
|
|
if $populated_by_retrieve_list && !exists( $self->{ $field_name } ); |
|
326
|
|
|
|
|
|
|
|
|
327
|
54
|
|
|
|
|
562
|
return $self->{ $field_name }; |
|
328
|
|
|
|
|
|
|
} |
|
329
|
|
|
|
|
|
|
|
|
330
|
|
|
|
|
|
|
|
|
331
|
|
|
|
|
|
|
=head2 get_current_time() |
|
332
|
|
|
|
|
|
|
|
|
333
|
|
|
|
|
|
|
Return the current time, to use in SQL statements. |
|
334
|
|
|
|
|
|
|
|
|
335
|
|
|
|
|
|
|
my $current_time = $class->get_current_time( $field_name ); |
|
336
|
|
|
|
|
|
|
|
|
337
|
|
|
|
|
|
|
By default, DBIx::NinjaORM assumes that time is stored as unixtime (integer) in the database. If you are using a different field type for C and C, you can subclass this method to return the current time in a different format. |
|
338
|
|
|
|
|
|
|
|
|
339
|
|
|
|
|
|
|
Arguments: |
|
340
|
|
|
|
|
|
|
|
|
341
|
|
|
|
|
|
|
=over 4 |
|
342
|
|
|
|
|
|
|
|
|
343
|
|
|
|
|
|
|
=item * $field_name |
|
344
|
|
|
|
|
|
|
|
|
345
|
|
|
|
|
|
|
The name of the field that will be populated with the return value. |
|
346
|
|
|
|
|
|
|
|
|
347
|
|
|
|
|
|
|
=back |
|
348
|
|
|
|
|
|
|
|
|
349
|
|
|
|
|
|
|
Notes: |
|
350
|
|
|
|
|
|
|
|
|
351
|
|
|
|
|
|
|
=over 4 |
|
352
|
|
|
|
|
|
|
|
|
353
|
|
|
|
|
|
|
=item * |
|
354
|
|
|
|
|
|
|
|
|
355
|
|
|
|
|
|
|
The return value of this method will be inserted directly into the database, so |
|
356
|
|
|
|
|
|
|
you can use C for example, and if you are inserting strings those should |
|
357
|
|
|
|
|
|
|
be quoted in the subclassed method. |
|
358
|
|
|
|
|
|
|
|
|
359
|
|
|
|
|
|
|
=back |
|
360
|
|
|
|
|
|
|
|
|
361
|
|
|
|
|
|
|
=cut |
|
362
|
|
|
|
|
|
|
|
|
363
|
|
|
|
|
|
|
sub get_current_time |
|
364
|
|
|
|
|
|
|
{ |
|
365
|
105
|
|
|
105
|
1
|
1682
|
my ( $self, $field_name ) = @_; |
|
366
|
|
|
|
|
|
|
|
|
367
|
105
|
|
|
|
|
438
|
return time(); |
|
368
|
|
|
|
|
|
|
} |
|
369
|
|
|
|
|
|
|
|
|
370
|
|
|
|
|
|
|
|
|
371
|
|
|
|
|
|
|
=head2 insert() |
|
372
|
|
|
|
|
|
|
|
|
373
|
|
|
|
|
|
|
Insert a row corresponding to the data passed as first parameter, and fill the |
|
374
|
|
|
|
|
|
|
object accordingly upon success. |
|
375
|
|
|
|
|
|
|
|
|
376
|
|
|
|
|
|
|
my $book = My::Model::Book->new(); |
|
377
|
|
|
|
|
|
|
$book->insert( |
|
378
|
|
|
|
|
|
|
{ |
|
379
|
|
|
|
|
|
|
name => 'Learning Perl', |
|
380
|
|
|
|
|
|
|
} |
|
381
|
|
|
|
|
|
|
); |
|
382
|
|
|
|
|
|
|
|
|
383
|
|
|
|
|
|
|
If you don't need the object afterwards, you can simply do: |
|
384
|
|
|
|
|
|
|
|
|
385
|
|
|
|
|
|
|
My::Model::Book->insert( |
|
386
|
|
|
|
|
|
|
{ |
|
387
|
|
|
|
|
|
|
name => 'Learning Perl', |
|
388
|
|
|
|
|
|
|
} |
|
389
|
|
|
|
|
|
|
); |
|
390
|
|
|
|
|
|
|
|
|
391
|
|
|
|
|
|
|
This method supports the following optional arguments: |
|
392
|
|
|
|
|
|
|
|
|
393
|
|
|
|
|
|
|
=over 4 |
|
394
|
|
|
|
|
|
|
|
|
395
|
|
|
|
|
|
|
=item * overwrite_created |
|
396
|
|
|
|
|
|
|
|
|
397
|
|
|
|
|
|
|
A UNIX timestamp to be used instead of the current time for the value of |
|
398
|
|
|
|
|
|
|
'created'. |
|
399
|
|
|
|
|
|
|
|
|
400
|
|
|
|
|
|
|
=item * generated_primary_key_value |
|
401
|
|
|
|
|
|
|
|
|
402
|
|
|
|
|
|
|
A primary key value, in case the underlying table doesn't have an |
|
403
|
|
|
|
|
|
|
autoincremented primary key. |
|
404
|
|
|
|
|
|
|
|
|
405
|
|
|
|
|
|
|
=item * dbh |
|
406
|
|
|
|
|
|
|
|
|
407
|
|
|
|
|
|
|
A different database handle than the default one specified in |
|
408
|
|
|
|
|
|
|
C, but it has to be writable. |
|
409
|
|
|
|
|
|
|
|
|
410
|
|
|
|
|
|
|
=item * ignore |
|
411
|
|
|
|
|
|
|
|
|
412
|
|
|
|
|
|
|
INSERT IGNORE instead of plain INSERT. |
|
413
|
|
|
|
|
|
|
|
|
414
|
|
|
|
|
|
|
=back |
|
415
|
|
|
|
|
|
|
|
|
416
|
|
|
|
|
|
|
$book->insert( |
|
417
|
|
|
|
|
|
|
\%data, |
|
418
|
|
|
|
|
|
|
overwrite_created => $unixtime, |
|
419
|
|
|
|
|
|
|
generated_primary_key_value => $value, |
|
420
|
|
|
|
|
|
|
dbh => $dbh, |
|
421
|
|
|
|
|
|
|
ignore => $boolean, |
|
422
|
|
|
|
|
|
|
); |
|
423
|
|
|
|
|
|
|
|
|
424
|
|
|
|
|
|
|
=cut |
|
425
|
|
|
|
|
|
|
|
|
426
|
|
|
|
|
|
|
sub insert ## no critic (Subroutines::RequireArgUnpacking) |
|
427
|
|
|
|
|
|
|
{ |
|
428
|
60
|
100
|
|
60
|
1
|
23301
|
croak 'The first argument passed must be a hashref' |
|
429
|
|
|
|
|
|
|
if !Data::Validate::Type::is_hashref( $_[1] ); |
|
430
|
|
|
|
|
|
|
|
|
431
|
59
|
|
|
|
|
2369
|
my ( $self, $data, %args ) = @_; |
|
432
|
|
|
|
|
|
|
|
|
433
|
|
|
|
|
|
|
# Allows calling Module->insert() if we don't need the object afterwards. |
|
434
|
|
|
|
|
|
|
# In this case, we turn $self from a class into an object. |
|
435
|
59
|
100
|
|
|
|
273
|
$self = $self->new() |
|
436
|
|
|
|
|
|
|
if !ref( $self ); |
|
437
|
|
|
|
|
|
|
|
|
438
|
|
|
|
|
|
|
# Allow using a different database handle. |
|
439
|
59
|
|
|
|
|
592
|
my $dbh = $self->assert_dbh( $args{'dbh'} ); |
|
440
|
|
|
|
|
|
|
|
|
441
|
|
|
|
|
|
|
# Clean input. |
|
442
|
56
|
|
|
|
|
630
|
my $clean_data = $self->validate_data( $data, %args ); |
|
443
|
56
|
50
|
|
|
|
196
|
return 0 |
|
444
|
|
|
|
|
|
|
if !defined( $clean_data ); |
|
445
|
|
|
|
|
|
|
|
|
446
|
|
|
|
|
|
|
# Retrieve the metadata for that table. |
|
447
|
56
|
|
|
|
|
109
|
my $class = ref( $self ); |
|
448
|
56
|
|
|
|
|
169
|
my $table_name = $self->get_info('table_name'); |
|
449
|
56
|
50
|
|
|
|
237
|
croak "The table name for class '$class' is not defined" |
|
450
|
|
|
|
|
|
|
if !defined( $table_name ); |
|
451
|
|
|
|
|
|
|
|
|
452
|
56
|
|
|
|
|
206
|
my $primary_key_name = $self->get_info('primary_key_name'); |
|
453
|
56
|
50
|
33
|
|
|
310
|
croak "Missing primary key name for class '$class', cannot force primary key value" |
|
454
|
|
|
|
|
|
|
if !defined( $primary_key_name ) && defined( $args{'generated_primary_key_value'} ); |
|
455
|
|
|
|
|
|
|
|
|
456
|
|
|
|
|
|
|
# Set defaults. |
|
457
|
56
|
100
|
|
|
|
198
|
if ( $self->get_info('has_created_field') ) |
|
458
|
|
|
|
|
|
|
{ |
|
459
|
53
|
100
|
66
|
|
|
1050
|
$clean_data->{'created'} = defined( $args{'overwrite_created'} ) && $args{'overwrite_created'} ne '' |
|
460
|
|
|
|
|
|
|
? $args{'overwrite_created'} |
|
461
|
|
|
|
|
|
|
: $self->get_current_time(); |
|
462
|
|
|
|
|
|
|
} |
|
463
|
56
|
100
|
|
|
|
236
|
$clean_data->{'modified'} = $self->get_current_time() |
|
464
|
|
|
|
|
|
|
if $self->get_info('has_modified_field'); |
|
465
|
56
|
100
|
|
|
|
263
|
$clean_data->{ $primary_key_name } = $args{'generated_primary_key_value'} |
|
466
|
|
|
|
|
|
|
if defined( $args{'generated_primary_key_value'} ); |
|
467
|
|
|
|
|
|
|
|
|
468
|
|
|
|
|
|
|
# Prepare the query elements. |
|
469
|
56
|
50
|
33
|
|
|
320
|
my $ignore = defined( $args{'ignore'} ) && $args{'ignore'} ? 1 : 0; |
|
470
|
56
|
|
|
|
|
136
|
my @sql_fields = (); |
|
471
|
56
|
|
|
|
|
127
|
my @sql_values = (); |
|
472
|
56
|
|
|
|
|
108
|
my @placeholder_values = (); |
|
473
|
56
|
|
|
|
|
202
|
foreach my $key ( keys %$clean_data ) |
|
474
|
|
|
|
|
|
|
{ |
|
475
|
172
|
|
|
|
|
265
|
push( @sql_fields, $key ); |
|
476
|
|
|
|
|
|
|
|
|
477
|
|
|
|
|
|
|
# 'created' and 'modified' support SQL keywords, so we don't use |
|
478
|
|
|
|
|
|
|
# placeholders. |
|
479
|
172
|
100
|
|
|
|
1574
|
if ( $key =~ /^(?:created|modified)$/x ) |
|
480
|
|
|
|
|
|
|
{ |
|
481
|
106
|
|
|
|
|
295
|
push( @sql_values, $clean_data->{ $key } ); |
|
482
|
|
|
|
|
|
|
} |
|
483
|
|
|
|
|
|
|
else |
|
484
|
|
|
|
|
|
|
{ |
|
485
|
|
|
|
|
|
|
# All the other data need to be inserted using placeholders, for |
|
486
|
|
|
|
|
|
|
# security purposes. |
|
487
|
66
|
|
|
|
|
299
|
push( @sql_values, '?' ); |
|
488
|
66
|
|
|
|
|
198
|
push( @placeholder_values, $clean_data->{ $key } ); |
|
489
|
|
|
|
|
|
|
} |
|
490
|
|
|
|
|
|
|
} |
|
491
|
|
|
|
|
|
|
|
|
492
|
56
|
50
|
|
|
|
1056
|
my $query = sprintf( |
|
493
|
|
|
|
|
|
|
q| |
|
494
|
|
|
|
|
|
|
INSERT %s INTO %s( %s ) |
|
495
|
|
|
|
|
|
|
VALUES ( %s ) |
|
496
|
|
|
|
|
|
|
|, |
|
497
|
|
|
|
|
|
|
$ignore ? 'IGNORE' : '', |
|
498
|
|
|
|
|
|
|
$dbh->quote_identifier( $table_name ), |
|
499
|
|
|
|
|
|
|
join( ', ', @sql_fields ), |
|
500
|
|
|
|
|
|
|
join( ', ', @sql_values ), |
|
501
|
|
|
|
|
|
|
); |
|
502
|
|
|
|
|
|
|
|
|
503
|
|
|
|
|
|
|
# Insert. |
|
504
|
|
|
|
|
|
|
try |
|
505
|
|
|
|
|
|
|
{ |
|
506
|
56
|
|
|
56
|
|
14697
|
local $dbh->{'RaiseError'} = 1; |
|
507
|
56
|
|
|
|
|
530
|
$dbh->do( |
|
508
|
|
|
|
|
|
|
$query, |
|
509
|
|
|
|
|
|
|
{}, |
|
510
|
|
|
|
|
|
|
@placeholder_values, |
|
511
|
|
|
|
|
|
|
); |
|
512
|
|
|
|
|
|
|
} |
|
513
|
|
|
|
|
|
|
catch |
|
514
|
|
|
|
|
|
|
{ |
|
515
|
1
|
|
|
1
|
|
31
|
$log->fatalf( |
|
516
|
|
|
|
|
|
|
"Could not insert row: %s\nQuery: %s\nValues: %s", |
|
517
|
|
|
|
|
|
|
$_, |
|
518
|
|
|
|
|
|
|
$query, |
|
519
|
|
|
|
|
|
|
\@placeholder_values, |
|
520
|
|
|
|
|
|
|
); |
|
521
|
1
|
|
|
|
|
24
|
croak "Insert failed: $_"; |
|
522
|
56
|
|
|
|
|
4613
|
}; |
|
523
|
|
|
|
|
|
|
|
|
524
|
55
|
50
|
|
|
|
23785310
|
if ( defined( $primary_key_name ) ) |
|
525
|
|
|
|
|
|
|
{ |
|
526
|
55
|
100
|
|
|
|
1542
|
$clean_data->{ $primary_key_name } = defined( $args{'generated_primary_key_value'} ) |
|
527
|
|
|
|
|
|
|
? $args{'generated_primary_key_value'} |
|
528
|
|
|
|
|
|
|
: $dbh->last_insert_id( undef, undef, $table_name, $primary_key_name ); |
|
529
|
|
|
|
|
|
|
} |
|
530
|
|
|
|
|
|
|
|
|
531
|
|
|
|
|
|
|
# Check that the object was correctly inserted. |
|
532
|
55
|
50
|
33
|
|
|
1115
|
croak "Could not insert into table '$table_name': " . dumper( $data ) |
|
533
|
|
|
|
|
|
|
if defined( $primary_key_name ) && !defined( $clean_data->{ $primary_key_name } ); |
|
534
|
|
|
|
|
|
|
|
|
535
|
|
|
|
|
|
|
# Make sure that the object reflects the changes in the database. |
|
536
|
55
|
|
|
|
|
2004
|
$self->set( |
|
537
|
|
|
|
|
|
|
$clean_data, |
|
538
|
|
|
|
|
|
|
force => 1, |
|
539
|
|
|
|
|
|
|
); |
|
540
|
|
|
|
|
|
|
|
|
541
|
55
|
|
|
|
|
1085
|
return; |
|
542
|
|
|
|
|
|
|
} |
|
543
|
|
|
|
|
|
|
|
|
544
|
|
|
|
|
|
|
|
|
545
|
|
|
|
|
|
|
=head2 new() |
|
546
|
|
|
|
|
|
|
|
|
547
|
|
|
|
|
|
|
C has two possible uses: |
|
548
|
|
|
|
|
|
|
|
|
549
|
|
|
|
|
|
|
=over 4 |
|
550
|
|
|
|
|
|
|
|
|
551
|
|
|
|
|
|
|
=item * Creating a new empty object |
|
552
|
|
|
|
|
|
|
|
|
553
|
|
|
|
|
|
|
my $object = My::Model::Book->new(); |
|
554
|
|
|
|
|
|
|
|
|
555
|
|
|
|
|
|
|
=item * Retrieving a single object from the database. |
|
556
|
|
|
|
|
|
|
|
|
557
|
|
|
|
|
|
|
# Retrieve by ID. |
|
558
|
|
|
|
|
|
|
my $object = My::Model::Book->new( { id => 3 } ) |
|
559
|
|
|
|
|
|
|
// die 'Book #3 does not exist'; |
|
560
|
|
|
|
|
|
|
|
|
561
|
|
|
|
|
|
|
# Retrieve by unique field. |
|
562
|
|
|
|
|
|
|
my $object = My::Model::Book->new( { isbn => '9781449303587' } ) |
|
563
|
|
|
|
|
|
|
// die 'Book with ISBN 9781449303587 does not exist'; |
|
564
|
|
|
|
|
|
|
|
|
565
|
|
|
|
|
|
|
=back |
|
566
|
|
|
|
|
|
|
|
|
567
|
|
|
|
|
|
|
When retrieving a single object from the database, the first argument should be |
|
568
|
|
|
|
|
|
|
a hashref containing the following information to select a single row: |
|
569
|
|
|
|
|
|
|
|
|
570
|
|
|
|
|
|
|
=over 4 |
|
571
|
|
|
|
|
|
|
|
|
572
|
|
|
|
|
|
|
=item * id |
|
573
|
|
|
|
|
|
|
|
|
574
|
|
|
|
|
|
|
The ID for the primary key on the underlying table. C is an alias for the |
|
575
|
|
|
|
|
|
|
primary key field name. |
|
576
|
|
|
|
|
|
|
|
|
577
|
|
|
|
|
|
|
my $object = My::Model::Book->new( { id => 3 } ) |
|
578
|
|
|
|
|
|
|
// die 'Book #3 does not exist'; |
|
579
|
|
|
|
|
|
|
|
|
580
|
|
|
|
|
|
|
=item * A unique field |
|
581
|
|
|
|
|
|
|
|
|
582
|
|
|
|
|
|
|
Allows passing a unique field and its value, in order to load the |
|
583
|
|
|
|
|
|
|
corresponding object from the database. |
|
584
|
|
|
|
|
|
|
|
|
585
|
|
|
|
|
|
|
my $object = My::Model::Book->new( { isbn => '9781449303587' } ) |
|
586
|
|
|
|
|
|
|
// die 'Book with ISBN 9781449303587 does not exist'; |
|
587
|
|
|
|
|
|
|
|
|
588
|
|
|
|
|
|
|
Note that unique fields need to be defined in C, in the |
|
589
|
|
|
|
|
|
|
C key. |
|
590
|
|
|
|
|
|
|
|
|
591
|
|
|
|
|
|
|
=back |
|
592
|
|
|
|
|
|
|
|
|
593
|
|
|
|
|
|
|
This method also supports the following optional arguments, passed in a hash |
|
594
|
|
|
|
|
|
|
after the filtering criteria above-mentioned: |
|
595
|
|
|
|
|
|
|
|
|
596
|
|
|
|
|
|
|
=over 4 |
|
597
|
|
|
|
|
|
|
|
|
598
|
|
|
|
|
|
|
=item * skip_cache (default: 0) |
|
599
|
|
|
|
|
|
|
|
|
600
|
|
|
|
|
|
|
By default, if cache is enabled with C in |
|
601
|
|
|
|
|
|
|
C, then C attempts to load the object from the cache |
|
602
|
|
|
|
|
|
|
first. Setting C to 1 forces the ORM to load the values from the |
|
603
|
|
|
|
|
|
|
database. |
|
604
|
|
|
|
|
|
|
|
|
605
|
|
|
|
|
|
|
my $object = My::Model::Book->new( |
|
606
|
|
|
|
|
|
|
{ isbn => '9781449303587' }, |
|
607
|
|
|
|
|
|
|
skip_cache => 1, |
|
608
|
|
|
|
|
|
|
) // die 'Book with ISBN 9781449303587 does not exist'; |
|
609
|
|
|
|
|
|
|
|
|
610
|
|
|
|
|
|
|
=item * lock (default: 0) |
|
611
|
|
|
|
|
|
|
|
|
612
|
|
|
|
|
|
|
By default, the underlying row is not locked when retrieving an object via |
|
613
|
|
|
|
|
|
|
C. Setting C to 1 forces the ORM to bypass the cache if any, and |
|
614
|
|
|
|
|
|
|
to lock the rows in the database as it retrieves them. |
|
615
|
|
|
|
|
|
|
|
|
616
|
|
|
|
|
|
|
my $object = My::Model::Book->new( |
|
617
|
|
|
|
|
|
|
{ isbn => '9781449303587' }, |
|
618
|
|
|
|
|
|
|
lock => 1, |
|
619
|
|
|
|
|
|
|
) // die 'Book with ISBN 9781449303587 does not exist'; |
|
620
|
|
|
|
|
|
|
|
|
621
|
|
|
|
|
|
|
=back |
|
622
|
|
|
|
|
|
|
|
|
623
|
|
|
|
|
|
|
=cut |
|
624
|
|
|
|
|
|
|
|
|
625
|
|
|
|
|
|
|
sub new |
|
626
|
|
|
|
|
|
|
{ |
|
627
|
79
|
|
|
79
|
1
|
201640
|
my ( $class, $filters, %args ) = @_; |
|
628
|
|
|
|
|
|
|
|
|
629
|
|
|
|
|
|
|
# If filters exist, they need to be a hashref. |
|
630
|
79
|
50
|
66
|
|
|
923
|
croak 'The first argument must be a hashref containing filtering criteria' |
|
631
|
|
|
|
|
|
|
if defined( $filters ) && !Data::Validate::Type::is_hashref( $filters ); |
|
632
|
|
|
|
|
|
|
|
|
633
|
|
|
|
|
|
|
# Check if we have a unique identifier passed. |
|
634
|
|
|
|
|
|
|
# Note: passing an ID is a subcase of passing field defined as unique, but |
|
635
|
|
|
|
|
|
|
# unique_fields() doesn't include the primary key name. |
|
636
|
79
|
|
|
|
|
577
|
my $unique_field; |
|
637
|
79
|
|
50
|
|
|
190
|
foreach my $field ( 'id', @{ $class->get_info('unique_fields') // [] } ) |
|
|
79
|
|
|
|
|
772
|
|
|
638
|
|
|
|
|
|
|
{ |
|
639
|
|
|
|
|
|
|
next |
|
640
|
82
|
100
|
|
|
|
467
|
if ! exists( $filters->{ $field } ); |
|
641
|
|
|
|
|
|
|
|
|
642
|
|
|
|
|
|
|
# If the field exists in the list of filters, it needs to be |
|
643
|
|
|
|
|
|
|
# defined. Being undefined probably indicates a problem in the calling code. |
|
644
|
9
|
50
|
|
|
|
59
|
croak "Called new() with '$field' declared but not defined" |
|
645
|
|
|
|
|
|
|
if ! defined( $filters->{ $field } ); |
|
646
|
|
|
|
|
|
|
|
|
647
|
|
|
|
|
|
|
# Detect if we're passing two unique fields to retrieve the object. This is |
|
648
|
|
|
|
|
|
|
# obviously bad. |
|
649
|
9
|
50
|
|
|
|
45
|
croak "Called new() with the unique argument '$field', but already found another unique argument '$unique_field'" |
|
650
|
|
|
|
|
|
|
if defined( $unique_field ); |
|
651
|
|
|
|
|
|
|
|
|
652
|
9
|
|
|
|
|
101
|
$unique_field = $field; |
|
653
|
|
|
|
|
|
|
} |
|
654
|
|
|
|
|
|
|
|
|
655
|
|
|
|
|
|
|
# Retrieve the object. |
|
656
|
79
|
|
|
|
|
194
|
my $self; |
|
657
|
79
|
100
|
|
|
|
291
|
if ( defined( $unique_field ) ) |
|
658
|
|
|
|
|
|
|
{ |
|
659
|
9
|
50
|
|
|
|
265
|
my $objects = $class->retrieve_list( |
|
660
|
|
|
|
|
|
|
{ |
|
661
|
|
|
|
|
|
|
$unique_field => $filters->{ $unique_field }, |
|
662
|
|
|
|
|
|
|
}, |
|
663
|
|
|
|
|
|
|
skip_cache => $args{'skip_cache'}, |
|
664
|
|
|
|
|
|
|
lock => $args{'lock'} ? 1 : 0, |
|
665
|
|
|
|
|
|
|
); |
|
666
|
|
|
|
|
|
|
|
|
667
|
9
|
|
|
|
|
42
|
my $objects_count = scalar( @$objects ); |
|
668
|
9
|
50
|
|
|
|
195
|
if ( $objects_count == 0 ) |
|
|
|
50
|
|
|
|
|
|
|
669
|
|
|
|
|
|
|
{ |
|
670
|
|
|
|
|
|
|
# No row found. |
|
671
|
0
|
|
|
|
|
0
|
$self = undef; |
|
672
|
|
|
|
|
|
|
} |
|
673
|
|
|
|
|
|
|
elsif ( $objects_count == 1 ) |
|
674
|
|
|
|
|
|
|
{ |
|
675
|
9
|
|
|
|
|
32
|
$self = $objects->[0]; |
|
676
|
|
|
|
|
|
|
} |
|
677
|
|
|
|
|
|
|
else |
|
678
|
|
|
|
|
|
|
{ |
|
679
|
0
|
|
|
|
|
0
|
croak "Called new() with a set of non-unique arguments that returned $objects_count objects: " . dumper( \%args ); |
|
680
|
|
|
|
|
|
|
} |
|
681
|
|
|
|
|
|
|
} |
|
682
|
|
|
|
|
|
|
else |
|
683
|
|
|
|
|
|
|
{ |
|
684
|
70
|
|
|
|
|
343
|
$self = bless( {}, $class ); |
|
685
|
|
|
|
|
|
|
} |
|
686
|
|
|
|
|
|
|
|
|
687
|
79
|
|
|
|
|
800
|
return $self; |
|
688
|
|
|
|
|
|
|
} |
|
689
|
|
|
|
|
|
|
|
|
690
|
|
|
|
|
|
|
|
|
691
|
|
|
|
|
|
|
=head2 remove() |
|
692
|
|
|
|
|
|
|
|
|
693
|
|
|
|
|
|
|
Delete in the database the row corresponding to the current object. |
|
694
|
|
|
|
|
|
|
|
|
695
|
|
|
|
|
|
|
$book->remove(); |
|
696
|
|
|
|
|
|
|
|
|
697
|
|
|
|
|
|
|
This method accepts the following arguments: |
|
698
|
|
|
|
|
|
|
|
|
699
|
|
|
|
|
|
|
=over 4 |
|
700
|
|
|
|
|
|
|
|
|
701
|
|
|
|
|
|
|
=item * dbh |
|
702
|
|
|
|
|
|
|
|
|
703
|
|
|
|
|
|
|
A different database handle from the default specified in C. |
|
704
|
|
|
|
|
|
|
This is particularly useful if you have separate reader/writer databases. |
|
705
|
|
|
|
|
|
|
|
|
706
|
|
|
|
|
|
|
=back |
|
707
|
|
|
|
|
|
|
|
|
708
|
|
|
|
|
|
|
=cut |
|
709
|
|
|
|
|
|
|
|
|
710
|
|
|
|
|
|
|
sub remove |
|
711
|
|
|
|
|
|
|
{ |
|
712
|
5
|
|
|
5
|
1
|
2114
|
my ( $self, %args ) = @_; |
|
713
|
|
|
|
|
|
|
|
|
714
|
|
|
|
|
|
|
# Retrieve the metadata for that table. |
|
715
|
5
|
|
|
|
|
13
|
my $class = ref( $self ); |
|
716
|
5
|
|
|
|
|
25
|
my $table_name = $self->get_info('table_name'); |
|
717
|
5
|
100
|
|
|
|
42
|
croak "The table name for class '$class' is not defined" |
|
718
|
|
|
|
|
|
|
if ! defined( $table_name ); |
|
719
|
|
|
|
|
|
|
|
|
720
|
4
|
|
|
|
|
12
|
my $primary_key_name = $self->get_info('primary_key_name'); |
|
721
|
4
|
100
|
|
|
|
31
|
croak "Missing primary key name for class '$class', cannot delete safely" |
|
722
|
|
|
|
|
|
|
if !defined( $primary_key_name ); |
|
723
|
|
|
|
|
|
|
|
|
724
|
3
|
100
|
|
|
|
23
|
croak "The object of class '$class' does not have a primary key value, cannot delete" |
|
725
|
|
|
|
|
|
|
if ! defined( $self->id() ); |
|
726
|
|
|
|
|
|
|
|
|
727
|
|
|
|
|
|
|
# Allow using a different DB handle. |
|
728
|
2
|
|
|
|
|
17
|
my $dbh = $self->assert_dbh( $args{'dbh'} ); |
|
729
|
|
|
|
|
|
|
|
|
730
|
|
|
|
|
|
|
# Prepare the query. |
|
731
|
2
|
|
|
|
|
33
|
my $query = sprintf( |
|
732
|
|
|
|
|
|
|
q| |
|
733
|
|
|
|
|
|
|
DELETE |
|
734
|
|
|
|
|
|
|
FROM %s |
|
735
|
|
|
|
|
|
|
WHERE %s = ? |
|
736
|
|
|
|
|
|
|
|, |
|
737
|
|
|
|
|
|
|
$dbh->quote_identifier( $table_name ), |
|
738
|
|
|
|
|
|
|
$dbh->quote_identifier( $primary_key_name ), |
|
739
|
|
|
|
|
|
|
); |
|
740
|
2
|
|
|
|
|
124
|
my @query_values = ( $self->id() ); |
|
741
|
|
|
|
|
|
|
|
|
742
|
|
|
|
|
|
|
# Delete the row. |
|
743
|
|
|
|
|
|
|
try |
|
744
|
|
|
|
|
|
|
{ |
|
745
|
2
|
|
|
2
|
|
135
|
local $dbh->{'RaiseError'} = 1; |
|
746
|
2
|
|
|
|
|
18
|
$dbh->do( |
|
747
|
|
|
|
|
|
|
$query, |
|
748
|
|
|
|
|
|
|
{}, |
|
749
|
|
|
|
|
|
|
@query_values, |
|
750
|
|
|
|
|
|
|
); |
|
751
|
|
|
|
|
|
|
} |
|
752
|
|
|
|
|
|
|
catch |
|
753
|
|
|
|
|
|
|
{ |
|
754
|
1
|
|
|
1
|
|
33
|
$log->fatalf( |
|
755
|
|
|
|
|
|
|
"Could not delete row: %s\nQuery: %s\nValues: %s", |
|
756
|
|
|
|
|
|
|
$_, |
|
757
|
|
|
|
|
|
|
$query, |
|
758
|
|
|
|
|
|
|
\@query_values, |
|
759
|
|
|
|
|
|
|
); |
|
760
|
1
|
|
|
|
|
26
|
croak "Remove failed: $_"; |
|
761
|
2
|
|
|
|
|
23
|
}; |
|
762
|
|
|
|
|
|
|
|
|
763
|
1
|
|
|
|
|
17888
|
return; |
|
764
|
|
|
|
|
|
|
} |
|
765
|
|
|
|
|
|
|
|
|
766
|
|
|
|
|
|
|
|
|
767
|
|
|
|
|
|
|
=head2 retrieve_list_nocache() |
|
768
|
|
|
|
|
|
|
|
|
769
|
|
|
|
|
|
|
Dispatch of retrieve_list() when objects should not be retrieved from the cache. |
|
770
|
|
|
|
|
|
|
|
|
771
|
|
|
|
|
|
|
See C for the parameters this method accepts. |
|
772
|
|
|
|
|
|
|
|
|
773
|
|
|
|
|
|
|
=cut |
|
774
|
|
|
|
|
|
|
|
|
775
|
|
|
|
|
|
|
sub retrieve_list_nocache ## no critic (Subroutines::ProhibitExcessComplexity) |
|
776
|
|
|
|
|
|
|
{ |
|
777
|
27
|
|
|
27
|
1
|
12368
|
my ( $class, $filters, %args ) = @_; |
|
778
|
|
|
|
|
|
|
|
|
779
|
|
|
|
|
|
|
# Handle a different database handle, if requested. |
|
780
|
27
|
|
|
|
|
239
|
my $dbh = $class->assert_dbh( $args{'dbh'} ); |
|
781
|
|
|
|
|
|
|
|
|
782
|
|
|
|
|
|
|
# TODO: If we're asked to lock the rows, we check that we're in a transaction. |
|
783
|
|
|
|
|
|
|
|
|
784
|
|
|
|
|
|
|
# Check if we were passed arguments we don't know how to handle. This will |
|
785
|
|
|
|
|
|
|
# help the calling code to detect typos or deprecated arguments. |
|
786
|
27
|
|
|
|
|
134
|
foreach my $arg ( keys %args ) |
|
787
|
|
|
|
|
|
|
{ |
|
788
|
36
|
50
|
|
|
|
186
|
next if defined( $RETRIEVE_LIST_VALID_ARGUMENTS->{ $arg } ); |
|
789
|
|
|
|
|
|
|
|
|
790
|
0
|
|
|
|
|
0
|
croak( |
|
791
|
|
|
|
|
|
|
"The argument '$arg' passed to DBIx::NinjaORM->retrieve_list() via " . |
|
792
|
|
|
|
|
|
|
"${class}->retrieve_list() is not handled by the superclass. " . |
|
793
|
|
|
|
|
|
|
"It could mean that you have a typo in the name or that the argument has " . |
|
794
|
|
|
|
|
|
|
"been deprecated." |
|
795
|
|
|
|
|
|
|
); |
|
796
|
|
|
|
|
|
|
} |
|
797
|
|
|
|
|
|
|
|
|
798
|
|
|
|
|
|
|
# Check the parameters and prepare the corresponding where clauses. |
|
799
|
27
|
|
50
|
|
|
252
|
my $where_clauses = $args{'query_extensions'}->{'where_clauses'} || []; |
|
800
|
27
|
|
50
|
|
|
188
|
my $where_values = $args{'query_extensions'}->{'where_values'} || []; |
|
801
|
27
|
|
|
|
|
83
|
my $filtering_field_keys_passed = 0; |
|
802
|
27
|
|
|
|
|
366
|
my $filtering_criteria = $class->parse_filtering_criteria( |
|
803
|
|
|
|
|
|
|
$filters |
|
804
|
|
|
|
|
|
|
); |
|
805
|
26
|
50
|
|
|
|
111
|
if ( defined( $filtering_criteria ) ) |
|
806
|
|
|
|
|
|
|
{ |
|
807
|
26
|
50
|
|
|
|
63
|
push( @$where_clauses, @{ $filtering_criteria->[0] || [] } ); |
|
|
26
|
|
|
|
|
215
|
|
|
808
|
26
|
50
|
|
|
|
54
|
push( @$where_values, @{ $filtering_criteria->[1] || [] } ); |
|
|
26
|
|
|
|
|
121
|
|
|
809
|
26
|
|
|
|
|
58
|
$filtering_field_keys_passed = $filtering_criteria->[2]; |
|
810
|
|
|
|
|
|
|
} |
|
811
|
|
|
|
|
|
|
|
|
812
|
|
|
|
|
|
|
# Make sure there's at least one argument, unless allow_all=1 or there is |
|
813
|
|
|
|
|
|
|
# custom where clauses. |
|
814
|
26
|
100
|
100
|
|
|
837
|
croak 'At least one argument must be passed' |
|
|
|
|
66
|
|
|
|
|
|
815
|
|
|
|
|
|
|
if !$args{'allow_all'} && !$filtering_field_keys_passed && scalar( @$where_clauses ) == 0; |
|
816
|
|
|
|
|
|
|
|
|
817
|
|
|
|
|
|
|
# Prepare the ORDER BY. |
|
818
|
22
|
|
|
|
|
106
|
my $table_name = $class->get_info('table_name'); |
|
819
|
22
|
100
|
66
|
|
|
407
|
my $order_by = defined( $args{'order_by'} ) && ( $args{'order_by'} ne '' ) |
|
|
|
100
|
|
|
|
|
|
|
820
|
|
|
|
|
|
|
? "ORDER BY $args{'order_by'}" |
|
821
|
|
|
|
|
|
|
: $class->get_info('has_created_field') |
|
822
|
|
|
|
|
|
|
? "ORDER BY " . $dbh->quote_identifier( $table_name ) . ".created ASC" |
|
823
|
|
|
|
|
|
|
: "ORDER BY " . $dbh->quote_identifier( $table_name ) . '.' . $class->get_info('primary_key_name'); |
|
824
|
|
|
|
|
|
|
|
|
825
|
|
|
|
|
|
|
# Prepare quoted identifiers. |
|
826
|
22
|
|
|
|
|
571
|
my $primary_key_name = $class->get_info('primary_key_name'); |
|
827
|
22
|
|
|
|
|
125
|
my $quoted_primary_key_name = $dbh->quote_identifier( $primary_key_name ); |
|
828
|
22
|
|
|
|
|
732
|
my $quoted_table_name = $dbh->quote_identifier( $table_name ); |
|
829
|
|
|
|
|
|
|
|
|
830
|
|
|
|
|
|
|
# Prepare the SQL request elements. |
|
831
|
22
|
100
|
|
|
|
658
|
my $where = scalar( @$where_clauses ) != 0 |
|
832
|
|
|
|
|
|
|
? 'WHERE ( ' . join( ' ) AND ( ', @$where_clauses ) . ' )' |
|
833
|
|
|
|
|
|
|
: ''; |
|
834
|
22
|
|
100
|
|
|
300
|
my $joins = $args{'query_extensions'}->{'joins'} || ''; |
|
835
|
22
|
50
|
33
|
|
|
160
|
my $limit = defined( $args{'limit'} ) && ( $args{'limit'} =~ m/^\d+$/ ) |
|
836
|
|
|
|
|
|
|
? 'LIMIT ' . $args{'limit'} |
|
837
|
|
|
|
|
|
|
: ''; |
|
838
|
|
|
|
|
|
|
|
|
839
|
|
|
|
|
|
|
# Prepare the list of fields to retrieve. |
|
840
|
22
|
|
|
|
|
47
|
my $fields; |
|
841
|
22
|
50
|
33
|
|
|
425
|
if ( defined( $args{'exclude_fields'} ) || defined( $args{'select_fields'} ) ) |
|
842
|
|
|
|
|
|
|
{ |
|
843
|
0
|
|
|
|
|
0
|
my $table_schema = $class->get_table_schema(); |
|
844
|
0
|
0
|
|
|
|
0
|
croak "Failed to retrieve schema for table '$table_name'" |
|
845
|
|
|
|
|
|
|
if !defined( $table_schema ); |
|
846
|
0
|
|
|
|
|
0
|
my $column_names = $table_schema->get_column_names(); |
|
847
|
0
|
0
|
|
|
|
0
|
croak "Failed to retrieve column names for table '$table_name'" |
|
848
|
|
|
|
|
|
|
if !defined( $column_names ); |
|
849
|
|
|
|
|
|
|
|
|
850
|
0
|
|
|
|
|
0
|
my @filtered_fields = (); |
|
851
|
0
|
0
|
0
|
|
|
0
|
if ( defined( $args{'exclude_fields'} ) && !defined( $args{'select_fields'} ) ) |
|
|
|
0
|
0
|
|
|
|
|
|
852
|
|
|
|
|
|
|
{ |
|
853
|
0
|
|
|
|
|
0
|
my %excluded_fields = map { $_ => 1 } @{ $args{'exclude_fields'} }; |
|
|
0
|
|
|
|
|
0
|
|
|
|
0
|
|
|
|
|
0
|
|
|
854
|
0
|
|
|
|
|
0
|
foreach my $field ( @$column_names ) |
|
855
|
|
|
|
|
|
|
{ |
|
856
|
0
|
0
|
|
|
|
0
|
$excluded_fields{ $field } |
|
857
|
|
|
|
|
|
|
? delete( $excluded_fields{ $field } ) |
|
858
|
|
|
|
|
|
|
: push( @filtered_fields, $field ); |
|
859
|
|
|
|
|
|
|
} |
|
860
|
0
|
0
|
|
|
|
0
|
croak "The following excluded fields are not valid: " . join( ', ', keys %excluded_fields ) |
|
861
|
|
|
|
|
|
|
if scalar( keys %excluded_fields ) != 0; |
|
862
|
|
|
|
|
|
|
} |
|
863
|
|
|
|
|
|
|
elsif ( !defined( $args{'exclude_fields'} ) && defined( $args{'select_fields'} ) ) |
|
864
|
|
|
|
|
|
|
{ |
|
865
|
0
|
|
|
|
|
0
|
my %selected_fields = map { $_ => 1 } @{ $args{'select_fields'} }; |
|
|
0
|
|
|
|
|
0
|
|
|
|
0
|
|
|
|
|
0
|
|
|
866
|
0
|
0
|
0
|
|
|
0
|
croak 'The primary key must be in the list of selected fields' |
|
867
|
|
|
|
|
|
|
if defined( $primary_key_name ) && !$selected_fields{ $primary_key_name }; |
|
868
|
|
|
|
|
|
|
|
|
869
|
0
|
|
|
|
|
0
|
foreach my $field ( @$column_names ) |
|
870
|
|
|
|
|
|
|
{ |
|
871
|
0
|
0
|
|
|
|
0
|
next if !$selected_fields{ $field }; |
|
872
|
0
|
|
|
|
|
0
|
push( @filtered_fields, $field ); |
|
873
|
0
|
|
|
|
|
0
|
delete( $selected_fields{ $field } ); |
|
874
|
|
|
|
|
|
|
} |
|
875
|
|
|
|
|
|
|
|
|
876
|
0
|
0
|
|
|
|
0
|
croak "The following restricted fields are not valid: " . join( ', ', keys %selected_fields ) |
|
877
|
|
|
|
|
|
|
if scalar( keys %selected_fields ) != 0; |
|
878
|
|
|
|
|
|
|
} |
|
879
|
|
|
|
|
|
|
else |
|
880
|
|
|
|
|
|
|
{ |
|
881
|
0
|
|
|
|
|
0
|
croak "The 'exclude_fields' and 'select_fields' options are not compatible, use one or the other"; |
|
882
|
|
|
|
|
|
|
} |
|
883
|
|
|
|
|
|
|
|
|
884
|
0
|
0
|
|
|
|
0
|
croak "No fields left after filtering out the excluded/restricted fields" |
|
885
|
|
|
|
|
|
|
if scalar( @filtered_fields ) == 0; |
|
886
|
|
|
|
|
|
|
|
|
887
|
0
|
|
|
|
|
0
|
$fields = join( |
|
888
|
|
|
|
|
|
|
', ', |
|
889
|
0
|
|
|
|
|
0
|
map { "$quoted_table_name.$_" } @filtered_fields |
|
890
|
|
|
|
|
|
|
); |
|
891
|
|
|
|
|
|
|
} |
|
892
|
|
|
|
|
|
|
else |
|
893
|
|
|
|
|
|
|
{ |
|
894
|
22
|
|
|
|
|
66
|
$fields = $quoted_table_name . '.*'; |
|
895
|
|
|
|
|
|
|
} |
|
896
|
|
|
|
|
|
|
|
|
897
|
22
|
100
|
|
|
|
145
|
$fields .= ', ' . $args{'query_extensions'}->{'joined_fields'} |
|
898
|
|
|
|
|
|
|
if defined( $args{'query_extensions'}->{'joined_fields'} ); |
|
899
|
|
|
|
|
|
|
|
|
900
|
|
|
|
|
|
|
# We need to make an exception for lock=1 when using SQLite, since |
|
901
|
|
|
|
|
|
|
# SQLite doesn't support FOR UPDATE. |
|
902
|
|
|
|
|
|
|
# Per http://sqlite.org/cvstrac/wiki?p=UnsupportedSql, the entire |
|
903
|
|
|
|
|
|
|
# database is locked when updating any bit of it, so we can simply |
|
904
|
|
|
|
|
|
|
# ignore the locking request here. |
|
905
|
22
|
|
|
|
|
51
|
my $lock = ''; |
|
906
|
22
|
100
|
|
|
|
117
|
if ( $args{'lock'} ) |
|
907
|
|
|
|
|
|
|
{ |
|
908
|
1
|
|
50
|
|
|
22
|
my $database_type = $dbh->{'Driver'}->{'Name'} || ''; |
|
909
|
1
|
50
|
|
|
|
5
|
if ( $database_type eq 'SQLite' ) |
|
910
|
|
|
|
|
|
|
{ |
|
911
|
1
|
|
|
|
|
7
|
$log->info( |
|
912
|
|
|
|
|
|
|
'SQLite does not support locking since only one process at a time is ', |
|
913
|
|
|
|
|
|
|
'allowed to update a given SQLite database, so lock=1 is ignored.', |
|
914
|
|
|
|
|
|
|
); |
|
915
|
|
|
|
|
|
|
} |
|
916
|
|
|
|
|
|
|
else |
|
917
|
|
|
|
|
|
|
{ |
|
918
|
0
|
|
|
|
|
0
|
$lock = 'FOR UPDATE'; |
|
919
|
|
|
|
|
|
|
} |
|
920
|
|
|
|
|
|
|
} |
|
921
|
|
|
|
|
|
|
|
|
922
|
|
|
|
|
|
|
# Check if we need to paginate. |
|
923
|
22
|
|
|
|
|
275
|
my $pagination_info = {}; |
|
924
|
22
|
100
|
|
|
|
118
|
if ( defined( $args{'pagination'} ) ) |
|
925
|
|
|
|
|
|
|
{ |
|
926
|
|
|
|
|
|
|
# Allow for pagination => 1 as a shortcut to get all the defaults. |
|
927
|
5
|
100
|
66
|
|
|
19
|
$args{'pagination'} = {} |
|
928
|
|
|
|
|
|
|
if !Data::Validate::Type::is_hashref( $args{'pagination'} ) && ( $args{'pagination'} eq '1' ); |
|
929
|
|
|
|
|
|
|
|
|
930
|
|
|
|
|
|
|
# Set defaults. |
|
931
|
5
|
100
|
100
|
|
|
117
|
$pagination_info->{'per_page'} = ( $args{'pagination'}->{'per_page'} || '' ) =~ m/^\d+$/ |
|
932
|
|
|
|
|
|
|
? $args{'pagination'}->{'per_page'} |
|
933
|
|
|
|
|
|
|
: 20; |
|
934
|
|
|
|
|
|
|
|
|
935
|
|
|
|
|
|
|
# Count the total number of results. |
|
936
|
5
|
|
|
|
|
99
|
my $count_data = $dbh->selectrow_arrayref( |
|
937
|
|
|
|
|
|
|
sprintf( |
|
938
|
|
|
|
|
|
|
q| |
|
939
|
|
|
|
|
|
|
SELECT COUNT(*) |
|
940
|
|
|
|
|
|
|
FROM %s |
|
941
|
|
|
|
|
|
|
%s |
|
942
|
|
|
|
|
|
|
%s |
|
943
|
|
|
|
|
|
|
|, |
|
944
|
|
|
|
|
|
|
$quoted_table_name, |
|
945
|
|
|
|
|
|
|
$joins, |
|
946
|
|
|
|
|
|
|
$where, |
|
947
|
|
|
|
|
|
|
), |
|
948
|
|
|
|
|
|
|
{}, |
|
949
|
5
|
|
|
|
|
37
|
map { @$_ } @$where_values, |
|
950
|
|
|
|
|
|
|
); |
|
951
|
5
|
50
|
33
|
|
|
2005
|
$pagination_info->{'total_count'} = defined( $count_data ) && scalar( @$count_data ) != 0 |
|
952
|
|
|
|
|
|
|
? $count_data->[0] |
|
953
|
|
|
|
|
|
|
: undef; |
|
954
|
|
|
|
|
|
|
|
|
955
|
|
|
|
|
|
|
# Calculate what the max page can be. |
|
956
|
5
|
|
|
|
|
31
|
$pagination_info->{'page_max'} = int( ( $pagination_info->{'total_count'} - 1 ) / $pagination_info->{'per_page'} ) + 1; |
|
957
|
|
|
|
|
|
|
|
|
958
|
|
|
|
|
|
|
# Determine what the current page is. |
|
959
|
5
|
50
|
66
|
|
|
57
|
$pagination_info->{'page'} = ( ( $args{'pagination'}->{'page'} || '' ) =~ m/^\d+$/ ) && ( $args{'pagination'}->{'page'} > 0 ) |
|
|
|
100
|
|
|
|
|
|
|
960
|
|
|
|
|
|
|
? $pagination_info->{'page_max'} < $args{'pagination'}->{'page'} |
|
961
|
|
|
|
|
|
|
? $pagination_info->{'page_max'} |
|
962
|
|
|
|
|
|
|
: $args{'pagination'}->{'page'} |
|
963
|
|
|
|
|
|
|
: 1; |
|
964
|
|
|
|
|
|
|
|
|
965
|
|
|
|
|
|
|
# Set LIMIT and OFFSET. |
|
966
|
5
|
|
|
|
|
31
|
$limit = "LIMIT $pagination_info->{'per_page'} " |
|
967
|
|
|
|
|
|
|
. 'OFFSET ' . ( ( $pagination_info->{'page'} - 1 ) * $pagination_info->{'per_page'} ); |
|
968
|
|
|
|
|
|
|
} |
|
969
|
|
|
|
|
|
|
|
|
970
|
|
|
|
|
|
|
# If we need to lock the rows and there's joins, let's do this in two steps: |
|
971
|
|
|
|
|
|
|
# 1) Lock the rows without join. |
|
972
|
|
|
|
|
|
|
# 2) Using the IDs found, do another select to retrieve the data with the joins. |
|
973
|
22
|
50
|
33
|
|
|
100
|
if ( ( $lock ne '' ) && ( $joins ne '' ) ) |
|
974
|
|
|
|
|
|
|
{ |
|
975
|
0
|
|
|
|
|
0
|
my $query = sprintf( |
|
976
|
|
|
|
|
|
|
q| |
|
977
|
|
|
|
|
|
|
SELECT %s |
|
978
|
|
|
|
|
|
|
FROM %s |
|
979
|
|
|
|
|
|
|
%s |
|
980
|
|
|
|
|
|
|
ORDER BY %s ASC |
|
981
|
|
|
|
|
|
|
%s |
|
982
|
|
|
|
|
|
|
%s |
|
983
|
|
|
|
|
|
|
|, |
|
984
|
|
|
|
|
|
|
$quoted_primary_key_name, |
|
985
|
|
|
|
|
|
|
$quoted_table_name, |
|
986
|
|
|
|
|
|
|
$where, |
|
987
|
|
|
|
|
|
|
$quoted_primary_key_name, |
|
988
|
|
|
|
|
|
|
$limit, |
|
989
|
|
|
|
|
|
|
$lock, |
|
990
|
|
|
|
|
|
|
); |
|
991
|
|
|
|
|
|
|
|
|
992
|
0
|
|
|
|
|
0
|
my @query_values = map { @$_ } @$where_values; |
|
|
0
|
|
|
|
|
0
|
|
|
993
|
0
|
0
|
|
|
|
0
|
$log->debugf( |
|
994
|
|
|
|
|
|
|
"Performing pre-locking query:\n%s\nValues:\n%s", |
|
995
|
|
|
|
|
|
|
$query, |
|
996
|
|
|
|
|
|
|
\@query_values, |
|
997
|
|
|
|
|
|
|
) if $args{'show_queries'}; |
|
998
|
|
|
|
|
|
|
|
|
999
|
0
|
|
|
|
|
0
|
my $locked_ids; |
|
1000
|
|
|
|
|
|
|
try |
|
1001
|
|
|
|
|
|
|
{ |
|
1002
|
0
|
|
|
0
|
|
0
|
local $dbh->{'RaiseError'} = 1; |
|
1003
|
0
|
|
|
|
|
0
|
$locked_ids = $dbh->selectall_arrayref( |
|
1004
|
|
|
|
|
|
|
$query, |
|
1005
|
|
|
|
|
|
|
{ |
|
1006
|
|
|
|
|
|
|
Columns => [ 1 ], |
|
1007
|
|
|
|
|
|
|
}, |
|
1008
|
|
|
|
|
|
|
@query_values |
|
1009
|
|
|
|
|
|
|
); |
|
1010
|
|
|
|
|
|
|
} |
|
1011
|
|
|
|
|
|
|
catch |
|
1012
|
|
|
|
|
|
|
{ |
|
1013
|
0
|
|
|
0
|
|
0
|
$log->fatalf( |
|
1014
|
|
|
|
|
|
|
"Could not select rows in pre-locking query: %s\nQuery: %s\nValues:\n%s", |
|
1015
|
|
|
|
|
|
|
$_, |
|
1016
|
|
|
|
|
|
|
$query, |
|
1017
|
|
|
|
|
|
|
\@query_values, |
|
1018
|
|
|
|
|
|
|
); |
|
1019
|
0
|
|
|
|
|
0
|
croak "Failed select: $_"; |
|
1020
|
0
|
|
|
|
|
0
|
}; |
|
1021
|
|
|
|
|
|
|
|
|
1022
|
0
|
0
|
0
|
|
|
0
|
if ( !defined( $locked_ids ) || ( scalar( @$locked_ids ) == 0 ) ) |
|
1023
|
|
|
|
|
|
|
{ |
|
1024
|
0
|
|
|
|
|
0
|
return []; |
|
1025
|
|
|
|
|
|
|
} |
|
1026
|
|
|
|
|
|
|
|
|
1027
|
0
|
|
|
|
|
0
|
$where = sprintf( |
|
1028
|
|
|
|
|
|
|
'WHERE %s.%s IN ( %s )', |
|
1029
|
|
|
|
|
|
|
$quoted_table_name, |
|
1030
|
|
|
|
|
|
|
$quoted_primary_key_name, |
|
1031
|
|
|
|
|
|
|
join( ', ', ( ('?') x scalar( @$locked_ids ) ) ), |
|
1032
|
|
|
|
|
|
|
); |
|
1033
|
0
|
|
|
|
|
0
|
$where_values = [ [ map { $_->[0] } @$locked_ids ] ]; |
|
|
0
|
|
|
|
|
0
|
|
|
1034
|
0
|
|
|
|
|
0
|
$lock = ''; |
|
1035
|
|
|
|
|
|
|
} |
|
1036
|
|
|
|
|
|
|
|
|
1037
|
|
|
|
|
|
|
# Prepare the query elements. |
|
1038
|
22
|
|
|
|
|
213
|
my $query = sprintf( |
|
1039
|
|
|
|
|
|
|
q| |
|
1040
|
|
|
|
|
|
|
SELECT %s |
|
1041
|
|
|
|
|
|
|
FROM %s |
|
1042
|
|
|
|
|
|
|
%s %s %s %s %s |
|
1043
|
|
|
|
|
|
|
|, |
|
1044
|
|
|
|
|
|
|
$fields, |
|
1045
|
|
|
|
|
|
|
$quoted_table_name, |
|
1046
|
|
|
|
|
|
|
$joins, |
|
1047
|
|
|
|
|
|
|
$where, |
|
1048
|
|
|
|
|
|
|
$order_by, |
|
1049
|
|
|
|
|
|
|
$limit, |
|
1050
|
|
|
|
|
|
|
$lock, |
|
1051
|
|
|
|
|
|
|
); |
|
1052
|
22
|
|
|
|
|
68
|
my @query_values = map { @$_ } @$where_values; |
|
|
20
|
|
|
|
|
85
|
|
|
1053
|
22
|
50
|
|
|
|
1586
|
$log->debugf( |
|
1054
|
|
|
|
|
|
|
"Performing query:\n%s\nValues:\n%s", |
|
1055
|
|
|
|
|
|
|
$query, |
|
1056
|
|
|
|
|
|
|
\@query_values, |
|
1057
|
|
|
|
|
|
|
) if $args{'show_queries'}; |
|
1058
|
|
|
|
|
|
|
|
|
1059
|
|
|
|
|
|
|
# Retrieve the objects. |
|
1060
|
22
|
|
|
|
|
46
|
my $sth; |
|
1061
|
|
|
|
|
|
|
try |
|
1062
|
|
|
|
|
|
|
{ |
|
1063
|
22
|
|
|
22
|
|
2299
|
local $dbh->{'RaiseError'} = 1; |
|
1064
|
22
|
|
|
|
|
232
|
$sth = $dbh->prepare( $query ); |
|
1065
|
22
|
|
|
|
|
25249
|
$sth->execute( @query_values ); |
|
1066
|
|
|
|
|
|
|
} |
|
1067
|
|
|
|
|
|
|
catch |
|
1068
|
|
|
|
|
|
|
{ |
|
1069
|
0
|
|
|
0
|
|
0
|
$log->fatalf( |
|
1070
|
|
|
|
|
|
|
"Could not select rows: %s\nQuery: %s\nValues: %s", |
|
1071
|
|
|
|
|
|
|
$_, |
|
1072
|
|
|
|
|
|
|
$query, |
|
1073
|
|
|
|
|
|
|
\@query_values, |
|
1074
|
|
|
|
|
|
|
); |
|
1075
|
0
|
|
|
|
|
0
|
croak "Failed select: $_"; |
|
1076
|
22
|
|
|
|
|
324
|
}; |
|
1077
|
|
|
|
|
|
|
|
|
1078
|
22
|
|
|
|
|
800
|
my $object_list = []; |
|
1079
|
22
|
|
|
|
|
2607
|
while ( my $ref = $sth->fetchrow_hashref() ) |
|
1080
|
|
|
|
|
|
|
{ |
|
1081
|
84
|
|
|
|
|
4095
|
my $object = Storable::dclone( $ref ); |
|
1082
|
84
|
|
|
|
|
276
|
bless( $object, $class ); |
|
1083
|
|
|
|
|
|
|
|
|
1084
|
84
|
|
|
|
|
347
|
$object->reorganize_non_native_fields(); |
|
1085
|
|
|
|
|
|
|
|
|
1086
|
|
|
|
|
|
|
# Add a flag to distinguish objects that were populated via |
|
1087
|
|
|
|
|
|
|
# retrieve_list_nocache(), as those objects are known for sure to contain |
|
1088
|
|
|
|
|
|
|
# all the keys for columns that exist in the database. We also won't have to |
|
1089
|
|
|
|
|
|
|
# worry about missing defaults, like insert() would have to. |
|
1090
|
84
|
|
|
|
|
156
|
$object->{'_populated_by_retrieve_list'} = 1; |
|
1091
|
|
|
|
|
|
|
|
|
1092
|
|
|
|
|
|
|
# Add cache debugging information. |
|
1093
|
84
|
|
|
|
|
255
|
$object->{'_debug'}->{'list_cache_used'} = 0; |
|
1094
|
84
|
|
|
|
|
167
|
$object->{'_debug'}->{'object_cache_used'} = 0; |
|
1095
|
|
|
|
|
|
|
|
|
1096
|
|
|
|
|
|
|
# Store if we've excluded any fields, as it will impact caching in |
|
1097
|
|
|
|
|
|
|
# retrieve_list(). |
|
1098
|
84
|
50
|
|
|
|
414
|
$object->{'_excluded_fields'} = $args{'exclude_fields'} |
|
1099
|
|
|
|
|
|
|
if defined( $args{'exclude_fields'} ); |
|
1100
|
|
|
|
|
|
|
|
|
1101
|
|
|
|
|
|
|
# Store if we've restricted to any fields, as it will impact caching in |
|
1102
|
|
|
|
|
|
|
# retrieve_list(). |
|
1103
|
84
|
50
|
|
|
|
260
|
$object->{'_selected_fields'} = $args{'select_fields'} |
|
1104
|
|
|
|
|
|
|
if defined( $args{'select_fields'} ); |
|
1105
|
|
|
|
|
|
|
|
|
1106
|
84
|
|
|
|
|
1905
|
push( @$object_list, $object ); |
|
1107
|
|
|
|
|
|
|
} |
|
1108
|
|
|
|
|
|
|
|
|
1109
|
22
|
100
|
66
|
|
|
269
|
if ( wantarray && defined( $args{'pagination'} ) ) |
|
1110
|
|
|
|
|
|
|
{ |
|
1111
|
5
|
|
|
|
|
154
|
return ( $object_list, $pagination_info ); |
|
1112
|
|
|
|
|
|
|
} |
|
1113
|
|
|
|
|
|
|
else |
|
1114
|
|
|
|
|
|
|
{ |
|
1115
|
17
|
|
|
|
|
680
|
return $object_list; |
|
1116
|
|
|
|
|
|
|
} |
|
1117
|
|
|
|
|
|
|
} |
|
1118
|
|
|
|
|
|
|
|
|
1119
|
|
|
|
|
|
|
|
|
1120
|
|
|
|
|
|
|
=head2 set() |
|
1121
|
|
|
|
|
|
|
|
|
1122
|
|
|
|
|
|
|
Set fields and values on an object. |
|
1123
|
|
|
|
|
|
|
|
|
1124
|
|
|
|
|
|
|
$book->set( |
|
1125
|
|
|
|
|
|
|
{ |
|
1126
|
|
|
|
|
|
|
name => 'Learning Perl', |
|
1127
|
|
|
|
|
|
|
isbn => '9781449303587', |
|
1128
|
|
|
|
|
|
|
}, |
|
1129
|
|
|
|
|
|
|
); |
|
1130
|
|
|
|
|
|
|
|
|
1131
|
|
|
|
|
|
|
This method supports the following arguments: |
|
1132
|
|
|
|
|
|
|
|
|
1133
|
|
|
|
|
|
|
=over 4 |
|
1134
|
|
|
|
|
|
|
|
|
1135
|
|
|
|
|
|
|
=item * force |
|
1136
|
|
|
|
|
|
|
|
|
1137
|
|
|
|
|
|
|
Set the properties on the object without going through C. |
|
1138
|
|
|
|
|
|
|
|
|
1139
|
|
|
|
|
|
|
$book->set( |
|
1140
|
|
|
|
|
|
|
{ |
|
1141
|
|
|
|
|
|
|
name => 'Learning Perl', |
|
1142
|
|
|
|
|
|
|
isbn => '9781449303587', |
|
1143
|
|
|
|
|
|
|
}, |
|
1144
|
|
|
|
|
|
|
force => 1, |
|
1145
|
|
|
|
|
|
|
); |
|
1146
|
|
|
|
|
|
|
|
|
1147
|
|
|
|
|
|
|
=back |
|
1148
|
|
|
|
|
|
|
|
|
1149
|
|
|
|
|
|
|
=cut |
|
1150
|
|
|
|
|
|
|
|
|
1151
|
|
|
|
|
|
|
sub set ## no critic (NamingConventions::ProhibitAmbiguousNames, Subroutines::RequireArgUnpacking) |
|
1152
|
|
|
|
|
|
|
{ |
|
1153
|
67
|
100
|
|
67
|
1
|
4609
|
croak 'The first argument passed must be a hashref' |
|
1154
|
|
|
|
|
|
|
if !Data::Validate::Type::is_hashref( $_[1] ); |
|
1155
|
|
|
|
|
|
|
|
|
1156
|
66
|
|
|
|
|
2900
|
my ( $self, $data, %args ) = @_; |
|
1157
|
|
|
|
|
|
|
|
|
1158
|
|
|
|
|
|
|
# Validate the data first, unless force=1. |
|
1159
|
66
|
100
|
|
|
|
1118
|
$data = $self->validate_data( $data ) |
|
1160
|
|
|
|
|
|
|
if !$args{'force'}; |
|
1161
|
|
|
|
|
|
|
|
|
1162
|
|
|
|
|
|
|
# Update the object. |
|
1163
|
64
|
|
|
|
|
850
|
foreach ( keys %$data ) |
|
1164
|
|
|
|
|
|
|
{ |
|
1165
|
236
|
|
|
|
|
886
|
$self->{ $_ } = $data->{ $_ }; |
|
1166
|
|
|
|
|
|
|
} |
|
1167
|
|
|
|
|
|
|
|
|
1168
|
64
|
|
|
|
|
456
|
return; |
|
1169
|
|
|
|
|
|
|
} |
|
1170
|
|
|
|
|
|
|
|
|
1171
|
|
|
|
|
|
|
|
|
1172
|
|
|
|
|
|
|
=head2 static_class_info() |
|
1173
|
|
|
|
|
|
|
|
|
1174
|
|
|
|
|
|
|
This methods sets defaults as well as general information for a specific class. |
|
1175
|
|
|
|
|
|
|
|
|
1176
|
|
|
|
|
|
|
It allows for example indicating what table the objects will be related to, or |
|
1177
|
|
|
|
|
|
|
what database handle to use. See L for the |
|
1178
|
|
|
|
|
|
|
full list of options that can be set or overridden. |
|
1179
|
|
|
|
|
|
|
|
|
1180
|
|
|
|
|
|
|
Here's what a typical subclassed C would look like: |
|
1181
|
|
|
|
|
|
|
|
|
1182
|
|
|
|
|
|
|
sub static_class_info |
|
1183
|
|
|
|
|
|
|
{ |
|
1184
|
|
|
|
|
|
|
my ( $class ) = @_; |
|
1185
|
|
|
|
|
|
|
|
|
1186
|
|
|
|
|
|
|
# Retrieve defaults coming from higher in the inheritance chain, up |
|
1187
|
|
|
|
|
|
|
# to DBIx::NinjaORM->static_class_info(). |
|
1188
|
|
|
|
|
|
|
my $info = $class->SUPER::static_class_info(); |
|
1189
|
|
|
|
|
|
|
|
|
1190
|
|
|
|
|
|
|
# Set or override information. |
|
1191
|
|
|
|
|
|
|
$info->set( |
|
1192
|
|
|
|
|
|
|
{ |
|
1193
|
|
|
|
|
|
|
table_name => 'books', |
|
1194
|
|
|
|
|
|
|
primary_key_name => 'book_id', |
|
1195
|
|
|
|
|
|
|
default_dbh => DBI->connect( |
|
1196
|
|
|
|
|
|
|
"dbi:mysql:[database_name]:localhost:3306", |
|
1197
|
|
|
|
|
|
|
"[user]", |
|
1198
|
|
|
|
|
|
|
"[password]", |
|
1199
|
|
|
|
|
|
|
), |
|
1200
|
|
|
|
|
|
|
} |
|
1201
|
|
|
|
|
|
|
); |
|
1202
|
|
|
|
|
|
|
|
|
1203
|
|
|
|
|
|
|
# Return the updated information hashref. |
|
1204
|
|
|
|
|
|
|
return $info; |
|
1205
|
|
|
|
|
|
|
} |
|
1206
|
|
|
|
|
|
|
|
|
1207
|
|
|
|
|
|
|
=cut |
|
1208
|
|
|
|
|
|
|
|
|
1209
|
|
|
|
|
|
|
sub static_class_info |
|
1210
|
|
|
|
|
|
|
{ |
|
1211
|
79
|
|
|
79
|
1
|
2904
|
return DBIx::NinjaORM::StaticClassInfo->new(); |
|
1212
|
|
|
|
|
|
|
} |
|
1213
|
|
|
|
|
|
|
|
|
1214
|
|
|
|
|
|
|
|
|
1215
|
|
|
|
|
|
|
=head2 update() |
|
1216
|
|
|
|
|
|
|
|
|
1217
|
|
|
|
|
|
|
Update the row in the database corresponding to the current object, using the |
|
1218
|
|
|
|
|
|
|
primary key and its value on the object. |
|
1219
|
|
|
|
|
|
|
|
|
1220
|
|
|
|
|
|
|
$book->update( |
|
1221
|
|
|
|
|
|
|
{ |
|
1222
|
|
|
|
|
|
|
name => 'Learning Perl', |
|
1223
|
|
|
|
|
|
|
} |
|
1224
|
|
|
|
|
|
|
); |
|
1225
|
|
|
|
|
|
|
|
|
1226
|
|
|
|
|
|
|
This method supports the following optional arguments: |
|
1227
|
|
|
|
|
|
|
|
|
1228
|
|
|
|
|
|
|
=over 4 |
|
1229
|
|
|
|
|
|
|
|
|
1230
|
|
|
|
|
|
|
=item * skip_modified_update (default 0) |
|
1231
|
|
|
|
|
|
|
|
|
1232
|
|
|
|
|
|
|
Do not update the 'modified' field. This is useful if you're using 'modified' to |
|
1233
|
|
|
|
|
|
|
record when was the last time a human changed the row, but you want to exclude |
|
1234
|
|
|
|
|
|
|
automated changes. |
|
1235
|
|
|
|
|
|
|
|
|
1236
|
|
|
|
|
|
|
=item * dbh |
|
1237
|
|
|
|
|
|
|
|
|
1238
|
|
|
|
|
|
|
A different database handle than the default one specified in |
|
1239
|
|
|
|
|
|
|
C, but it has to be writable. |
|
1240
|
|
|
|
|
|
|
|
|
1241
|
|
|
|
|
|
|
=item * restrictions |
|
1242
|
|
|
|
|
|
|
|
|
1243
|
|
|
|
|
|
|
The update statement is limited using the primary key. This parameter however |
|
1244
|
|
|
|
|
|
|
allows adding extra restrictions on the update. Additional clauses passed here |
|
1245
|
|
|
|
|
|
|
are joined with AND. |
|
1246
|
|
|
|
|
|
|
|
|
1247
|
|
|
|
|
|
|
$book->update( |
|
1248
|
|
|
|
|
|
|
{ |
|
1249
|
|
|
|
|
|
|
author_id => 1234, |
|
1250
|
|
|
|
|
|
|
}, |
|
1251
|
|
|
|
|
|
|
restrictions => |
|
1252
|
|
|
|
|
|
|
{ |
|
1253
|
|
|
|
|
|
|
where_clauses => [ 'status != ?' ], |
|
1254
|
|
|
|
|
|
|
where_values => [ 'protected' ], |
|
1255
|
|
|
|
|
|
|
}, |
|
1256
|
|
|
|
|
|
|
); |
|
1257
|
|
|
|
|
|
|
|
|
1258
|
|
|
|
|
|
|
=item * set |
|
1259
|
|
|
|
|
|
|
|
|
1260
|
|
|
|
|
|
|
\%data contains the data to update the row with "SET field = value". It is |
|
1261
|
|
|
|
|
|
|
however sometimes necessary to use more complex SETs, such as |
|
1262
|
|
|
|
|
|
|
"SET field = field + value", which is what this parameter allows. |
|
1263
|
|
|
|
|
|
|
|
|
1264
|
|
|
|
|
|
|
Important: you will need to subclass C in your model classes and |
|
1265
|
|
|
|
|
|
|
update manually the values upon success (or reload the object), as |
|
1266
|
|
|
|
|
|
|
L cannot determine the end result of those complex sets on the |
|
1267
|
|
|
|
|
|
|
database side. |
|
1268
|
|
|
|
|
|
|
|
|
1269
|
|
|
|
|
|
|
$book->update( |
|
1270
|
|
|
|
|
|
|
{ |
|
1271
|
|
|
|
|
|
|
name => 'Learning Perl', |
|
1272
|
|
|
|
|
|
|
}, |
|
1273
|
|
|
|
|
|
|
set => |
|
1274
|
|
|
|
|
|
|
{ |
|
1275
|
|
|
|
|
|
|
placeholders => [ 'edits = edits + ?' ], |
|
1276
|
|
|
|
|
|
|
values => [ 1 ], |
|
1277
|
|
|
|
|
|
|
} |
|
1278
|
|
|
|
|
|
|
); |
|
1279
|
|
|
|
|
|
|
|
|
1280
|
|
|
|
|
|
|
=back |
|
1281
|
|
|
|
|
|
|
|
|
1282
|
|
|
|
|
|
|
=cut |
|
1283
|
|
|
|
|
|
|
|
|
1284
|
|
|
|
|
|
|
sub update ## no critic (Subroutines::RequireArgUnpacking) |
|
1285
|
|
|
|
|
|
|
{ |
|
1286
|
9
|
100
|
|
9
|
1
|
17243
|
croak 'The first argument passed must be a hashref' |
|
1287
|
|
|
|
|
|
|
if !Data::Validate::Type::is_hashref( $_[1] ); |
|
1288
|
|
|
|
|
|
|
|
|
1289
|
8
|
|
|
|
|
188
|
my ( $self, $data, %args ) = @_; |
|
1290
|
|
|
|
|
|
|
|
|
1291
|
|
|
|
|
|
|
# Allow using a different DB handle. |
|
1292
|
8
|
|
|
|
|
78
|
my $dbh = $self->assert_dbh( $args{'dbh'} ); |
|
1293
|
|
|
|
|
|
|
|
|
1294
|
|
|
|
|
|
|
# Clean input |
|
1295
|
6
|
|
|
|
|
57
|
my $clean_data = $self->validate_data( $data, %args ); |
|
1296
|
6
|
50
|
|
|
|
35
|
return 0 |
|
1297
|
|
|
|
|
|
|
if !defined( $clean_data ); |
|
1298
|
|
|
|
|
|
|
|
|
1299
|
|
|
|
|
|
|
# Set defaults |
|
1300
|
6
|
100
|
66
|
|
|
109
|
$clean_data->{'modified'} = $self->get_current_time() |
|
1301
|
|
|
|
|
|
|
if !$args{'skip_modified_update'} && $self->get_info('has_modified_field'); |
|
1302
|
|
|
|
|
|
|
|
|
1303
|
|
|
|
|
|
|
# If there's nothing to update, bail out. |
|
1304
|
6
|
50
|
|
|
|
106
|
if ( scalar( keys %$clean_data ) == 0 ) |
|
1305
|
|
|
|
|
|
|
{ |
|
1306
|
0
|
0
|
|
|
|
0
|
$log->debug( 'No data left to update after validation, skipping SQL update' ) |
|
1307
|
|
|
|
|
|
|
if $self->is_verbose(); |
|
1308
|
0
|
|
|
|
|
0
|
return; |
|
1309
|
|
|
|
|
|
|
} |
|
1310
|
|
|
|
|
|
|
|
|
1311
|
|
|
|
|
|
|
# Retrieve the meta-data for that table. |
|
1312
|
6
|
|
|
|
|
109
|
my $class = ref( $self ); |
|
1313
|
|
|
|
|
|
|
|
|
1314
|
6
|
|
|
|
|
26
|
my $table_name = $self->get_info('table_name'); |
|
1315
|
6
|
50
|
|
|
|
33
|
croak "The table name for class '$class' is not defined" |
|
1316
|
|
|
|
|
|
|
if ! defined( $table_name ); |
|
1317
|
|
|
|
|
|
|
|
|
1318
|
6
|
|
|
|
|
20
|
my $primary_key_name = $self->get_info('primary_key_name'); |
|
1319
|
6
|
50
|
33
|
|
|
40
|
croak "Missing primary key name for class '$class', cannot force primary key value" |
|
1320
|
|
|
|
|
|
|
if !defined( $primary_key_name ) && defined( $args{'generated_primary_key_value'} ); |
|
1321
|
|
|
|
|
|
|
|
|
1322
|
6
|
50
|
|
|
|
55
|
croak "The object of class '$class' does not have a primary key value, cannot update" |
|
1323
|
|
|
|
|
|
|
if ! defined( $self->id() ); |
|
1324
|
|
|
|
|
|
|
|
|
1325
|
|
|
|
|
|
|
# Prepare the SQL request elements. |
|
1326
|
6
|
|
50
|
|
|
53
|
my $where_clauses = $args{'restrictions'}->{'where_clauses'} || []; |
|
1327
|
6
|
|
50
|
|
|
46
|
my $where_values = $args{'restrictions'}->{'where_values'} || []; |
|
1328
|
6
|
|
|
|
|
27
|
push( @$where_clauses, $primary_key_name . ' = ?' ); |
|
1329
|
6
|
|
|
|
|
21
|
push( @$where_values, [ $self->id() ] ); |
|
1330
|
|
|
|
|
|
|
|
|
1331
|
|
|
|
|
|
|
# Prepare the values to set. |
|
1332
|
6
|
|
|
|
|
16
|
my @set_placeholders = (); |
|
1333
|
6
|
|
|
|
|
13
|
my @set_values = (); |
|
1334
|
6
|
|
|
|
|
23
|
foreach my $key ( keys %$clean_data ) |
|
1335
|
|
|
|
|
|
|
{ |
|
1336
|
11
|
100
|
|
|
|
143
|
if ( $key eq 'modified' ) |
|
1337
|
|
|
|
|
|
|
{ |
|
1338
|
|
|
|
|
|
|
# 'created' supports SQL keywords and is quoted by get_current_time() if |
|
1339
|
|
|
|
|
|
|
# needed, so we don't use placeholders. |
|
1340
|
5
|
|
|
|
|
56
|
push( @set_placeholders, $dbh->quote_identifier( $key ) . ' = ' . $clean_data->{ $key } ); |
|
1341
|
|
|
|
|
|
|
} |
|
1342
|
|
|
|
|
|
|
else |
|
1343
|
|
|
|
|
|
|
{ |
|
1344
|
|
|
|
|
|
|
# All the other data need to be inserted using placeholders, for |
|
1345
|
|
|
|
|
|
|
# security purposes. |
|
1346
|
6
|
|
|
|
|
58
|
push( @set_placeholders, $dbh->quote_identifier( $key ) . ' = ?' ); |
|
1347
|
6
|
|
|
|
|
507
|
push( @set_values, $clean_data->{ $key } ); |
|
1348
|
|
|
|
|
|
|
} |
|
1349
|
|
|
|
|
|
|
} |
|
1350
|
6
|
50
|
|
|
|
381
|
if ( defined( $args{'set'} ) ) |
|
1351
|
|
|
|
|
|
|
{ |
|
1352
|
0
|
|
0
|
|
|
0
|
push( @set_placeholders, @{ $args{'set'}->{'placeholders'} // [] } ); |
|
|
0
|
|
|
|
|
0
|
|
|
1353
|
0
|
|
0
|
|
|
0
|
push( @set_values, @{ $args{'set'}->{'values'} // [] } ); |
|
|
0
|
|
|
|
|
0
|
|
|
1354
|
|
|
|
|
|
|
} |
|
1355
|
|
|
|
|
|
|
|
|
1356
|
|
|
|
|
|
|
# Prepare the query elements. |
|
1357
|
6
|
|
|
|
|
33
|
my $query = sprintf( |
|
1358
|
|
|
|
|
|
|
qq| |
|
1359
|
|
|
|
|
|
|
UPDATE %s |
|
1360
|
|
|
|
|
|
|
SET %s |
|
1361
|
|
|
|
|
|
|
WHERE %s |
|
1362
|
|
|
|
|
|
|
|, |
|
1363
|
|
|
|
|
|
|
$dbh->quote_identifier( $table_name ), |
|
1364
|
|
|
|
|
|
|
join( ', ', @set_placeholders ), |
|
1365
|
|
|
|
|
|
|
'( ' . join( ' ) AND ( ', @$where_clauses ) . ' )', |
|
1366
|
|
|
|
|
|
|
); |
|
1367
|
6
|
|
|
|
|
28
|
my @query_values = |
|
1368
|
|
|
|
|
|
|
( |
|
1369
|
|
|
|
|
|
|
@set_values, |
|
1370
|
6
|
|
|
|
|
203
|
map { @$_ } @$where_values, |
|
1371
|
|
|
|
|
|
|
); |
|
1372
|
|
|
|
|
|
|
|
|
1373
|
|
|
|
|
|
|
# Update the row. |
|
1374
|
6
|
|
|
|
|
11
|
my $rows_updated_count; |
|
1375
|
|
|
|
|
|
|
try |
|
1376
|
|
|
|
|
|
|
{ |
|
1377
|
6
|
|
|
6
|
|
496
|
local $dbh->{'RaiseError'} = 1; |
|
1378
|
6
|
|
|
|
|
127
|
my $sth = $dbh->prepare( $query ); |
|
1379
|
5
|
|
|
|
|
123068
|
$sth->execute( @query_values ); |
|
1380
|
|
|
|
|
|
|
|
|
1381
|
5
|
|
|
|
|
756
|
$rows_updated_count = $sth->rows(); |
|
1382
|
|
|
|
|
|
|
} |
|
1383
|
|
|
|
|
|
|
catch |
|
1384
|
|
|
|
|
|
|
{ |
|
1385
|
1
|
|
|
1
|
|
34
|
$log->fatalf( |
|
1386
|
|
|
|
|
|
|
"Could not update rows: %s\nQuery: %s\nValues: %s", |
|
1387
|
|
|
|
|
|
|
$_, |
|
1388
|
|
|
|
|
|
|
$query, |
|
1389
|
|
|
|
|
|
|
\@query_values, |
|
1390
|
|
|
|
|
|
|
); |
|
1391
|
|
|
|
|
|
|
|
|
1392
|
1
|
|
|
|
|
28
|
croak "Update failed: $_"; |
|
1393
|
6
|
|
|
|
|
83
|
}; |
|
1394
|
|
|
|
|
|
|
|
|
1395
|
|
|
|
|
|
|
# Also, if rows() returns -1, it's an error. |
|
1396
|
5
|
50
|
|
|
|
265
|
croak 'Could not execute update: ' . $dbh->errstr() |
|
1397
|
|
|
|
|
|
|
if $rows_updated_count < 0; |
|
1398
|
|
|
|
|
|
|
|
|
1399
|
5
|
|
|
|
|
45
|
my $object_cache_time = $self->get_info('object_cache_time'); |
|
1400
|
|
|
|
|
|
|
# This needs to be before the set() below, so we invalidate the cache based on the |
|
1401
|
|
|
|
|
|
|
# old object. We don't need to do it twice, because you can't change primary IDs, and |
|
1402
|
|
|
|
|
|
|
# you can't change unique fields to ones that are taken, and that's all that we set |
|
1403
|
|
|
|
|
|
|
# the object cache keys for. |
|
1404
|
5
|
50
|
|
|
|
39
|
if ( defined( $object_cache_time ) ) |
|
1405
|
|
|
|
|
|
|
{ |
|
1406
|
0
|
0
|
|
|
|
0
|
$log->debugf( |
|
1407
|
|
|
|
|
|
|
"An update on '%s' is forcing to clear the cache for '%s=%s'", |
|
1408
|
|
|
|
|
|
|
$table_name, |
|
1409
|
|
|
|
|
|
|
$primary_key_name, |
|
1410
|
|
|
|
|
|
|
$self->id(), |
|
1411
|
|
|
|
|
|
|
) if $self->is_verbose(); |
|
1412
|
|
|
|
|
|
|
|
|
1413
|
0
|
|
|
|
|
0
|
$self->invalidate_cached_object(); |
|
1414
|
|
|
|
|
|
|
} |
|
1415
|
|
|
|
|
|
|
|
|
1416
|
|
|
|
|
|
|
# Make sure that the object reflects $clean_data. |
|
1417
|
|
|
|
|
|
|
$self->set( |
|
1418
|
5
|
|
|
|
|
59
|
$clean_data, |
|
1419
|
|
|
|
|
|
|
force => 1, |
|
1420
|
|
|
|
|
|
|
); |
|
1421
|
|
|
|
|
|
|
|
|
1422
|
5
|
|
|
|
|
663
|
return $rows_updated_count; |
|
1423
|
|
|
|
|
|
|
} |
|
1424
|
|
|
|
|
|
|
|
|
1425
|
|
|
|
|
|
|
|
|
1426
|
|
|
|
|
|
|
=head2 validate_data() |
|
1427
|
|
|
|
|
|
|
|
|
1428
|
|
|
|
|
|
|
Validate the hashref of data passed as first argument. This is used both by |
|
1429
|
|
|
|
|
|
|
C and C to check the data before performing databse |
|
1430
|
|
|
|
|
|
|
operations. |
|
1431
|
|
|
|
|
|
|
|
|
1432
|
|
|
|
|
|
|
my $validated_data = $object->validate_data( |
|
1433
|
|
|
|
|
|
|
\%data, |
|
1434
|
|
|
|
|
|
|
); |
|
1435
|
|
|
|
|
|
|
|
|
1436
|
|
|
|
|
|
|
If there is invalid data, the method will croak with a detail of the error. |
|
1437
|
|
|
|
|
|
|
|
|
1438
|
|
|
|
|
|
|
=cut |
|
1439
|
|
|
|
|
|
|
|
|
1440
|
|
|
|
|
|
|
sub validate_data |
|
1441
|
|
|
|
|
|
|
{ |
|
1442
|
71
|
|
|
71
|
1
|
2807
|
my ( $self, $original_data ) = @_; |
|
1443
|
|
|
|
|
|
|
|
|
1444
|
71
|
|
|
|
|
7186
|
my $data = Storable::dclone( $original_data ); |
|
1445
|
|
|
|
|
|
|
|
|
1446
|
|
|
|
|
|
|
# Protect read-only fields. |
|
1447
|
71
|
|
50
|
|
|
172
|
foreach my $field ( @{ $self->get_info('readonly_fields') // [] } ) |
|
|
71
|
|
|
|
|
294
|
|
|
1448
|
|
|
|
|
|
|
{ |
|
1449
|
6
|
100
|
|
|
|
25
|
next if ! exists( $data->{ $field } ); |
|
1450
|
|
|
|
|
|
|
|
|
1451
|
3
|
|
|
|
|
53
|
croak "The field '$field' is read-only and cannot be set via the model"; |
|
1452
|
|
|
|
|
|
|
} |
|
1453
|
|
|
|
|
|
|
|
|
1454
|
|
|
|
|
|
|
# Don't allow setting timestamps. |
|
1455
|
68
|
|
|
|
|
200
|
foreach my $field ( qw( created modified ) ) |
|
1456
|
|
|
|
|
|
|
{ |
|
1457
|
136
|
50
|
|
|
|
1099
|
next if ! exists( $data->{ $field } ); |
|
1458
|
|
|
|
|
|
|
|
|
1459
|
0
|
|
|
|
|
0
|
$log->warnf( |
|
1460
|
|
|
|
|
|
|
"The field '%s' cannot be set and will be ignored", |
|
1461
|
|
|
|
|
|
|
$field, |
|
1462
|
|
|
|
|
|
|
); |
|
1463
|
0
|
|
|
|
|
0
|
delete( $data->{ $field } ); |
|
1464
|
|
|
|
|
|
|
} |
|
1465
|
|
|
|
|
|
|
|
|
1466
|
|
|
|
|
|
|
# Allow inserting the primary key, but not updating it. |
|
1467
|
68
|
|
|
|
|
260
|
my $primary_key_name = $self->get_info('primary_key_name'); |
|
1468
|
68
|
100
|
100
|
|
|
967
|
if ( defined( $primary_key_name ) && defined( $self->{ $primary_key_name } ) && exists( $data->{ $primary_key_name } ) ) |
|
|
|
|
100
|
|
|
|
|
|
1469
|
|
|
|
|
|
|
{ |
|
1470
|
1
|
|
50
|
|
|
30
|
croak "'$primary_key_name' with a value of '" . ( $data->{ $primary_key_name } || 'undef' ) . "' ", |
|
1471
|
|
|
|
|
|
|
"was passed to set(), but primary keys cannot be set manually"; |
|
1472
|
|
|
|
|
|
|
} |
|
1473
|
|
|
|
|
|
|
|
|
1474
|
|
|
|
|
|
|
# Fields starting with an underscore are hidden data that shouldn't be |
|
1475
|
|
|
|
|
|
|
# modified via a public interface. |
|
1476
|
67
|
|
|
|
|
327
|
foreach my $field ( keys %$data ) |
|
1477
|
|
|
|
|
|
|
{ |
|
1478
|
77
|
100
|
|
|
|
1117
|
delete( $data->{ $field } ) |
|
1479
|
|
|
|
|
|
|
if substr( $field, 0, 1 ) eq '_'; |
|
1480
|
|
|
|
|
|
|
} |
|
1481
|
|
|
|
|
|
|
|
|
1482
|
67
|
|
|
|
|
580
|
return $data; |
|
1483
|
|
|
|
|
|
|
} |
|
1484
|
|
|
|
|
|
|
|
|
1485
|
|
|
|
|
|
|
|
|
1486
|
|
|
|
|
|
|
=head1 UTILITY METHODS |
|
1487
|
|
|
|
|
|
|
|
|
1488
|
|
|
|
|
|
|
|
|
1489
|
|
|
|
|
|
|
=head2 dump() |
|
1490
|
|
|
|
|
|
|
|
|
1491
|
|
|
|
|
|
|
Return a string representation of the current object. |
|
1492
|
|
|
|
|
|
|
|
|
1493
|
|
|
|
|
|
|
my $string = $book->dump(); |
|
1494
|
|
|
|
|
|
|
|
|
1495
|
|
|
|
|
|
|
=cut |
|
1496
|
|
|
|
|
|
|
|
|
1497
|
|
|
|
|
|
|
sub dump ## no critic (Subroutines::ProhibitBuiltinHomonyms) |
|
1498
|
|
|
|
|
|
|
{ |
|
1499
|
2
|
|
|
2
|
1
|
6043
|
my ( $self ) = @_; |
|
1500
|
|
|
|
|
|
|
|
|
1501
|
2
|
|
|
|
|
17
|
return dumper( $self ); |
|
1502
|
|
|
|
|
|
|
} |
|
1503
|
|
|
|
|
|
|
|
|
1504
|
|
|
|
|
|
|
|
|
1505
|
|
|
|
|
|
|
=head2 flatten_object() |
|
1506
|
|
|
|
|
|
|
|
|
1507
|
|
|
|
|
|
|
Return a hash with the requested key/value pairs based on the list of fields |
|
1508
|
|
|
|
|
|
|
provided. |
|
1509
|
|
|
|
|
|
|
|
|
1510
|
|
|
|
|
|
|
Note that non-native fields (starting with an underscore) are not allowed. It |
|
1511
|
|
|
|
|
|
|
also protects sensitive fields. |
|
1512
|
|
|
|
|
|
|
|
|
1513
|
|
|
|
|
|
|
#TODO: allow defining sensitive fields. |
|
1514
|
|
|
|
|
|
|
|
|
1515
|
|
|
|
|
|
|
my $book_data = $book->flatten_object( |
|
1516
|
|
|
|
|
|
|
[ 'name', 'isbn' ] |
|
1517
|
|
|
|
|
|
|
); |
|
1518
|
|
|
|
|
|
|
|
|
1519
|
|
|
|
|
|
|
=cut |
|
1520
|
|
|
|
|
|
|
|
|
1521
|
|
|
|
|
|
|
sub flatten_object |
|
1522
|
|
|
|
|
|
|
{ |
|
1523
|
5
|
|
|
5
|
1
|
8244
|
my ( $self, $fields ) = @_; |
|
1524
|
5
|
|
|
|
|
19
|
my @protected_fields = qw( password ); |
|
1525
|
|
|
|
|
|
|
|
|
1526
|
5
|
|
|
|
|
21
|
my %data = (); |
|
1527
|
5
|
|
|
|
|
13
|
foreach my $field ( @$fields ) |
|
1528
|
|
|
|
|
|
|
{ |
|
1529
|
6
|
100
|
|
|
|
13
|
if ( scalar( grep { $_ eq $field } @protected_fields ) != 0 ) |
|
|
6
|
100
|
|
|
|
53
|
|
|
|
|
100
|
|
|
|
|
|
|
1530
|
|
|
|
|
|
|
{ |
|
1531
|
1
|
|
|
|
|
28
|
croak "The fields '$field' is protected and cannot be added to the flattened copy"; |
|
1532
|
|
|
|
|
|
|
} |
|
1533
|
|
|
|
|
|
|
elsif ( substr( $field, 0, 1 ) eq '_' ) |
|
1534
|
|
|
|
|
|
|
{ |
|
1535
|
1
|
|
|
|
|
22
|
croak "The field '$field' is hidden and cannot be added to the flattened copy"; |
|
1536
|
|
|
|
|
|
|
} |
|
1537
|
|
|
|
|
|
|
elsif ( $field eq 'id' ) |
|
1538
|
|
|
|
|
|
|
{ |
|
1539
|
2
|
100
|
|
|
|
11
|
if ( defined( $self->get_info('primary_key_name') ) ) |
|
1540
|
|
|
|
|
|
|
{ |
|
1541
|
1
|
|
|
|
|
7
|
$data{'id'} = $self->id(); |
|
1542
|
|
|
|
|
|
|
} |
|
1543
|
|
|
|
|
|
|
else |
|
1544
|
|
|
|
|
|
|
{ |
|
1545
|
1
|
|
|
|
|
18
|
croak "Requested adding ID to the list of fields, but the class doesn't define a primary key name"; |
|
1546
|
|
|
|
|
|
|
} |
|
1547
|
|
|
|
|
|
|
} |
|
1548
|
|
|
|
|
|
|
else |
|
1549
|
|
|
|
|
|
|
{ |
|
1550
|
2
|
|
|
|
|
10
|
$data{ $field } = $self->{ $field }; |
|
1551
|
|
|
|
|
|
|
} |
|
1552
|
|
|
|
|
|
|
} |
|
1553
|
|
|
|
|
|
|
|
|
1554
|
2
|
|
|
|
|
14
|
return \%data; |
|
1555
|
|
|
|
|
|
|
} |
|
1556
|
|
|
|
|
|
|
|
|
1557
|
|
|
|
|
|
|
|
|
1558
|
|
|
|
|
|
|
=head2 reload() |
|
1559
|
|
|
|
|
|
|
|
|
1560
|
|
|
|
|
|
|
Reload the content of the current object. This always skips the cache. |
|
1561
|
|
|
|
|
|
|
|
|
1562
|
|
|
|
|
|
|
$book->reload(); |
|
1563
|
|
|
|
|
|
|
|
|
1564
|
|
|
|
|
|
|
=cut |
|
1565
|
|
|
|
|
|
|
|
|
1566
|
|
|
|
|
|
|
sub reload |
|
1567
|
|
|
|
|
|
|
{ |
|
1568
|
1
|
|
|
1
|
1
|
46
|
my ( $self ) = @_; |
|
1569
|
|
|
|
|
|
|
|
|
1570
|
|
|
|
|
|
|
# Make sure we were passed an object. |
|
1571
|
1
|
50
|
|
|
|
16
|
croak 'This method can only be called on an object' |
|
1572
|
|
|
|
|
|
|
if !Data::Validate::Type::is_hashref( $self ); |
|
1573
|
|
|
|
|
|
|
|
|
1574
|
1
|
|
|
|
|
27
|
my $class = ref( $self ); |
|
1575
|
|
|
|
|
|
|
|
|
1576
|
1
|
50
|
33
|
|
|
64
|
croak 'The object is not blessed with a class name' |
|
1577
|
|
|
|
|
|
|
if !defined( $class ) || ( $class eq '' ); |
|
1578
|
|
|
|
|
|
|
|
|
1579
|
1
|
50
|
|
|
|
16
|
croak "The class '$class' doesn't allow calling \$class->new()" |
|
1580
|
|
|
|
|
|
|
if ! $class->can('new'); |
|
1581
|
|
|
|
|
|
|
|
|
1582
|
|
|
|
|
|
|
# Verify that we can reload the object. |
|
1583
|
1
|
50
|
|
|
|
7
|
croak 'Cannot reload an object for which a primary key name has not been defined at the class level.' |
|
1584
|
|
|
|
|
|
|
if ! defined( $self->get_info('primary_key_name') ); |
|
1585
|
1
|
50
|
|
|
|
691
|
croak 'Cannot reload an object with no ID value for its primary key' |
|
1586
|
|
|
|
|
|
|
if ! defined( $self->id() ); |
|
1587
|
|
|
|
|
|
|
|
|
1588
|
|
|
|
|
|
|
# Retrieve a fresh version using the object ID. |
|
1589
|
1
|
|
|
|
|
4
|
my $id = $self->id(); |
|
1590
|
1
|
|
|
|
|
5
|
my $fresh_object = $class->new( |
|
1591
|
|
|
|
|
|
|
{ id => $self->id() }, |
|
1592
|
|
|
|
|
|
|
skip_cache => 1, |
|
1593
|
|
|
|
|
|
|
); |
|
1594
|
|
|
|
|
|
|
|
|
1595
|
1
|
50
|
|
|
|
7
|
croak "Could not retrieve the row in the database corresponding to the current object using ID '$id'" |
|
1596
|
|
|
|
|
|
|
if ! defined( $fresh_object ); |
|
1597
|
|
|
|
|
|
|
|
|
1598
|
|
|
|
|
|
|
# Keep the memory location intact. |
|
1599
|
1
|
|
|
|
|
4
|
%{ $self } = %{ $fresh_object }; |
|
|
1
|
|
|
|
|
12
|
|
|
|
1
|
|
|
|
|
5
|
|
|
1600
|
|
|
|
|
|
|
|
|
1601
|
1
|
|
|
|
|
32
|
return; |
|
1602
|
|
|
|
|
|
|
} |
|
1603
|
|
|
|
|
|
|
|
|
1604
|
|
|
|
|
|
|
|
|
1605
|
|
|
|
|
|
|
=head2 retrieve_list() |
|
1606
|
|
|
|
|
|
|
|
|
1607
|
|
|
|
|
|
|
Return an arrayref of objects matching all the criteria passed. |
|
1608
|
|
|
|
|
|
|
|
|
1609
|
|
|
|
|
|
|
This method supports the following filtering criteria in a hashref passed as |
|
1610
|
|
|
|
|
|
|
first argument: |
|
1611
|
|
|
|
|
|
|
|
|
1612
|
|
|
|
|
|
|
=over 4 |
|
1613
|
|
|
|
|
|
|
|
|
1614
|
|
|
|
|
|
|
=item * id |
|
1615
|
|
|
|
|
|
|
|
|
1616
|
|
|
|
|
|
|
An ID or an arrayref of IDs corresponding to the primary key. |
|
1617
|
|
|
|
|
|
|
|
|
1618
|
|
|
|
|
|
|
# Retrieve books with ID 1. |
|
1619
|
|
|
|
|
|
|
my $books = My::Model::Book->retrieve_list( |
|
1620
|
|
|
|
|
|
|
{ |
|
1621
|
|
|
|
|
|
|
id => 1, |
|
1622
|
|
|
|
|
|
|
} |
|
1623
|
|
|
|
|
|
|
); |
|
1624
|
|
|
|
|
|
|
|
|
1625
|
|
|
|
|
|
|
# Retrieve books with IDs 1, 2 or 3. |
|
1626
|
|
|
|
|
|
|
my $books = My::Model::Book->retrieve_list( |
|
1627
|
|
|
|
|
|
|
{ |
|
1628
|
|
|
|
|
|
|
id => [ 1, 2, 3 ] |
|
1629
|
|
|
|
|
|
|
} |
|
1630
|
|
|
|
|
|
|
); |
|
1631
|
|
|
|
|
|
|
|
|
1632
|
|
|
|
|
|
|
=item * Field names |
|
1633
|
|
|
|
|
|
|
|
|
1634
|
|
|
|
|
|
|
A scalar value or an arrayref of values corresponding to a field listed in |
|
1635
|
|
|
|
|
|
|
C under either C or C. |
|
1636
|
|
|
|
|
|
|
|
|
1637
|
|
|
|
|
|
|
# Retrieve books for an author. |
|
1638
|
|
|
|
|
|
|
my $books = My::Model::Book->retrieve_list( |
|
1639
|
|
|
|
|
|
|
{ |
|
1640
|
|
|
|
|
|
|
author_id => 12, |
|
1641
|
|
|
|
|
|
|
} |
|
1642
|
|
|
|
|
|
|
); |
|
1643
|
|
|
|
|
|
|
|
|
1644
|
|
|
|
|
|
|
# Retrieve books by ISBN. |
|
1645
|
|
|
|
|
|
|
my $books = My::Model::Book->retrieve_list( |
|
1646
|
|
|
|
|
|
|
{ |
|
1647
|
|
|
|
|
|
|
isbn => |
|
1648
|
|
|
|
|
|
|
[ |
|
1649
|
|
|
|
|
|
|
'9781449313142', |
|
1650
|
|
|
|
|
|
|
'9781449393090', |
|
1651
|
|
|
|
|
|
|
] |
|
1652
|
|
|
|
|
|
|
} |
|
1653
|
|
|
|
|
|
|
); |
|
1654
|
|
|
|
|
|
|
|
|
1655
|
|
|
|
|
|
|
=back |
|
1656
|
|
|
|
|
|
|
|
|
1657
|
|
|
|
|
|
|
Note that you can combine filters (which is the equivalent of AND in SQL) in |
|
1658
|
|
|
|
|
|
|
that hashref: |
|
1659
|
|
|
|
|
|
|
|
|
1660
|
|
|
|
|
|
|
# Retrieve books by ISBN for a specific author. |
|
1661
|
|
|
|
|
|
|
my $books = My::Model::Book->retrieve_list( |
|
1662
|
|
|
|
|
|
|
{ |
|
1663
|
|
|
|
|
|
|
isbn => |
|
1664
|
|
|
|
|
|
|
[ |
|
1665
|
|
|
|
|
|
|
'9781449313142', |
|
1666
|
|
|
|
|
|
|
'9781449393090', |
|
1667
|
|
|
|
|
|
|
], |
|
1668
|
|
|
|
|
|
|
author_id => 12, |
|
1669
|
|
|
|
|
|
|
} |
|
1670
|
|
|
|
|
|
|
); |
|
1671
|
|
|
|
|
|
|
|
|
1672
|
|
|
|
|
|
|
Filters as discussed above, imply an equality between the field and the values. For instance, in the last example, |
|
1673
|
|
|
|
|
|
|
the request could be written as "Please provide a list of books with author_id equal to 12, which also have an |
|
1674
|
|
|
|
|
|
|
ISBN equal to 9781449313142 or an ISBN equal to 9781449393090". |
|
1675
|
|
|
|
|
|
|
|
|
1676
|
|
|
|
|
|
|
If you wish to request records using some other operator than equals, you can create a request similar to the following: |
|
1677
|
|
|
|
|
|
|
|
|
1678
|
|
|
|
|
|
|
# Retrieve books for a specific author with ISBNs starting with a certain pattern. |
|
1679
|
|
|
|
|
|
|
my $books = My::Model::Book->retrieve_list( |
|
1680
|
|
|
|
|
|
|
{ |
|
1681
|
|
|
|
|
|
|
isbn => |
|
1682
|
|
|
|
|
|
|
{ |
|
1683
|
|
|
|
|
|
|
operator => 'like', |
|
1684
|
|
|
|
|
|
|
value => [ '9781%' ], |
|
1685
|
|
|
|
|
|
|
}, |
|
1686
|
|
|
|
|
|
|
author_id => 12, |
|
1687
|
|
|
|
|
|
|
} |
|
1688
|
|
|
|
|
|
|
); |
|
1689
|
|
|
|
|
|
|
|
|
1690
|
|
|
|
|
|
|
The above example could be written as "Please provide a list of books with author_id equal to 12, which also have |
|
1691
|
|
|
|
|
|
|
an ISBN starting with 9781". |
|
1692
|
|
|
|
|
|
|
|
|
1693
|
|
|
|
|
|
|
Valid operators include: |
|
1694
|
|
|
|
|
|
|
|
|
1695
|
|
|
|
|
|
|
* = |
|
1696
|
|
|
|
|
|
|
* not |
|
1697
|
|
|
|
|
|
|
* <= |
|
1698
|
|
|
|
|
|
|
* >= |
|
1699
|
|
|
|
|
|
|
* < |
|
1700
|
|
|
|
|
|
|
* > |
|
1701
|
|
|
|
|
|
|
* between |
|
1702
|
|
|
|
|
|
|
* null |
|
1703
|
|
|
|
|
|
|
* not_null |
|
1704
|
|
|
|
|
|
|
* like |
|
1705
|
|
|
|
|
|
|
* not_like |
|
1706
|
|
|
|
|
|
|
|
|
1707
|
|
|
|
|
|
|
This method also supports the following optional arguments, passed in a hash |
|
1708
|
|
|
|
|
|
|
after the filtering criteria above-mentioned: |
|
1709
|
|
|
|
|
|
|
|
|
1710
|
|
|
|
|
|
|
=over 4 |
|
1711
|
|
|
|
|
|
|
|
|
1712
|
|
|
|
|
|
|
=item * dbh |
|
1713
|
|
|
|
|
|
|
|
|
1714
|
|
|
|
|
|
|
Retrieve the data against a different database than the default one specified |
|
1715
|
|
|
|
|
|
|
in C. |
|
1716
|
|
|
|
|
|
|
|
|
1717
|
|
|
|
|
|
|
=item * order_by |
|
1718
|
|
|
|
|
|
|
|
|
1719
|
|
|
|
|
|
|
Specify an ORDER BY clause to sort the objects returned. |
|
1720
|
|
|
|
|
|
|
|
|
1721
|
|
|
|
|
|
|
my $books = My::Model::Book->retrieve_list( |
|
1722
|
|
|
|
|
|
|
{ |
|
1723
|
|
|
|
|
|
|
author_id => 12, |
|
1724
|
|
|
|
|
|
|
}, |
|
1725
|
|
|
|
|
|
|
order_by => 'books.name ASC', |
|
1726
|
|
|
|
|
|
|
); |
|
1727
|
|
|
|
|
|
|
|
|
1728
|
|
|
|
|
|
|
=item * limit |
|
1729
|
|
|
|
|
|
|
|
|
1730
|
|
|
|
|
|
|
Limit the number of objects to return. |
|
1731
|
|
|
|
|
|
|
|
|
1732
|
|
|
|
|
|
|
# Get 10 books from author #12. |
|
1733
|
|
|
|
|
|
|
my $books = My::Model::Book->retrieve_list( |
|
1734
|
|
|
|
|
|
|
{ |
|
1735
|
|
|
|
|
|
|
author_id => 12, |
|
1736
|
|
|
|
|
|
|
}, |
|
1737
|
|
|
|
|
|
|
limit => 10, |
|
1738
|
|
|
|
|
|
|
); |
|
1739
|
|
|
|
|
|
|
|
|
1740
|
|
|
|
|
|
|
=item * query_extensions |
|
1741
|
|
|
|
|
|
|
|
|
1742
|
|
|
|
|
|
|
Add joins and support different filtering criteria: |
|
1743
|
|
|
|
|
|
|
|
|
1744
|
|
|
|
|
|
|
=over 8 |
|
1745
|
|
|
|
|
|
|
|
|
1746
|
|
|
|
|
|
|
=item * where_clauses |
|
1747
|
|
|
|
|
|
|
|
|
1748
|
|
|
|
|
|
|
An arrayref of clauses to add to WHERE. |
|
1749
|
|
|
|
|
|
|
|
|
1750
|
|
|
|
|
|
|
=item * where_values |
|
1751
|
|
|
|
|
|
|
|
|
1752
|
|
|
|
|
|
|
An arrayref of values corresponding to the clauses. |
|
1753
|
|
|
|
|
|
|
|
|
1754
|
|
|
|
|
|
|
=item * joins |
|
1755
|
|
|
|
|
|
|
|
|
1756
|
|
|
|
|
|
|
A string specifying JOIN statements. |
|
1757
|
|
|
|
|
|
|
|
|
1758
|
|
|
|
|
|
|
=item * joined_fields |
|
1759
|
|
|
|
|
|
|
|
|
1760
|
|
|
|
|
|
|
A string of extra fields to add to the SELECT. |
|
1761
|
|
|
|
|
|
|
|
|
1762
|
|
|
|
|
|
|
=back |
|
1763
|
|
|
|
|
|
|
|
|
1764
|
|
|
|
|
|
|
my $books = My::Model::Book->retrieve_list( |
|
1765
|
|
|
|
|
|
|
{ |
|
1766
|
|
|
|
|
|
|
id => [ 1, 2, 3 ], |
|
1767
|
|
|
|
|
|
|
}, |
|
1768
|
|
|
|
|
|
|
query_extensions => |
|
1769
|
|
|
|
|
|
|
{ |
|
1770
|
|
|
|
|
|
|
where_clauses => [ 'authors.name = ?' ], |
|
1771
|
|
|
|
|
|
|
where_values => [ [ 'Randal L. Schwartz' ] ], |
|
1772
|
|
|
|
|
|
|
joins => 'INNER JOIN authors USING (author_id)', |
|
1773
|
|
|
|
|
|
|
joined_fields => 'authors.name AS _author_name', |
|
1774
|
|
|
|
|
|
|
} |
|
1775
|
|
|
|
|
|
|
); |
|
1776
|
|
|
|
|
|
|
|
|
1777
|
|
|
|
|
|
|
=item * pagination |
|
1778
|
|
|
|
|
|
|
|
|
1779
|
|
|
|
|
|
|
Off by default. Paginate the results. You can control the pagination options |
|
1780
|
|
|
|
|
|
|
by setting this to the following hash, with each key being optional and falling |
|
1781
|
|
|
|
|
|
|
back to the default if you omit it: |
|
1782
|
|
|
|
|
|
|
|
|
1783
|
|
|
|
|
|
|
my $books = My::Model::Book->retrieve_list( |
|
1784
|
|
|
|
|
|
|
{}, |
|
1785
|
|
|
|
|
|
|
allow_all => 1, |
|
1786
|
|
|
|
|
|
|
pagination => |
|
1787
|
|
|
|
|
|
|
{ |
|
1788
|
|
|
|
|
|
|
# The number of results to retrieve. |
|
1789
|
|
|
|
|
|
|
per_page => $per_page, |
|
1790
|
|
|
|
|
|
|
# Number of the page of results to retrieve. If you have per_page=10 |
|
1791
|
|
|
|
|
|
|
# and page=2, then this would retrieve rows 10-19 from the set of |
|
1792
|
|
|
|
|
|
|
# matching rows. |
|
1793
|
|
|
|
|
|
|
page => $page, |
|
1794
|
|
|
|
|
|
|
} |
|
1795
|
|
|
|
|
|
|
); |
|
1796
|
|
|
|
|
|
|
|
|
1797
|
|
|
|
|
|
|
Additionally, pagination can be set to '1' instead of {} and then the default |
|
1798
|
|
|
|
|
|
|
options will be used. |
|
1799
|
|
|
|
|
|
|
|
|
1800
|
|
|
|
|
|
|
More pagination information is then returned in list context: |
|
1801
|
|
|
|
|
|
|
|
|
1802
|
|
|
|
|
|
|
my ( $books, $pagination ) = My::Model::Book->retrieve_list( ... ); |
|
1803
|
|
|
|
|
|
|
|
|
1804
|
|
|
|
|
|
|
With the following pagination information inside C<$pagination>: |
|
1805
|
|
|
|
|
|
|
|
|
1806
|
|
|
|
|
|
|
{ |
|
1807
|
|
|
|
|
|
|
# The total number of rows matching the query. |
|
1808
|
|
|
|
|
|
|
total_count => $total_count, |
|
1809
|
|
|
|
|
|
|
# The current page being returned. |
|
1810
|
|
|
|
|
|
|
page => $page, |
|
1811
|
|
|
|
|
|
|
# The total number of pages to display the matching rows. |
|
1812
|
|
|
|
|
|
|
page_max => $page_max, |
|
1813
|
|
|
|
|
|
|
# The number of rows displayed per page. |
|
1814
|
|
|
|
|
|
|
per_page => $per_page, |
|
1815
|
|
|
|
|
|
|
} |
|
1816
|
|
|
|
|
|
|
|
|
1817
|
|
|
|
|
|
|
=item * lock (default 0) |
|
1818
|
|
|
|
|
|
|
|
|
1819
|
|
|
|
|
|
|
Add a lock to the rows retrieved. |
|
1820
|
|
|
|
|
|
|
|
|
1821
|
|
|
|
|
|
|
my $books = My::Model::Book->retrieve_list( |
|
1822
|
|
|
|
|
|
|
{ |
|
1823
|
|
|
|
|
|
|
id => [ 1, 2, 3 ], |
|
1824
|
|
|
|
|
|
|
}, |
|
1825
|
|
|
|
|
|
|
lock => 1, |
|
1826
|
|
|
|
|
|
|
); |
|
1827
|
|
|
|
|
|
|
|
|
1828
|
|
|
|
|
|
|
=item * allow_all (default 0) |
|
1829
|
|
|
|
|
|
|
|
|
1830
|
|
|
|
|
|
|
Retrieve all the rows in the table if no criteria is passed. Off by |
|
1831
|
|
|
|
|
|
|
default to prevent retrieving large tables at once. |
|
1832
|
|
|
|
|
|
|
|
|
1833
|
|
|
|
|
|
|
# All the books! |
|
1834
|
|
|
|
|
|
|
my $books = My::Model::Book->retrieve_list( |
|
1835
|
|
|
|
|
|
|
{}, |
|
1836
|
|
|
|
|
|
|
allow_all => 1, |
|
1837
|
|
|
|
|
|
|
); |
|
1838
|
|
|
|
|
|
|
|
|
1839
|
|
|
|
|
|
|
=item * show_queries (default 0) |
|
1840
|
|
|
|
|
|
|
|
|
1841
|
|
|
|
|
|
|
Set to '1' to see in the logs the queries being performed. |
|
1842
|
|
|
|
|
|
|
|
|
1843
|
|
|
|
|
|
|
my $books = My::Model::Book->retrieve_list( |
|
1844
|
|
|
|
|
|
|
{ |
|
1845
|
|
|
|
|
|
|
id => [ 1, 2, 3 ], |
|
1846
|
|
|
|
|
|
|
}, |
|
1847
|
|
|
|
|
|
|
show_queries => 1, |
|
1848
|
|
|
|
|
|
|
); |
|
1849
|
|
|
|
|
|
|
|
|
1850
|
|
|
|
|
|
|
=item * allow_subclassing (default 0) |
|
1851
|
|
|
|
|
|
|
|
|
1852
|
|
|
|
|
|
|
By default, C cannot be subclassed to prevent accidental |
|
1853
|
|
|
|
|
|
|
infinite recursions and breaking the cache features provided by NinjaORM. |
|
1854
|
|
|
|
|
|
|
Typically, if you want to add functionality to how retrieving a group of |
|
1855
|
|
|
|
|
|
|
objects works, you will want to modify C instead. |
|
1856
|
|
|
|
|
|
|
|
|
1857
|
|
|
|
|
|
|
If you really need to subclass C, you will then need to |
|
1858
|
|
|
|
|
|
|
set C to C<1> in subclassed method's call to its parent, |
|
1859
|
|
|
|
|
|
|
to indicate that you've carefully considered the impact of this and that it |
|
1860
|
|
|
|
|
|
|
is safe. |
|
1861
|
|
|
|
|
|
|
|
|
1862
|
|
|
|
|
|
|
=item * select_fields / exclude_fields (optional) |
|
1863
|
|
|
|
|
|
|
|
|
1864
|
|
|
|
|
|
|
By default, C will select all the fields that exist on the |
|
1865
|
|
|
|
|
|
|
table associated with the class. In some rare cases, it is however desirable to |
|
1866
|
|
|
|
|
|
|
either select only or to exclude explicitely some fields from the table, and |
|
1867
|
|
|
|
|
|
|
you can pass an arrayref with C and C |
|
1868
|
|
|
|
|
|
|
(respectively) to specify those. |
|
1869
|
|
|
|
|
|
|
|
|
1870
|
|
|
|
|
|
|
Important cache consideration: when this option is used, the cache will be used |
|
1871
|
|
|
|
|
|
|
to retrieve objects without polling the database when possible, but any objects |
|
1872
|
|
|
|
|
|
|
retrieved from the database will not be stashed in the cache as they will not |
|
1873
|
|
|
|
|
|
|
have the complete information for that object. If you have other |
|
1874
|
|
|
|
|
|
|
C calls warming the cache this most likely won't be an issue, |
|
1875
|
|
|
|
|
|
|
but if you exclusively run C calls with C and |
|
1876
|
|
|
|
|
|
|
C, then you may be better off creating a view and tieing the |
|
1877
|
|
|
|
|
|
|
class to that view. |
|
1878
|
|
|
|
|
|
|
|
|
1879
|
|
|
|
|
|
|
# To display an index of our library, we want all the book properties but not |
|
1880
|
|
|
|
|
|
|
# the book content, which is a huge field that we won't use in the template. |
|
1881
|
|
|
|
|
|
|
my $books = My::Model::Book->retrieve_list( |
|
1882
|
|
|
|
|
|
|
{}, |
|
1883
|
|
|
|
|
|
|
allow_all => 1, |
|
1884
|
|
|
|
|
|
|
exclude_fields => [ 'full_text' ], |
|
1885
|
|
|
|
|
|
|
); |
|
1886
|
|
|
|
|
|
|
|
|
1887
|
|
|
|
|
|
|
=back |
|
1888
|
|
|
|
|
|
|
|
|
1889
|
|
|
|
|
|
|
=cut |
|
1890
|
|
|
|
|
|
|
|
|
1891
|
|
|
|
|
|
|
sub retrieve_list |
|
1892
|
|
|
|
|
|
|
{ |
|
1893
|
26
|
|
|
26
|
1
|
20395
|
my ( $class, $filters, %args ) = @_; |
|
1894
|
26
|
|
100
|
|
|
190
|
my $allow_subclassing = delete( $args{'allow_subclassing'} ) || 0; |
|
1895
|
|
|
|
|
|
|
|
|
1896
|
|
|
|
|
|
|
# Check caller and prevent calls from a subclass' retrieve_list(). |
|
1897
|
26
|
100
|
|
|
|
150
|
if ( !$allow_subclassing ) |
|
1898
|
|
|
|
|
|
|
{ |
|
1899
|
25
|
|
|
|
|
211
|
my $subroutine = (caller(1))[3]; |
|
1900
|
25
|
50
|
|
|
|
752
|
if ( defined( $subroutine ) ) |
|
1901
|
|
|
|
|
|
|
{ |
|
1902
|
25
|
|
|
|
|
183
|
$subroutine =~ s/^.*:://; |
|
1903
|
25
|
100
|
|
|
|
176
|
croak( |
|
1904
|
|
|
|
|
|
|
'You have subclassed retrieve_list(), which is not allowed to prevent infinite recursions. ' . |
|
1905
|
|
|
|
|
|
|
'You most likely want to subclass retrieve_list_nocache() instead.' |
|
1906
|
|
|
|
|
|
|
) if $subroutine eq 'retrieve_list'; |
|
1907
|
|
|
|
|
|
|
} |
|
1908
|
|
|
|
|
|
|
} |
|
1909
|
|
|
|
|
|
|
|
|
1910
|
25
|
|
100
|
|
|
115
|
my $any_cache_time = $class->get_info('list_cache_time') || $class->get_info('object_cache_time'); |
|
1911
|
25
|
100
|
66
|
|
|
390
|
return defined( $any_cache_time ) && !$args{'skip_cache'} && !$args{'lock'} |
|
1912
|
|
|
|
|
|
|
? $class->retrieve_list_cache( $filters, %args ) |
|
1913
|
|
|
|
|
|
|
: $class->retrieve_list_nocache( $filters, %args ); |
|
1914
|
|
|
|
|
|
|
} |
|
1915
|
|
|
|
|
|
|
|
|
1916
|
|
|
|
|
|
|
|
|
1917
|
|
|
|
|
|
|
=head1 ACCESSORS |
|
1918
|
|
|
|
|
|
|
|
|
1919
|
|
|
|
|
|
|
|
|
1920
|
|
|
|
|
|
|
=head2 get_cache_key_field() |
|
1921
|
|
|
|
|
|
|
|
|
1922
|
|
|
|
|
|
|
Return the name of the field that should be used in the cache key. |
|
1923
|
|
|
|
|
|
|
|
|
1924
|
|
|
|
|
|
|
my $cache_time = $class->cache_key_field(); |
|
1925
|
|
|
|
|
|
|
my $cache_time = $object->cache_key_field(); |
|
1926
|
|
|
|
|
|
|
|
|
1927
|
|
|
|
|
|
|
=cut |
|
1928
|
|
|
|
|
|
|
|
|
1929
|
|
|
|
|
|
|
sub get_cache_key_field |
|
1930
|
|
|
|
|
|
|
{ |
|
1931
|
3
|
|
|
3
|
1
|
7239
|
my ( $self ) = @_; |
|
1932
|
|
|
|
|
|
|
|
|
1933
|
3
|
|
|
|
|
17
|
my $cache_key_field = $self->cached_static_class_info()->get('cache_key_field'); |
|
1934
|
|
|
|
|
|
|
|
|
1935
|
|
|
|
|
|
|
# If the subclass specifies a field to use for the cache key name, use it. |
|
1936
|
|
|
|
|
|
|
# Otherwise, we fall back on the primary key if it exists. |
|
1937
|
3
|
100
|
|
|
|
17
|
return defined( $cache_key_field ) |
|
1938
|
|
|
|
|
|
|
? $cache_key_field |
|
1939
|
|
|
|
|
|
|
: $self->get_info('primary_key_name'); |
|
1940
|
|
|
|
|
|
|
} |
|
1941
|
|
|
|
|
|
|
|
|
1942
|
|
|
|
|
|
|
|
|
1943
|
|
|
|
|
|
|
=head2 get_default_dbh() |
|
1944
|
|
|
|
|
|
|
|
|
1945
|
|
|
|
|
|
|
WARNING: this method will be removed soon. Use C instead. |
|
1946
|
|
|
|
|
|
|
|
|
1947
|
|
|
|
|
|
|
Return the default database handle to use with this class. |
|
1948
|
|
|
|
|
|
|
|
|
1949
|
|
|
|
|
|
|
my $default_dbh = $class->get_default_dbh(); |
|
1950
|
|
|
|
|
|
|
my $default_dbh = $object->get_default_dbh(); |
|
1951
|
|
|
|
|
|
|
|
|
1952
|
|
|
|
|
|
|
=cut |
|
1953
|
|
|
|
|
|
|
|
|
1954
|
|
|
|
|
|
|
sub get_default_dbh |
|
1955
|
|
|
|
|
|
|
{ |
|
1956
|
2
|
|
|
2
|
1
|
5766
|
my ( $self ) = @_; |
|
1957
|
|
|
|
|
|
|
|
|
1958
|
2
|
|
|
|
|
46
|
carp "get_default_dbh() has been deprecated, please change the method call to get_info('default_dbh')"; |
|
1959
|
|
|
|
|
|
|
|
|
1960
|
2
|
|
|
|
|
1450
|
return $self->get_info('default_dbh'); |
|
1961
|
|
|
|
|
|
|
} |
|
1962
|
|
|
|
|
|
|
|
|
1963
|
|
|
|
|
|
|
|
|
1964
|
|
|
|
|
|
|
=head2 get_filtering_fields() |
|
1965
|
|
|
|
|
|
|
|
|
1966
|
|
|
|
|
|
|
Returns the fields that can be used as filtering criteria in retrieve_list(). |
|
1967
|
|
|
|
|
|
|
|
|
1968
|
|
|
|
|
|
|
Notes: |
|
1969
|
|
|
|
|
|
|
|
|
1970
|
|
|
|
|
|
|
=over 4 |
|
1971
|
|
|
|
|
|
|
|
|
1972
|
|
|
|
|
|
|
=item * Does not include the primary key. |
|
1973
|
|
|
|
|
|
|
|
|
1974
|
|
|
|
|
|
|
=item * Includes unique fields. |
|
1975
|
|
|
|
|
|
|
|
|
1976
|
|
|
|
|
|
|
my $filtering_fields = $class->get_filtering_fields(); |
|
1977
|
|
|
|
|
|
|
my $filtering_fields = $object->get_filtering_fields(); |
|
1978
|
|
|
|
|
|
|
|
|
1979
|
|
|
|
|
|
|
=back |
|
1980
|
|
|
|
|
|
|
|
|
1981
|
|
|
|
|
|
|
=cut |
|
1982
|
|
|
|
|
|
|
|
|
1983
|
|
|
|
|
|
|
sub get_filtering_fields |
|
1984
|
|
|
|
|
|
|
{ |
|
1985
|
36
|
|
|
36
|
1
|
6025
|
my ( $self ) = @_; |
|
1986
|
|
|
|
|
|
|
|
|
1987
|
34
|
|
|
|
|
133
|
my %fields = ( |
|
1988
|
36
|
|
|
|
|
185
|
map { $_ => undef } |
|
1989
|
|
|
|
|
|
|
( |
|
1990
|
36
|
|
|
|
|
110
|
@{ $self->cached_static_class_info()->get('filtering_fields') }, |
|
1991
|
36
|
|
|
|
|
79
|
@{ $self->cached_static_class_info()->get('unique_fields') }, |
|
1992
|
|
|
|
|
|
|
) |
|
1993
|
|
|
|
|
|
|
); |
|
1994
|
36
|
|
|
|
|
248
|
return [ keys %fields ]; |
|
1995
|
|
|
|
|
|
|
} |
|
1996
|
|
|
|
|
|
|
|
|
1997
|
|
|
|
|
|
|
|
|
1998
|
|
|
|
|
|
|
=head2 get_info() |
|
1999
|
|
|
|
|
|
|
|
|
2000
|
|
|
|
|
|
|
Return cached static class information for the current object or class. |
|
2001
|
|
|
|
|
|
|
|
|
2002
|
|
|
|
|
|
|
my $info = $class->get_info(); |
|
2003
|
|
|
|
|
|
|
my $info = $object->get_info(); |
|
2004
|
|
|
|
|
|
|
|
|
2005
|
|
|
|
|
|
|
=cut |
|
2006
|
|
|
|
|
|
|
|
|
2007
|
|
|
|
|
|
|
sub get_info { |
|
2008
|
887
|
|
|
887
|
1
|
24215
|
my ( $self, $key ) = @_; |
|
2009
|
|
|
|
|
|
|
|
|
2010
|
887
|
|
|
|
|
3268
|
return $self->cached_static_class_info()->get( $key ); |
|
2011
|
|
|
|
|
|
|
} |
|
2012
|
|
|
|
|
|
|
|
|
2013
|
|
|
|
|
|
|
|
|
2014
|
|
|
|
|
|
|
=head2 get_list_cache_time() |
|
2015
|
|
|
|
|
|
|
|
|
2016
|
|
|
|
|
|
|
WARNING: this method will be removed soon. Use C |
|
2017
|
|
|
|
|
|
|
instead. |
|
2018
|
|
|
|
|
|
|
|
|
2019
|
|
|
|
|
|
|
Return the duration for which a list of objects of the current class can be |
|
2020
|
|
|
|
|
|
|
cached. |
|
2021
|
|
|
|
|
|
|
|
|
2022
|
|
|
|
|
|
|
my $list_cache_time = $class->list_cache_time(); |
|
2023
|
|
|
|
|
|
|
my $list_cache_time = $object->list_cache_time(); |
|
2024
|
|
|
|
|
|
|
|
|
2025
|
|
|
|
|
|
|
=cut |
|
2026
|
|
|
|
|
|
|
|
|
2027
|
|
|
|
|
|
|
sub get_list_cache_time |
|
2028
|
|
|
|
|
|
|
{ |
|
2029
|
3
|
|
|
3
|
1
|
6310
|
my ( $self ) = @_; |
|
2030
|
|
|
|
|
|
|
|
|
2031
|
3
|
|
|
|
|
58
|
carp "get_list_cache_time() has been deprecated, please change the method call to get_info('list_cache_time')"; |
|
2032
|
|
|
|
|
|
|
|
|
2033
|
3
|
|
|
|
|
2148
|
return $self->get_info('list_cache_time'); |
|
2034
|
|
|
|
|
|
|
} |
|
2035
|
|
|
|
|
|
|
|
|
2036
|
|
|
|
|
|
|
|
|
2037
|
|
|
|
|
|
|
=head2 get_memcache() |
|
2038
|
|
|
|
|
|
|
|
|
2039
|
|
|
|
|
|
|
WARNING: this method will be removed soon. Use C instead. |
|
2040
|
|
|
|
|
|
|
|
|
2041
|
|
|
|
|
|
|
Return the memcache object to use with this class. |
|
2042
|
|
|
|
|
|
|
|
|
2043
|
|
|
|
|
|
|
my $memcache = $class->get_memcache(); |
|
2044
|
|
|
|
|
|
|
my $memcache = $object->get_memcache(); |
|
2045
|
|
|
|
|
|
|
|
|
2046
|
|
|
|
|
|
|
=cut |
|
2047
|
|
|
|
|
|
|
|
|
2048
|
|
|
|
|
|
|
sub get_memcache |
|
2049
|
|
|
|
|
|
|
{ |
|
2050
|
2
|
|
|
2
|
1
|
5279
|
my ( $self ) = @_; |
|
2051
|
|
|
|
|
|
|
|
|
2052
|
2
|
|
|
|
|
36
|
carp "get_memcache() has been deprecated, please change the method call to get_info('memcache')"; |
|
2053
|
|
|
|
|
|
|
|
|
2054
|
2
|
|
|
|
|
1387
|
return $self->get_info('memcache'); |
|
2055
|
|
|
|
|
|
|
} |
|
2056
|
|
|
|
|
|
|
|
|
2057
|
|
|
|
|
|
|
|
|
2058
|
|
|
|
|
|
|
=head2 get_object_cache_time() |
|
2059
|
|
|
|
|
|
|
|
|
2060
|
|
|
|
|
|
|
WARNING: this method will be removed soon. Use C |
|
2061
|
|
|
|
|
|
|
instead. |
|
2062
|
|
|
|
|
|
|
|
|
2063
|
|
|
|
|
|
|
Return the duration for which an object of the current class can be cached. |
|
2064
|
|
|
|
|
|
|
|
|
2065
|
|
|
|
|
|
|
my $object_cache_time = $class->get_object_cache_time(); |
|
2066
|
|
|
|
|
|
|
my $object_cache_time = $object->get_object_cache_time(); |
|
2067
|
|
|
|
|
|
|
|
|
2068
|
|
|
|
|
|
|
=cut |
|
2069
|
|
|
|
|
|
|
|
|
2070
|
|
|
|
|
|
|
sub get_object_cache_time |
|
2071
|
|
|
|
|
|
|
{ |
|
2072
|
3
|
|
|
3
|
1
|
5973
|
my ( $self ) = @_; |
|
2073
|
|
|
|
|
|
|
|
|
2074
|
3
|
|
|
|
|
53
|
carp "get_object_cache_time() has been deprecated, please change the method call to get_info('object_cache_time')"; |
|
2075
|
|
|
|
|
|
|
|
|
2076
|
3
|
|
|
|
|
2069
|
return $self->get_info('object_cache_time'); |
|
2077
|
|
|
|
|
|
|
} |
|
2078
|
|
|
|
|
|
|
|
|
2079
|
|
|
|
|
|
|
|
|
2080
|
|
|
|
|
|
|
=head2 get_primary_key_name() |
|
2081
|
|
|
|
|
|
|
|
|
2082
|
|
|
|
|
|
|
WARNING: this method will be removed soon. Use C instead. |
|
2083
|
|
|
|
|
|
|
|
|
2084
|
|
|
|
|
|
|
Return the underlying primary key name for the current class or object. |
|
2085
|
|
|
|
|
|
|
|
|
2086
|
|
|
|
|
|
|
my $primary_key_name = $class->get_primary_key_name(); |
|
2087
|
|
|
|
|
|
|
my $primary_key_name = $object->get_primary_key_name(); |
|
2088
|
|
|
|
|
|
|
|
|
2089
|
|
|
|
|
|
|
=cut |
|
2090
|
|
|
|
|
|
|
|
|
2091
|
|
|
|
|
|
|
sub get_primary_key_name |
|
2092
|
|
|
|
|
|
|
{ |
|
2093
|
2
|
|
|
2
|
1
|
4824
|
my ( $self ) = @_; |
|
2094
|
|
|
|
|
|
|
|
|
2095
|
2
|
|
|
|
|
33
|
carp "get_primary_key_name() has been deprecated, please change the method call to get_info('primary_key_name')"; |
|
2096
|
|
|
|
|
|
|
|
|
2097
|
2
|
|
|
|
|
1080
|
return $self->get_info('primary_key_name'); |
|
2098
|
|
|
|
|
|
|
} |
|
2099
|
|
|
|
|
|
|
|
|
2100
|
|
|
|
|
|
|
|
|
2101
|
|
|
|
|
|
|
=head2 get_readonly_fields() |
|
2102
|
|
|
|
|
|
|
|
|
2103
|
|
|
|
|
|
|
WARNING: this method will be removed soon. Use C instead. |
|
2104
|
|
|
|
|
|
|
|
|
2105
|
|
|
|
|
|
|
Return an arrayref of fields that cannot be modified via C, C, |
|
2106
|
|
|
|
|
|
|
or C. |
|
2107
|
|
|
|
|
|
|
|
|
2108
|
|
|
|
|
|
|
my $readonly_fields = $class->get_readonly_fields(); |
|
2109
|
|
|
|
|
|
|
my $readonly_fields = $object->get_readonly_fields(); |
|
2110
|
|
|
|
|
|
|
|
|
2111
|
|
|
|
|
|
|
=cut |
|
2112
|
|
|
|
|
|
|
|
|
2113
|
|
|
|
|
|
|
sub get_readonly_fields |
|
2114
|
|
|
|
|
|
|
{ |
|
2115
|
3
|
|
|
3
|
1
|
6667
|
my ( $self ) = @_; |
|
2116
|
|
|
|
|
|
|
|
|
2117
|
3
|
|
|
|
|
60
|
carp "get_readonly_fields() has been deprecated, please change the method call to get_info('readonly_fields')"; |
|
2118
|
|
|
|
|
|
|
|
|
2119
|
3
|
|
|
|
|
2061
|
return $self->get_info('readonly_fields'); |
|
2120
|
|
|
|
|
|
|
} |
|
2121
|
|
|
|
|
|
|
|
|
2122
|
|
|
|
|
|
|
|
|
2123
|
|
|
|
|
|
|
=head2 get_table_name() |
|
2124
|
|
|
|
|
|
|
|
|
2125
|
|
|
|
|
|
|
WARNING: this method will be removed soon. Use C instead. |
|
2126
|
|
|
|
|
|
|
|
|
2127
|
|
|
|
|
|
|
Returns the underlying table name for the current class or object. |
|
2128
|
|
|
|
|
|
|
|
|
2129
|
|
|
|
|
|
|
my $table_name = $class->get_table_name(); |
|
2130
|
|
|
|
|
|
|
my $table_name = $object->get_table_name(); |
|
2131
|
|
|
|
|
|
|
|
|
2132
|
|
|
|
|
|
|
=cut |
|
2133
|
|
|
|
|
|
|
|
|
2134
|
|
|
|
|
|
|
sub get_table_name |
|
2135
|
|
|
|
|
|
|
{ |
|
2136
|
2
|
|
|
2
|
1
|
4891
|
my ( $self ) = @_; |
|
2137
|
|
|
|
|
|
|
|
|
2138
|
2
|
|
|
|
|
40
|
carp "get_table_name() has been deprecated, please change the method call to get_info('table_name')"; |
|
2139
|
|
|
|
|
|
|
|
|
2140
|
2
|
|
|
|
|
1138
|
return $self->get_info('table_name'); |
|
2141
|
|
|
|
|
|
|
} |
|
2142
|
|
|
|
|
|
|
|
|
2143
|
|
|
|
|
|
|
|
|
2144
|
|
|
|
|
|
|
=head2 get_unique_fields() |
|
2145
|
|
|
|
|
|
|
|
|
2146
|
|
|
|
|
|
|
WARNING: this method will be removed soon. Use C |
|
2147
|
|
|
|
|
|
|
instead. |
|
2148
|
|
|
|
|
|
|
|
|
2149
|
|
|
|
|
|
|
Return an arrayref of fields that are unique for the underlying table. |
|
2150
|
|
|
|
|
|
|
|
|
2151
|
|
|
|
|
|
|
Important: this doesn't include the primary key name. To retrieve the name |
|
2152
|
|
|
|
|
|
|
of the primary key, use C<$class->primary_key_name()> |
|
2153
|
|
|
|
|
|
|
|
|
2154
|
|
|
|
|
|
|
my $unique_fields = $class->get_unique_fields(); |
|
2155
|
|
|
|
|
|
|
my $unique_fields = $object->get_unique_fields(); |
|
2156
|
|
|
|
|
|
|
|
|
2157
|
|
|
|
|
|
|
=cut |
|
2158
|
|
|
|
|
|
|
|
|
2159
|
|
|
|
|
|
|
sub get_unique_fields |
|
2160
|
|
|
|
|
|
|
{ |
|
2161
|
3
|
|
|
3
|
1
|
6173
|
my ( $self ) = @_; |
|
2162
|
|
|
|
|
|
|
|
|
2163
|
3
|
|
|
|
|
54
|
carp "get_unique_fields() has been deprecated, please change the method call to get_info('unique_fields')"; |
|
2164
|
|
|
|
|
|
|
|
|
2165
|
3
|
|
|
|
|
2002
|
return $self->get_info('unique_fields'); |
|
2166
|
|
|
|
|
|
|
} |
|
2167
|
|
|
|
|
|
|
|
|
2168
|
|
|
|
|
|
|
|
|
2169
|
|
|
|
|
|
|
=head2 has_created_field() |
|
2170
|
|
|
|
|
|
|
|
|
2171
|
|
|
|
|
|
|
WARNING: this method will be removed soon. Use C |
|
2172
|
|
|
|
|
|
|
instead. |
|
2173
|
|
|
|
|
|
|
|
|
2174
|
|
|
|
|
|
|
Return a boolean to indicate whether the underlying table has a 'created' |
|
2175
|
|
|
|
|
|
|
field. |
|
2176
|
|
|
|
|
|
|
|
|
2177
|
|
|
|
|
|
|
my $has_created_field = $class->has_created_field(); |
|
2178
|
|
|
|
|
|
|
my $has_created_field = $object->has_created_field(); |
|
2179
|
|
|
|
|
|
|
|
|
2180
|
|
|
|
|
|
|
=cut |
|
2181
|
|
|
|
|
|
|
|
|
2182
|
|
|
|
|
|
|
sub has_created_field |
|
2183
|
|
|
|
|
|
|
{ |
|
2184
|
5
|
|
|
5
|
1
|
10655
|
my ( $self ) = @_; |
|
2185
|
|
|
|
|
|
|
|
|
2186
|
5
|
|
|
|
|
78
|
carp "has_created_field() has been deprecated, please change the method call to get_info('has_created_field')"; |
|
2187
|
|
|
|
|
|
|
|
|
2188
|
5
|
|
|
|
|
3356
|
return $self->get_info('has_created_field'); |
|
2189
|
|
|
|
|
|
|
} |
|
2190
|
|
|
|
|
|
|
|
|
2191
|
|
|
|
|
|
|
|
|
2192
|
|
|
|
|
|
|
=head2 has_modified_field() |
|
2193
|
|
|
|
|
|
|
|
|
2194
|
|
|
|
|
|
|
WARNING: this method will be removed soon. Use C instead. |
|
2195
|
|
|
|
|
|
|
|
|
2196
|
|
|
|
|
|
|
Return a boolean to indicate whether the underlying table has a 'modified' |
|
2197
|
|
|
|
|
|
|
field. |
|
2198
|
|
|
|
|
|
|
|
|
2199
|
|
|
|
|
|
|
my $has_modified_field = $class->has_modified_field(); |
|
2200
|
|
|
|
|
|
|
my $has_modified_field = $object->has_modified_field(); |
|
2201
|
|
|
|
|
|
|
|
|
2202
|
|
|
|
|
|
|
=cut |
|
2203
|
|
|
|
|
|
|
|
|
2204
|
|
|
|
|
|
|
sub has_modified_field |
|
2205
|
|
|
|
|
|
|
{ |
|
2206
|
5
|
|
|
5
|
1
|
10789
|
my ( $self ) = @_; |
|
2207
|
|
|
|
|
|
|
|
|
2208
|
5
|
|
|
|
|
89
|
carp "has_modified_field() has been deprecated, please change the method call to get_info('has_modified_field')"; |
|
2209
|
|
|
|
|
|
|
|
|
2210
|
5
|
|
|
|
|
3494
|
return $self->get_info('has_modified_field'); |
|
2211
|
|
|
|
|
|
|
} |
|
2212
|
|
|
|
|
|
|
|
|
2213
|
|
|
|
|
|
|
|
|
2214
|
|
|
|
|
|
|
=head2 id() |
|
2215
|
|
|
|
|
|
|
|
|
2216
|
|
|
|
|
|
|
Return the value associated with the primary key for the current object. |
|
2217
|
|
|
|
|
|
|
|
|
2218
|
|
|
|
|
|
|
my $id = $object->id(); |
|
2219
|
|
|
|
|
|
|
|
|
2220
|
|
|
|
|
|
|
=cut |
|
2221
|
|
|
|
|
|
|
|
|
2222
|
|
|
|
|
|
|
sub id |
|
2223
|
|
|
|
|
|
|
{ |
|
2224
|
38
|
|
|
38
|
1
|
3890
|
my ( $self ) = @_; |
|
2225
|
|
|
|
|
|
|
|
|
2226
|
38
|
|
|
|
|
169
|
my $primary_key_name = $self->get_info('primary_key_name'); |
|
2227
|
38
|
50
|
|
|
|
529
|
return defined( $primary_key_name ) |
|
2228
|
|
|
|
|
|
|
? $self->{ $primary_key_name } |
|
2229
|
|
|
|
|
|
|
: undef; |
|
2230
|
|
|
|
|
|
|
} |
|
2231
|
|
|
|
|
|
|
|
|
2232
|
|
|
|
|
|
|
|
|
2233
|
|
|
|
|
|
|
=head2 is_verbose() |
|
2234
|
|
|
|
|
|
|
|
|
2235
|
|
|
|
|
|
|
Return if verbosity is enabled. |
|
2236
|
|
|
|
|
|
|
|
|
2237
|
|
|
|
|
|
|
This method supports two types of verbosity: |
|
2238
|
|
|
|
|
|
|
|
|
2239
|
|
|
|
|
|
|
=over 4 |
|
2240
|
|
|
|
|
|
|
|
|
2241
|
|
|
|
|
|
|
=item * general verbosity |
|
2242
|
|
|
|
|
|
|
|
|
2243
|
|
|
|
|
|
|
Called with no argument, this returns whether code in general will be verbose. |
|
2244
|
|
|
|
|
|
|
|
|
2245
|
|
|
|
|
|
|
$log->debug( 'This is verbose' ) |
|
2246
|
|
|
|
|
|
|
if $class->is_verbose(); |
|
2247
|
|
|
|
|
|
|
$log->debug( 'This is verbose' ) |
|
2248
|
|
|
|
|
|
|
if $object->is_verbose(); |
|
2249
|
|
|
|
|
|
|
|
|
2250
|
|
|
|
|
|
|
=item * verbosity for a specific type of operations |
|
2251
|
|
|
|
|
|
|
|
|
2252
|
|
|
|
|
|
|
Called with a specific type of operations as first argument, this returns |
|
2253
|
|
|
|
|
|
|
whether that type of operations will be verbose. |
|
2254
|
|
|
|
|
|
|
|
|
2255
|
|
|
|
|
|
|
$log->debug( 'Describe cache operation' ) |
|
2256
|
|
|
|
|
|
|
if $class->is_verbose( $operation_type ); |
|
2257
|
|
|
|
|
|
|
$log->debug( 'Describe cache operation' ) |
|
2258
|
|
|
|
|
|
|
if $object->is_verbose( $operation_type ); |
|
2259
|
|
|
|
|
|
|
|
|
2260
|
|
|
|
|
|
|
Currently, the following types of operations are supported: |
|
2261
|
|
|
|
|
|
|
|
|
2262
|
|
|
|
|
|
|
=over 8 |
|
2263
|
|
|
|
|
|
|
|
|
2264
|
|
|
|
|
|
|
=item * 'cache_operations' |
|
2265
|
|
|
|
|
|
|
|
|
2266
|
|
|
|
|
|
|
=back |
|
2267
|
|
|
|
|
|
|
|
|
2268
|
|
|
|
|
|
|
=back |
|
2269
|
|
|
|
|
|
|
|
|
2270
|
|
|
|
|
|
|
=cut |
|
2271
|
|
|
|
|
|
|
|
|
2272
|
|
|
|
|
|
|
sub is_verbose |
|
2273
|
|
|
|
|
|
|
{ |
|
2274
|
0
|
|
|
0
|
1
|
0
|
my ( $self, $specific_area ) = @_; |
|
2275
|
|
|
|
|
|
|
|
|
2276
|
0
|
|
|
|
|
0
|
my $cached_static_class_info = $self->cached_static_class_info(); |
|
2277
|
|
|
|
|
|
|
|
|
2278
|
0
|
0
|
|
|
|
0
|
if ( defined( $specific_area ) ) |
|
2279
|
|
|
|
|
|
|
{ |
|
2280
|
0
|
|
|
|
|
0
|
my $info_key = 'verbose_' . $specific_area; |
|
2281
|
|
|
|
|
|
|
|
|
2282
|
0
|
0
|
|
|
|
0
|
croak "'$specific_area' is not valid" |
|
2283
|
|
|
|
|
|
|
if ! exists( $cached_static_class_info->{ $info_key } ); |
|
2284
|
|
|
|
|
|
|
|
|
2285
|
0
|
|
|
|
|
0
|
return $cached_static_class_info->get( $info_key ); |
|
2286
|
|
|
|
|
|
|
} |
|
2287
|
|
|
|
|
|
|
else |
|
2288
|
|
|
|
|
|
|
{ |
|
2289
|
0
|
|
|
|
|
0
|
return $cached_static_class_info->get('verbose'); |
|
2290
|
|
|
|
|
|
|
} |
|
2291
|
|
|
|
|
|
|
} |
|
2292
|
|
|
|
|
|
|
|
|
2293
|
|
|
|
|
|
|
|
|
2294
|
|
|
|
|
|
|
=head1 CACHE RELATED METHODS |
|
2295
|
|
|
|
|
|
|
|
|
2296
|
|
|
|
|
|
|
|
|
2297
|
|
|
|
|
|
|
=head2 cached_static_class_info() |
|
2298
|
|
|
|
|
|
|
|
|
2299
|
|
|
|
|
|
|
Return a cached version of the information retrieved by C. |
|
2300
|
|
|
|
|
|
|
|
|
2301
|
|
|
|
|
|
|
my $static_class_info = $class->cached_static_class_info(); |
|
2302
|
|
|
|
|
|
|
my $static_class_info = $object->cached_static_class_info(); |
|
2303
|
|
|
|
|
|
|
|
|
2304
|
|
|
|
|
|
|
=cut |
|
2305
|
|
|
|
|
|
|
|
|
2306
|
|
|
|
|
|
|
{ |
|
2307
|
|
|
|
|
|
|
my $CACHE = {}; |
|
2308
|
|
|
|
|
|
|
sub cached_static_class_info |
|
2309
|
|
|
|
|
|
|
{ |
|
2310
|
963
|
|
|
963
|
1
|
2271
|
my ( $self ) = @_; |
|
2311
|
963
|
|
66
|
|
|
4678
|
my $class = ref( $self ) || $self; |
|
2312
|
|
|
|
|
|
|
|
|
2313
|
963
|
|
66
|
|
|
4010
|
$CACHE->{ $class } ||= $class->static_class_info(); |
|
2314
|
|
|
|
|
|
|
|
|
2315
|
963
|
|
|
|
|
8058
|
return $CACHE->{ $class } |
|
2316
|
|
|
|
|
|
|
} |
|
2317
|
|
|
|
|
|
|
} |
|
2318
|
|
|
|
|
|
|
|
|
2319
|
|
|
|
|
|
|
|
|
2320
|
|
|
|
|
|
|
=head2 get_table_schema() |
|
2321
|
|
|
|
|
|
|
|
|
2322
|
|
|
|
|
|
|
Return the schema corresponding to the underlying table. |
|
2323
|
|
|
|
|
|
|
|
|
2324
|
|
|
|
|
|
|
my $table_schema = $class->get_table_schema(); |
|
2325
|
|
|
|
|
|
|
my $table_schema = $object->get_table_schema(); |
|
2326
|
|
|
|
|
|
|
|
|
2327
|
|
|
|
|
|
|
=cut |
|
2328
|
|
|
|
|
|
|
|
|
2329
|
|
|
|
|
|
|
{ |
|
2330
|
|
|
|
|
|
|
my $TABLE_SCHEMAS_CACHE = {}; |
|
2331
|
|
|
|
|
|
|
sub get_table_schema |
|
2332
|
|
|
|
|
|
|
{ |
|
2333
|
1
|
|
|
1
|
1
|
2006
|
my ( $self ) = @_; |
|
2334
|
1
|
|
33
|
|
|
11
|
my $class = ref( $self ) || $self; |
|
2335
|
|
|
|
|
|
|
|
|
2336
|
1
|
50
|
|
|
|
6
|
if ( !defined( $TABLE_SCHEMAS_CACHE->{ $class } ) ) |
|
2337
|
|
|
|
|
|
|
{ |
|
2338
|
1
|
|
|
|
|
14
|
my $dbh = $class->assert_dbh(); |
|
2339
|
1
|
|
|
|
|
8
|
my $table_name = $self->get_info('table_name'); |
|
2340
|
|
|
|
|
|
|
|
|
2341
|
1
|
|
|
|
|
6
|
Class::Load::load_class( 'DBIx::NinjaORM::Schema::Table' ); |
|
2342
|
1
|
|
|
|
|
64
|
my $table_schema = DBIx::NinjaORM::Schema::Table->new( |
|
2343
|
|
|
|
|
|
|
name => $table_name, |
|
2344
|
|
|
|
|
|
|
dbh => $self->assert_dbh(), |
|
2345
|
|
|
|
|
|
|
); |
|
2346
|
1
|
|
|
|
|
4
|
$table_schema->get_columns(); |
|
2347
|
1
|
|
|
|
|
6
|
$TABLE_SCHEMAS_CACHE->{ $class } = $table_schema; |
|
2348
|
|
|
|
|
|
|
|
|
2349
|
1
|
50
|
|
|
|
15
|
croak "Failed to load schema for '$table_name'" |
|
2350
|
|
|
|
|
|
|
if !defined( $TABLE_SCHEMAS_CACHE->{ $class } ); |
|
2351
|
|
|
|
|
|
|
} |
|
2352
|
|
|
|
|
|
|
|
|
2353
|
1
|
|
|
|
|
14
|
return $TABLE_SCHEMAS_CACHE->{ $class }; |
|
2354
|
|
|
|
|
|
|
} |
|
2355
|
|
|
|
|
|
|
} |
|
2356
|
|
|
|
|
|
|
|
|
2357
|
|
|
|
|
|
|
|
|
2358
|
|
|
|
|
|
|
=head2 delete_cache() |
|
2359
|
|
|
|
|
|
|
|
|
2360
|
|
|
|
|
|
|
Delete a key from the cache. |
|
2361
|
|
|
|
|
|
|
|
|
2362
|
|
|
|
|
|
|
my $value = $class->delete_cache( key => $key ); |
|
2363
|
|
|
|
|
|
|
|
|
2364
|
|
|
|
|
|
|
=cut |
|
2365
|
|
|
|
|
|
|
|
|
2366
|
|
|
|
|
|
|
sub delete_cache |
|
2367
|
|
|
|
|
|
|
{ |
|
2368
|
0
|
|
|
0
|
1
|
0
|
my ( $self, %args ) = @_; |
|
2369
|
0
|
|
|
|
|
0
|
my $key = delete( $args{'key'} ); |
|
2370
|
0
|
0
|
|
|
|
0
|
croak 'Invalid argument(s): ' . join( ', ', keys %args ) |
|
2371
|
|
|
|
|
|
|
if scalar( keys %args ) != 0; |
|
2372
|
|
|
|
|
|
|
|
|
2373
|
|
|
|
|
|
|
# Check parameters. |
|
2374
|
0
|
0
|
0
|
|
|
0
|
croak 'The parameter "key" is mandatory' |
|
2375
|
|
|
|
|
|
|
if !defined( $key ) || $key !~ /\w/; |
|
2376
|
|
|
|
|
|
|
|
|
2377
|
0
|
|
|
|
|
0
|
my $memcache = $self->get_info('memcache'); |
|
2378
|
|
|
|
|
|
|
return undef |
|
2379
|
0
|
0
|
|
|
|
0
|
if !defined( $memcache ); |
|
2380
|
|
|
|
|
|
|
|
|
2381
|
0
|
|
|
|
|
0
|
return $memcache->delete( $key ); |
|
2382
|
|
|
|
|
|
|
} |
|
2383
|
|
|
|
|
|
|
|
|
2384
|
|
|
|
|
|
|
|
|
2385
|
|
|
|
|
|
|
=head2 get_cache() |
|
2386
|
|
|
|
|
|
|
|
|
2387
|
|
|
|
|
|
|
Get a value from the cache. |
|
2388
|
|
|
|
|
|
|
|
|
2389
|
|
|
|
|
|
|
my $value = $class->get_cache( key => $key ); |
|
2390
|
|
|
|
|
|
|
|
|
2391
|
|
|
|
|
|
|
=cut |
|
2392
|
|
|
|
|
|
|
|
|
2393
|
|
|
|
|
|
|
sub get_cache |
|
2394
|
|
|
|
|
|
|
{ |
|
2395
|
0
|
|
|
0
|
1
|
0
|
my ( $self, %args ) = @_; |
|
2396
|
0
|
|
|
|
|
0
|
my $key = delete( $args{'key'} ); |
|
2397
|
0
|
0
|
|
|
|
0
|
croak 'Invalid argument(s): ' . join( ', ', keys %args ) |
|
2398
|
|
|
|
|
|
|
if scalar( keys %args ) != 0; |
|
2399
|
|
|
|
|
|
|
|
|
2400
|
|
|
|
|
|
|
# Check parameters. |
|
2401
|
0
|
0
|
0
|
|
|
0
|
croak 'The parameter "key" is mandatory' |
|
2402
|
|
|
|
|
|
|
if !defined( $key ) || $key !~ /\w/; |
|
2403
|
|
|
|
|
|
|
|
|
2404
|
0
|
|
|
|
|
0
|
my $memcache = $self->get_info('memcache'); |
|
2405
|
|
|
|
|
|
|
return undef |
|
2406
|
0
|
0
|
|
|
|
0
|
if !defined( $memcache ); |
|
2407
|
|
|
|
|
|
|
|
|
2408
|
0
|
|
|
|
|
0
|
return $memcache->get( $key ); |
|
2409
|
|
|
|
|
|
|
} |
|
2410
|
|
|
|
|
|
|
|
|
2411
|
|
|
|
|
|
|
|
|
2412
|
|
|
|
|
|
|
=head2 get_object_cache_key() |
|
2413
|
|
|
|
|
|
|
|
|
2414
|
|
|
|
|
|
|
Return the name of the cache key for an object or a class, given a field name |
|
2415
|
|
|
|
|
|
|
on which a unique constraint exists and the corresponding value. |
|
2416
|
|
|
|
|
|
|
|
|
2417
|
|
|
|
|
|
|
my $cache_key = $object->get_object_cache_key(); |
|
2418
|
|
|
|
|
|
|
my $cache_key = $class->get_object_cache_key( |
|
2419
|
|
|
|
|
|
|
unique_field => $unique_field, |
|
2420
|
|
|
|
|
|
|
value => $value, |
|
2421
|
|
|
|
|
|
|
); |
|
2422
|
|
|
|
|
|
|
|
|
2423
|
|
|
|
|
|
|
=cut |
|
2424
|
|
|
|
|
|
|
|
|
2425
|
|
|
|
|
|
|
sub get_object_cache_key |
|
2426
|
|
|
|
|
|
|
{ |
|
2427
|
0
|
|
|
0
|
1
|
0
|
my ( $self, %args ) = @_; |
|
2428
|
0
|
|
|
|
|
0
|
my $unique_field = delete( $args{'unique_field'} ); |
|
2429
|
0
|
|
|
|
|
0
|
my $value = delete( $args{'value'} ); |
|
2430
|
|
|
|
|
|
|
|
|
2431
|
|
|
|
|
|
|
# Retrieve the field we'll use to create the cache key. |
|
2432
|
0
|
|
|
|
|
0
|
my $cache_key_field = $self->get_cache_key_field(); |
|
2433
|
0
|
0
|
|
|
|
0
|
croak 'No cache key found for class' |
|
2434
|
|
|
|
|
|
|
if !defined( $cache_key_field ); |
|
2435
|
|
|
|
|
|
|
|
|
2436
|
0
|
|
|
|
|
0
|
my $table_name = $self->get_info('table_name'); |
|
2437
|
0
|
0
|
|
|
|
0
|
if ( defined( $unique_field ) ) |
|
2438
|
|
|
|
|
|
|
{ |
|
2439
|
0
|
0
|
|
|
|
0
|
if ( !defined( $value ) ) |
|
2440
|
|
|
|
|
|
|
{ |
|
2441
|
0
|
|
|
|
|
0
|
$log->debugf( |
|
2442
|
|
|
|
|
|
|
"Passed unique field '%s' without a corresponding value for " |
|
2443
|
|
|
|
|
|
|
. "table '%s', cannot determine cache key", |
|
2444
|
|
|
|
|
|
|
$unique_field, |
|
2445
|
|
|
|
|
|
|
$table_name, |
|
2446
|
|
|
|
|
|
|
); |
|
2447
|
0
|
|
|
|
|
0
|
return; |
|
2448
|
|
|
|
|
|
|
} |
|
2449
|
|
|
|
|
|
|
|
|
2450
|
|
|
|
|
|
|
# 'id' is only an alias and needs to be expanded to its actual name. |
|
2451
|
0
|
0
|
|
|
|
0
|
$unique_field = $self->get_info('primary_key_name') |
|
2452
|
|
|
|
|
|
|
if $unique_field eq 'id'; |
|
2453
|
|
|
|
|
|
|
} |
|
2454
|
|
|
|
|
|
|
else |
|
2455
|
|
|
|
|
|
|
{ |
|
2456
|
|
|
|
|
|
|
# If no unique field was passed, use the $cache_key_field field and its |
|
2457
|
|
|
|
|
|
|
# corresponding value. |
|
2458
|
0
|
0
|
|
|
|
0
|
if ( Data::Validate::Type::is_hashref( $self ) ) |
|
2459
|
|
|
|
|
|
|
{ |
|
2460
|
0
|
|
|
|
|
0
|
$unique_field = $cache_key_field; |
|
2461
|
0
|
|
|
|
|
0
|
$value = $self->{ $unique_field }; |
|
2462
|
|
|
|
|
|
|
|
|
2463
|
0
|
0
|
|
|
|
0
|
unless ( defined( $value ) ) |
|
2464
|
|
|
|
|
|
|
{ |
|
2465
|
0
|
|
|
|
|
0
|
$log->debugf( |
|
2466
|
|
|
|
|
|
|
"Trying to use field '%s' on table '%s' to generate " |
|
2467
|
|
|
|
|
|
|
. "a cache key, but the value for that field on the " |
|
2468
|
|
|
|
|
|
|
. "object is undef", |
|
2469
|
|
|
|
|
|
|
$cache_key_field, |
|
2470
|
|
|
|
|
|
|
$table_name, |
|
2471
|
|
|
|
|
|
|
); |
|
2472
|
0
|
|
|
|
|
0
|
return; |
|
2473
|
|
|
|
|
|
|
} |
|
2474
|
|
|
|
|
|
|
} |
|
2475
|
|
|
|
|
|
|
else |
|
2476
|
|
|
|
|
|
|
{ |
|
2477
|
0
|
|
|
|
|
0
|
$log->debug( |
|
2478
|
|
|
|
|
|
|
"If you don't specify a unique field and value, you need to " |
|
2479
|
|
|
|
|
|
|
. "call this function on an object" |
|
2480
|
|
|
|
|
|
|
); |
|
2481
|
0
|
|
|
|
|
0
|
return; |
|
2482
|
|
|
|
|
|
|
} |
|
2483
|
|
|
|
|
|
|
} |
|
2484
|
|
|
|
|
|
|
|
|
2485
|
|
|
|
|
|
|
# If the unique field passed doesn't match what the cache key is, we need |
|
2486
|
|
|
|
|
|
|
# to do a database lookup to find out the corresponding cache key. |
|
2487
|
0
|
|
|
|
|
0
|
my $cache_key_value; |
|
2488
|
0
|
0
|
|
|
|
0
|
if ( $unique_field ne $cache_key_field ) |
|
2489
|
|
|
|
|
|
|
{ |
|
2490
|
0
|
|
|
|
|
0
|
my $dbh = $self->assert_dbh(); |
|
2491
|
|
|
|
|
|
|
|
|
2492
|
0
|
|
|
|
|
0
|
$cache_key_value = $dbh->selectrow_arrayref( |
|
2493
|
|
|
|
|
|
|
sprintf( |
|
2494
|
|
|
|
|
|
|
q| |
|
2495
|
|
|
|
|
|
|
SELECT %s |
|
2496
|
|
|
|
|
|
|
FROM %s |
|
2497
|
|
|
|
|
|
|
WHERE %s = ? |
|
2498
|
|
|
|
|
|
|
|, |
|
2499
|
|
|
|
|
|
|
$dbh->quote_identifier( $cache_key_field ), |
|
2500
|
|
|
|
|
|
|
$dbh->quote_identifier( $table_name ), |
|
2501
|
|
|
|
|
|
|
$dbh->quote_identifier( $unique_field ), |
|
2502
|
|
|
|
|
|
|
), |
|
2503
|
|
|
|
|
|
|
{}, |
|
2504
|
|
|
|
|
|
|
$value, |
|
2505
|
|
|
|
|
|
|
); |
|
2506
|
|
|
|
|
|
|
|
|
2507
|
0
|
0
|
0
|
|
|
0
|
$cache_key_value = defined( $cache_key_value ) && scalar( @$cache_key_value ) != 0 |
|
2508
|
|
|
|
|
|
|
? $cache_key_value->[0] |
|
2509
|
|
|
|
|
|
|
: undef; |
|
2510
|
|
|
|
|
|
|
|
|
2511
|
0
|
0
|
|
|
|
0
|
unless ( defined( $cache_key_value ) ) |
|
2512
|
|
|
|
|
|
|
{ |
|
2513
|
0
|
0
|
|
|
|
0
|
$log->debugf( |
|
2514
|
|
|
|
|
|
|
"Cache miss for unique field '%s' and value '%s' on table " |
|
2515
|
|
|
|
|
|
|
. "'%s', cannot generate cache key.", |
|
2516
|
|
|
|
|
|
|
$unique_field, |
|
2517
|
|
|
|
|
|
|
$value, |
|
2518
|
|
|
|
|
|
|
$table_name, |
|
2519
|
|
|
|
|
|
|
) if $self->is_verbose(); |
|
2520
|
0
|
|
|
|
|
0
|
return; |
|
2521
|
|
|
|
|
|
|
} |
|
2522
|
|
|
|
|
|
|
} |
|
2523
|
|
|
|
|
|
|
else |
|
2524
|
|
|
|
|
|
|
{ |
|
2525
|
0
|
|
|
|
|
0
|
$cache_key_value = $value; |
|
2526
|
|
|
|
|
|
|
} |
|
2527
|
|
|
|
|
|
|
|
|
2528
|
0
|
|
|
|
|
0
|
return lc( 'object|' . $table_name . '|' . $cache_key_field . '|' . $cache_key_value ); |
|
2529
|
|
|
|
|
|
|
} |
|
2530
|
|
|
|
|
|
|
|
|
2531
|
|
|
|
|
|
|
|
|
2532
|
|
|
|
|
|
|
=head2 invalidate_cached_object() |
|
2533
|
|
|
|
|
|
|
|
|
2534
|
|
|
|
|
|
|
Invalidate the cached copies of the current object across all the unique |
|
2535
|
|
|
|
|
|
|
keys this object can be referenced with. |
|
2536
|
|
|
|
|
|
|
|
|
2537
|
|
|
|
|
|
|
$object->invalidate_cached_object(); |
|
2538
|
|
|
|
|
|
|
|
|
2539
|
|
|
|
|
|
|
=cut |
|
2540
|
|
|
|
|
|
|
|
|
2541
|
|
|
|
|
|
|
sub invalidate_cached_object |
|
2542
|
|
|
|
|
|
|
{ |
|
2543
|
0
|
|
|
0
|
1
|
0
|
my ( $self ) = @_; |
|
2544
|
|
|
|
|
|
|
|
|
2545
|
0
|
|
|
|
|
0
|
my $primary_key_name = $self->get_info('primary_key_name'); |
|
2546
|
0
|
0
|
|
|
|
0
|
if ( defined( $primary_key_name ) ) |
|
2547
|
|
|
|
|
|
|
{ |
|
2548
|
0
|
|
|
|
|
0
|
my $cache_key = $self->get_object_cache_key( |
|
2549
|
|
|
|
|
|
|
unique_field => 'id', |
|
2550
|
|
|
|
|
|
|
value => $self->id(), |
|
2551
|
|
|
|
|
|
|
); |
|
2552
|
0
|
0
|
|
|
|
0
|
$self->delete_cache( key => $cache_key ) |
|
2553
|
|
|
|
|
|
|
if defined( $cache_key ); |
|
2554
|
|
|
|
|
|
|
} |
|
2555
|
|
|
|
|
|
|
|
|
2556
|
0
|
|
0
|
|
|
0
|
foreach my $field ( @{ $self->get_info('unique_fields') // [] } ) |
|
|
0
|
|
|
|
|
0
|
|
|
2557
|
|
|
|
|
|
|
{ |
|
2558
|
|
|
|
|
|
|
# If the object has no value for the unique field, it wasn't |
|
2559
|
|
|
|
|
|
|
# cached for this key/value pair and we can't build a cache key |
|
2560
|
|
|
|
|
|
|
# for it anyway, so we just skip to the next unique field. |
|
2561
|
0
|
0
|
|
|
|
0
|
next unless defined( $self->{ $field } ); |
|
2562
|
|
|
|
|
|
|
|
|
2563
|
0
|
|
|
|
|
0
|
my $cache_key = $self->get_object_cache_key( |
|
2564
|
|
|
|
|
|
|
unique_field => $field, |
|
2565
|
|
|
|
|
|
|
value => $self->{ $field }, |
|
2566
|
|
|
|
|
|
|
); |
|
2567
|
0
|
0
|
|
|
|
0
|
$self->delete_cache( key => $cache_key ) |
|
2568
|
|
|
|
|
|
|
if defined( $cache_key ); |
|
2569
|
|
|
|
|
|
|
} |
|
2570
|
|
|
|
|
|
|
|
|
2571
|
0
|
|
|
|
|
0
|
return 1; |
|
2572
|
|
|
|
|
|
|
} |
|
2573
|
|
|
|
|
|
|
|
|
2574
|
|
|
|
|
|
|
|
|
2575
|
|
|
|
|
|
|
=head2 retrieve_list_cache() |
|
2576
|
|
|
|
|
|
|
|
|
2577
|
|
|
|
|
|
|
Dispatch of retrieve_list() when objects should be retrieved from the cache. |
|
2578
|
|
|
|
|
|
|
|
|
2579
|
|
|
|
|
|
|
See C for the parameters this method accepts. |
|
2580
|
|
|
|
|
|
|
|
|
2581
|
|
|
|
|
|
|
=cut |
|
2582
|
|
|
|
|
|
|
|
|
2583
|
|
|
|
|
|
|
sub retrieve_list_cache ## no critic (Subroutines::ProhibitExcessComplexity) |
|
2584
|
|
|
|
|
|
|
{ |
|
2585
|
0
|
|
|
0
|
1
|
0
|
my ( $class, $filters, %args ) = @_; |
|
2586
|
0
|
|
|
|
|
0
|
my $list_cache_time = $class->get_info('list_cache_time'); |
|
2587
|
0
|
|
|
|
|
0
|
my $object_cache_time = $class->get_info('object_cache_time'); |
|
2588
|
0
|
|
|
|
|
0
|
my $primary_key_name = $class->get_info('primary_key_name'); |
|
2589
|
|
|
|
|
|
|
|
|
2590
|
|
|
|
|
|
|
# Create a unique cache key. |
|
2591
|
0
|
|
|
|
|
0
|
my $list_cache_keys = []; |
|
2592
|
0
|
|
|
|
|
0
|
foreach my $filter ( keys %$filters ) |
|
2593
|
|
|
|
|
|
|
{ |
|
2594
|
|
|
|
|
|
|
# Force all arguments into lower case for purposes of caching. |
|
2595
|
0
|
|
|
|
|
0
|
push( @$list_cache_keys, [ lc( $filter ), $filters->{ $filter } ] ); |
|
2596
|
|
|
|
|
|
|
} |
|
2597
|
0
|
|
|
|
|
0
|
foreach my $arg ( sort keys %args ) |
|
2598
|
|
|
|
|
|
|
{ |
|
2599
|
|
|
|
|
|
|
# Those arguments don't have an impact on the filters to IDs translation, |
|
2600
|
|
|
|
|
|
|
# so we can exclude them from the unique cache key. |
|
2601
|
0
|
|
|
|
|
0
|
my $has_impact = $RETRIEVE_LIST_VALID_ARGUMENTS->{ $arg }; |
|
2602
|
0
|
0
|
|
|
|
0
|
croak "The argument '$arg' is not valid" |
|
2603
|
|
|
|
|
|
|
if !defined( $has_impact ); |
|
2604
|
0
|
0
|
|
|
|
0
|
next if !$has_impact; |
|
2605
|
|
|
|
|
|
|
|
|
2606
|
|
|
|
|
|
|
# Force all arguments into lower case for purposes of caching. |
|
2607
|
0
|
|
|
|
|
0
|
push( @$list_cache_keys, [ lc( $arg ), $args{ $arg } ] ); |
|
2608
|
|
|
|
|
|
|
} |
|
2609
|
|
|
|
|
|
|
|
|
2610
|
0
|
|
|
|
|
0
|
my $list_cache_key = MIME::Base64::encode_base64( Storable::freeze( $list_cache_keys ) ); |
|
2611
|
0
|
|
|
|
|
0
|
chomp( $list_cache_key ); |
|
2612
|
0
|
|
|
|
|
0
|
my $list_cache_key_sha1 = Digest::SHA1::sha1_base64( $list_cache_key ); |
|
2613
|
|
|
|
|
|
|
|
|
2614
|
|
|
|
|
|
|
# Find out if the parameters are searching by ID or using a unique field. |
|
2615
|
0
|
|
|
|
|
0
|
my $search_field; |
|
2616
|
|
|
|
|
|
|
my $list_of_search_values; |
|
2617
|
0
|
|
0
|
|
|
0
|
foreach my $field ( 'id', @{ $class->get_info('unique_fields') // [] } ) |
|
|
0
|
|
|
|
|
0
|
|
|
2618
|
|
|
|
|
|
|
{ |
|
2619
|
|
|
|
|
|
|
next |
|
2620
|
0
|
0
|
|
|
|
0
|
unless exists( $filters->{ $field } ); |
|
2621
|
|
|
|
|
|
|
|
|
2622
|
0
|
|
|
|
|
0
|
$search_field = $field; |
|
2623
|
|
|
|
|
|
|
|
|
2624
|
0
|
|
0
|
|
|
0
|
$list_of_search_values = Data::Validate::Type::filter_arrayref( $filters->{ $field } ) |
|
2625
|
|
|
|
|
|
|
// [ $filters->{ $field } ]; |
|
2626
|
|
|
|
|
|
|
} |
|
2627
|
|
|
|
|
|
|
|
|
2628
|
|
|
|
|
|
|
# If we're searcing by ID or unique field, those are how the objects are |
|
2629
|
|
|
|
|
|
|
# cached so we already know how to retrieve them from the object cache. |
|
2630
|
|
|
|
|
|
|
# If we're searching by anything else, then we maintain a "list cache", |
|
2631
|
|
|
|
|
|
|
# which associates retrieve_list() args with the resulting IDs. |
|
2632
|
0
|
|
|
|
|
0
|
my $pagination; |
|
2633
|
0
|
|
|
|
|
0
|
my $list_cache_used = 0; |
|
2634
|
0
|
0
|
|
|
|
0
|
if ( !defined( $search_field ) ) |
|
2635
|
|
|
|
|
|
|
{ |
|
2636
|
|
|
|
|
|
|
# Test if we have a corresponding list of IDs in the cache. |
|
2637
|
0
|
|
|
|
|
0
|
my $cache = $class->get_cache( key => $list_cache_key_sha1 ); |
|
2638
|
|
|
|
|
|
|
|
|
2639
|
0
|
0
|
|
|
|
0
|
if ( defined( $cache ) ) |
|
2640
|
|
|
|
|
|
|
{ |
|
2641
|
0
|
|
|
|
|
0
|
my $cache_content = Storable::thaw( MIME::Base64::decode_base64( $cache ) ); |
|
2642
|
0
|
|
0
|
|
|
0
|
my ( $original_list_cache_key, $original_pagination, $original_search_field, $original_list_of_ids ) = @{ Data::Validate::Type::filter_arrayref( $cache_content ) // [] }; |
|
|
0
|
|
|
|
|
0
|
|
|
2643
|
|
|
|
|
|
|
|
|
2644
|
|
|
|
|
|
|
# We need to use SHA1 due to the limitation on the length of memcache keys |
|
2645
|
|
|
|
|
|
|
# (we can't just cache $cache_key). |
|
2646
|
|
|
|
|
|
|
# However, there is a very small risk of collision so we check here that |
|
2647
|
|
|
|
|
|
|
# the cache key stored inside the cache entry is the same. |
|
2648
|
0
|
0
|
|
|
|
0
|
if ( $original_list_cache_key eq $list_cache_key ) |
|
2649
|
|
|
|
|
|
|
{ |
|
2650
|
0
|
|
|
|
|
0
|
$list_of_search_values = $original_list_of_ids; |
|
2651
|
0
|
|
|
|
|
0
|
$pagination = $original_pagination; |
|
2652
|
0
|
|
|
|
|
0
|
$search_field = $original_search_field; |
|
2653
|
0
|
|
|
|
|
0
|
$list_cache_used = 1; |
|
2654
|
|
|
|
|
|
|
} |
|
2655
|
|
|
|
|
|
|
} |
|
2656
|
|
|
|
|
|
|
} |
|
2657
|
|
|
|
|
|
|
|
|
2658
|
0
|
|
|
|
|
0
|
my $cached_objects = {}; |
|
2659
|
0
|
|
|
|
|
0
|
my $objects; |
|
2660
|
0
|
0
|
0
|
|
|
0
|
if ( !$args{'lock'} && defined( $list_of_search_values ) ) |
|
2661
|
|
|
|
|
|
|
{ |
|
2662
|
0
|
0
|
|
|
|
0
|
$log->debug( "Using values (unique/IDs) from the list cache" ) |
|
2663
|
|
|
|
|
|
|
if $class->is_verbose('cache_operations'); |
|
2664
|
|
|
|
|
|
|
|
|
2665
|
|
|
|
|
|
|
# If we're not trying to lock the underlying rows, and we have a list of |
|
2666
|
|
|
|
|
|
|
# IDs from the cache, we try to get the objects from the object cache. |
|
2667
|
0
|
|
|
|
|
0
|
my $objects_to_retrieve_from_database = {}; |
|
2668
|
0
|
|
|
|
|
0
|
foreach my $search_value ( @$list_of_search_values ) |
|
2669
|
|
|
|
|
|
|
{ |
|
2670
|
0
|
0
|
|
|
|
0
|
my $object_cache_key = $class->get_object_cache_key( |
|
2671
|
|
|
|
|
|
|
unique_field => $search_field eq 'id' |
|
2672
|
|
|
|
|
|
|
? $primary_key_name |
|
2673
|
|
|
|
|
|
|
: $search_field, |
|
2674
|
|
|
|
|
|
|
value => $search_value, |
|
2675
|
|
|
|
|
|
|
); |
|
2676
|
|
|
|
|
|
|
|
|
2677
|
0
|
0
|
|
|
|
0
|
my $object = defined( $object_cache_key ) |
|
2678
|
|
|
|
|
|
|
? $class->get_cache( key => $object_cache_key ) |
|
2679
|
|
|
|
|
|
|
: undef; |
|
2680
|
|
|
|
|
|
|
|
|
2681
|
0
|
0
|
|
|
|
0
|
if ( defined( $object ) ) |
|
2682
|
|
|
|
|
|
|
{ |
|
2683
|
0
|
0
|
|
|
|
0
|
$log->debugf( |
|
2684
|
|
|
|
|
|
|
"Retrieved '%s' from cache.", |
|
2685
|
|
|
|
|
|
|
$object_cache_key, |
|
2686
|
|
|
|
|
|
|
) if $class->is_verbose('cache_operations'); |
|
2687
|
|
|
|
|
|
|
|
|
2688
|
0
|
|
|
|
|
0
|
$object->{'_debug'}->{'object_cache_used'} = 1; |
|
2689
|
|
|
|
|
|
|
|
|
2690
|
0
|
0
|
|
|
|
0
|
my $hash_key = lc( |
|
2691
|
|
|
|
|
|
|
$search_field eq 'id' |
|
2692
|
|
|
|
|
|
|
? $object->id() |
|
2693
|
|
|
|
|
|
|
: $object->get( $search_field ) |
|
2694
|
|
|
|
|
|
|
); |
|
2695
|
|
|
|
|
|
|
|
|
2696
|
0
|
|
|
|
|
0
|
$cached_objects->{ $hash_key } = $object; |
|
2697
|
|
|
|
|
|
|
} |
|
2698
|
|
|
|
|
|
|
else |
|
2699
|
|
|
|
|
|
|
{ |
|
2700
|
0
|
0
|
|
|
|
0
|
$log->debugf( |
|
2701
|
|
|
|
|
|
|
"'%s' not found in the cache.", |
|
2702
|
|
|
|
|
|
|
$object_cache_key, |
|
2703
|
|
|
|
|
|
|
) if $class->is_verbose('cache_operations'); |
|
2704
|
|
|
|
|
|
|
|
|
2705
|
0
|
|
|
|
|
0
|
$objects_to_retrieve_from_database->{ lc( $search_value ) } = $object_cache_key; |
|
2706
|
|
|
|
|
|
|
} |
|
2707
|
|
|
|
|
|
|
} |
|
2708
|
|
|
|
|
|
|
|
|
2709
|
|
|
|
|
|
|
# If we have any ID we couldn't get an object for from the cache, we now |
|
2710
|
|
|
|
|
|
|
# go to the database. |
|
2711
|
0
|
0
|
|
|
|
0
|
if ( scalar( keys %$objects_to_retrieve_from_database ) != 0 ) |
|
2712
|
|
|
|
|
|
|
{ |
|
2713
|
0
|
0
|
|
|
|
0
|
$log->debug( |
|
2714
|
|
|
|
|
|
|
"The following objects are not cached and need to be retrieved from the database: %s", |
|
2715
|
|
|
|
|
|
|
join( ', ', keys %$objects_to_retrieve_from_database ), |
|
2716
|
|
|
|
|
|
|
) if $class->is_verbose('cache_operations'); |
|
2717
|
|
|
|
|
|
|
|
|
2718
|
|
|
|
|
|
|
# We don't want to pass %args, which has a lot of information that may |
|
2719
|
|
|
|
|
|
|
# actually conflict with what we're trying to do here. However, some of |
|
2720
|
|
|
|
|
|
|
# the arguments are important, such as 'dbh' to connect to the correct |
|
2721
|
|
|
|
|
|
|
# database. We filter here the relevant arguments. |
|
2722
|
0
|
|
|
|
|
0
|
my %local_args = |
|
2723
|
0
|
|
|
|
|
0
|
map { $_ => $args{ $_ } } |
|
2724
|
0
|
|
|
|
|
0
|
grep { defined( $args{ $_ } ) } |
|
2725
|
|
|
|
|
|
|
qw( dbh show_queries exclude_fields select_fields ); |
|
2726
|
|
|
|
|
|
|
|
|
2727
|
0
|
|
|
|
|
0
|
$objects = $class->retrieve_list_nocache( |
|
2728
|
|
|
|
|
|
|
{ |
|
2729
|
|
|
|
|
|
|
$search_field => [ keys %$objects_to_retrieve_from_database ], |
|
2730
|
|
|
|
|
|
|
}, |
|
2731
|
|
|
|
|
|
|
%local_args, |
|
2732
|
|
|
|
|
|
|
); |
|
2733
|
|
|
|
|
|
|
} |
|
2734
|
|
|
|
|
|
|
|
|
2735
|
|
|
|
|
|
|
# Indicate that we've used the list cache to retrieve the list of object |
|
2736
|
|
|
|
|
|
|
# IDs. |
|
2737
|
0
|
0
|
|
|
|
0
|
if ( $list_cache_used ) |
|
2738
|
|
|
|
|
|
|
{ |
|
2739
|
0
|
|
0
|
|
|
0
|
foreach my $object ( values %$cached_objects, @{ $objects // [] } ) |
|
|
0
|
|
|
|
|
0
|
|
|
2740
|
|
|
|
|
|
|
{ |
|
2741
|
0
|
|
|
|
|
0
|
$object->{'_debug'}->{'list_cache_used'} = 1; |
|
2742
|
|
|
|
|
|
|
} |
|
2743
|
|
|
|
|
|
|
} |
|
2744
|
|
|
|
|
|
|
} |
|
2745
|
|
|
|
|
|
|
else |
|
2746
|
|
|
|
|
|
|
{ |
|
2747
|
|
|
|
|
|
|
# If we don't have a list of IDs, we need to go to the database via |
|
2748
|
|
|
|
|
|
|
# retrieve_list_nocache() to get the objects. |
|
2749
|
0
|
|
|
|
|
0
|
( $objects, $pagination ) = $class->retrieve_list_nocache( |
|
2750
|
|
|
|
|
|
|
$filters, |
|
2751
|
|
|
|
|
|
|
%args, |
|
2752
|
|
|
|
|
|
|
); |
|
2753
|
|
|
|
|
|
|
|
|
2754
|
|
|
|
|
|
|
# Set the list cache. |
|
2755
|
0
|
|
|
|
|
0
|
my $list_cache_ids = [ map { $_->id() } @$objects ]; |
|
|
0
|
|
|
|
|
0
|
|
|
2756
|
|
|
|
|
|
|
|
|
2757
|
0
|
0
|
|
|
|
0
|
$log->debugf( |
|
2758
|
|
|
|
|
|
|
"Adding key '%s' to the list cache, with the following IDs: %s", |
|
2759
|
|
|
|
|
|
|
$list_cache_key, |
|
2760
|
|
|
|
|
|
|
join( ', ', @$list_cache_ids ), |
|
2761
|
|
|
|
|
|
|
) if $class->is_verbose('cache_operations'); |
|
2762
|
|
|
|
|
|
|
|
|
2763
|
0
|
|
|
|
|
0
|
$class->set_cache( |
|
2764
|
|
|
|
|
|
|
key => $list_cache_key_sha1, |
|
2765
|
|
|
|
|
|
|
value => MIME::Base64::encode_base64( |
|
2766
|
|
|
|
|
|
|
Storable::freeze( |
|
2767
|
|
|
|
|
|
|
[ |
|
2768
|
|
|
|
|
|
|
$list_cache_key, |
|
2769
|
|
|
|
|
|
|
$pagination, |
|
2770
|
|
|
|
|
|
|
'id', |
|
2771
|
|
|
|
|
|
|
$list_cache_ids, |
|
2772
|
|
|
|
|
|
|
] |
|
2773
|
|
|
|
|
|
|
) |
|
2774
|
|
|
|
|
|
|
), |
|
2775
|
|
|
|
|
|
|
expire_time => $list_cache_time, |
|
2776
|
|
|
|
|
|
|
); |
|
2777
|
|
|
|
|
|
|
} |
|
2778
|
|
|
|
|
|
|
|
|
2779
|
|
|
|
|
|
|
# For cache purposes, we use the search field if it is available (as it's |
|
2780
|
|
|
|
|
|
|
# either the primary key or a unique field), and we fall back on 'id' |
|
2781
|
|
|
|
|
|
|
# which exists on all objects as a primary key shortcut. |
|
2782
|
0
|
|
0
|
|
|
0
|
my $cache_field = $search_field // 'id'; |
|
2783
|
|
|
|
|
|
|
|
|
2784
|
|
|
|
|
|
|
# Cache the objects. |
|
2785
|
0
|
|
|
|
|
0
|
my $database_objects = {}; |
|
2786
|
0
|
|
|
|
|
0
|
foreach my $object ( @$objects ) |
|
2787
|
|
|
|
|
|
|
{ |
|
2788
|
0
|
0
|
|
|
|
0
|
my $hash_key = lc( |
|
2789
|
|
|
|
|
|
|
$cache_field eq 'id' |
|
2790
|
|
|
|
|
|
|
? $object->id() |
|
2791
|
|
|
|
|
|
|
: $object->get( $cache_field ) |
|
2792
|
|
|
|
|
|
|
); |
|
2793
|
|
|
|
|
|
|
|
|
2794
|
0
|
|
|
|
|
0
|
$database_objects->{ $hash_key } = $object; |
|
2795
|
|
|
|
|
|
|
|
|
2796
|
|
|
|
|
|
|
# If the caller forced excluding fields, we can't cache the objects here. |
|
2797
|
|
|
|
|
|
|
# Otherwise, we would serve incomplete objects the next time a caller |
|
2798
|
|
|
|
|
|
|
# requests objects without specifying the same excluded fields. |
|
2799
|
|
|
|
|
|
|
# Same goes for explicit fields restrictions. |
|
2800
|
|
|
|
|
|
|
next |
|
2801
|
0
|
0
|
0
|
|
|
0
|
if exists( $object->{'_excluded_fields'} ) || exists( $object->{'_selected_fields'} ); |
|
2802
|
|
|
|
|
|
|
|
|
2803
|
0
|
0
|
|
|
|
0
|
my $object_cache_key = $cache_field eq 'id' |
|
2804
|
|
|
|
|
|
|
? $object->get_object_cache_key() |
|
2805
|
|
|
|
|
|
|
: $object->get_object_cache_key( |
|
2806
|
|
|
|
|
|
|
unique_field => $cache_field, |
|
2807
|
|
|
|
|
|
|
value => $object->get( $cache_field ), |
|
2808
|
|
|
|
|
|
|
); |
|
2809
|
|
|
|
|
|
|
|
|
2810
|
0
|
0
|
|
|
|
0
|
next if !defined( $object_cache_key ); |
|
2811
|
|
|
|
|
|
|
|
|
2812
|
0
|
|
|
|
|
0
|
$object->{'_debug'}->{'cache_expires'} = time() + $object_cache_time; |
|
2813
|
|
|
|
|
|
|
|
|
2814
|
0
|
0
|
|
|
|
0
|
$log->debugf( |
|
2815
|
|
|
|
|
|
|
"Set object cache for key '%s'.", |
|
2816
|
|
|
|
|
|
|
$object_cache_key, |
|
2817
|
|
|
|
|
|
|
) if $class->is_verbose('cache_operations'); |
|
2818
|
|
|
|
|
|
|
|
|
2819
|
0
|
|
|
|
|
0
|
$class->set_cache( |
|
2820
|
|
|
|
|
|
|
key => $object_cache_key, |
|
2821
|
|
|
|
|
|
|
value => $object, |
|
2822
|
|
|
|
|
|
|
expire_time => $object_cache_time, |
|
2823
|
|
|
|
|
|
|
); |
|
2824
|
|
|
|
|
|
|
} |
|
2825
|
|
|
|
|
|
|
|
|
2826
|
|
|
|
|
|
|
# Make sure the objects are sorted. |
|
2827
|
0
|
|
|
|
|
0
|
my $sorted_objects; |
|
2828
|
0
|
0
|
|
|
|
0
|
if ( defined( $list_of_search_values ) ) |
|
2829
|
|
|
|
|
|
|
{ |
|
2830
|
|
|
|
|
|
|
# If we've been using a list of IDs from the cache, we need to merge |
|
2831
|
|
|
|
|
|
|
# the objects and sort them. |
|
2832
|
0
|
|
|
|
|
0
|
$sorted_objects = []; |
|
2833
|
0
|
|
|
|
|
0
|
foreach my $search_value ( @$list_of_search_values ) |
|
2834
|
|
|
|
|
|
|
{ |
|
2835
|
0
|
0
|
|
|
|
0
|
if ( exists( $cached_objects->{ lc( $search_value ) } ) ) |
|
|
|
0
|
|
|
|
|
|
|
2836
|
|
|
|
|
|
|
{ |
|
2837
|
0
|
|
|
|
|
0
|
push( @$sorted_objects, $cached_objects->{ lc( $search_value ) } ); |
|
2838
|
|
|
|
|
|
|
} |
|
2839
|
|
|
|
|
|
|
elsif ( exists( $database_objects->{ lc( $search_value ) } ) ) |
|
2840
|
|
|
|
|
|
|
{ |
|
2841
|
0
|
|
|
|
|
0
|
push( @$sorted_objects, $database_objects->{ lc( $search_value ) } ); |
|
2842
|
|
|
|
|
|
|
} |
|
2843
|
|
|
|
|
|
|
else |
|
2844
|
|
|
|
|
|
|
{ |
|
2845
|
0
|
|
|
|
|
0
|
$log->debugf( |
|
2846
|
|
|
|
|
|
|
'Failed to retrieve object for %s=%s', |
|
2847
|
|
|
|
|
|
|
$cache_field, |
|
2848
|
|
|
|
|
|
|
$search_value, |
|
2849
|
|
|
|
|
|
|
); |
|
2850
|
|
|
|
|
|
|
} |
|
2851
|
|
|
|
|
|
|
} |
|
2852
|
|
|
|
|
|
|
} |
|
2853
|
|
|
|
|
|
|
else |
|
2854
|
|
|
|
|
|
|
{ |
|
2855
|
|
|
|
|
|
|
# Otherwise, $object comes from the database and is already sorted by |
|
2856
|
|
|
|
|
|
|
# retrieve_list_nocache(). |
|
2857
|
0
|
|
|
|
|
0
|
$sorted_objects = $objects; |
|
2858
|
|
|
|
|
|
|
} |
|
2859
|
|
|
|
|
|
|
|
|
2860
|
|
|
|
|
|
|
# Return the objects, taking into account whether pagination is requested. |
|
2861
|
0
|
0
|
0
|
|
|
0
|
if ( wantarray && defined( $args{'pagination'} ) ) |
|
2862
|
|
|
|
|
|
|
{ |
|
2863
|
0
|
|
|
|
|
0
|
return ( $sorted_objects, $pagination ); |
|
2864
|
|
|
|
|
|
|
} |
|
2865
|
|
|
|
|
|
|
else |
|
2866
|
|
|
|
|
|
|
{ |
|
2867
|
0
|
|
|
|
|
0
|
return $sorted_objects; |
|
2868
|
|
|
|
|
|
|
} |
|
2869
|
|
|
|
|
|
|
} |
|
2870
|
|
|
|
|
|
|
|
|
2871
|
|
|
|
|
|
|
|
|
2872
|
|
|
|
|
|
|
=head2 set_cache() |
|
2873
|
|
|
|
|
|
|
|
|
2874
|
|
|
|
|
|
|
Set a value into the cache. |
|
2875
|
|
|
|
|
|
|
|
|
2876
|
|
|
|
|
|
|
$class->set_cache( |
|
2877
|
|
|
|
|
|
|
key => $key, |
|
2878
|
|
|
|
|
|
|
value => $value, |
|
2879
|
|
|
|
|
|
|
expire_time => $expire_time, |
|
2880
|
|
|
|
|
|
|
); |
|
2881
|
|
|
|
|
|
|
|
|
2882
|
|
|
|
|
|
|
=cut |
|
2883
|
|
|
|
|
|
|
|
|
2884
|
|
|
|
|
|
|
sub set_cache |
|
2885
|
|
|
|
|
|
|
{ |
|
2886
|
0
|
|
|
0
|
1
|
0
|
my ( $self, %args ) = @_; |
|
2887
|
0
|
|
|
|
|
0
|
my $key = delete( $args{'key'} ); |
|
2888
|
0
|
|
|
|
|
0
|
my $value = delete( $args{'value'} ); |
|
2889
|
0
|
|
|
|
|
0
|
my $expire_time = delete( $args{'expire_time'} ); |
|
2890
|
0
|
0
|
|
|
|
0
|
croak 'Invalid argument(s): ' . join( ', ', keys %args ) |
|
2891
|
|
|
|
|
|
|
if scalar( keys %args ) != 0; |
|
2892
|
|
|
|
|
|
|
|
|
2893
|
|
|
|
|
|
|
# Check parameters. |
|
2894
|
0
|
0
|
0
|
|
|
0
|
croak 'The argument "key" is mandatory' |
|
2895
|
|
|
|
|
|
|
if !defined( $key ) || $key !~ /\w/; |
|
2896
|
0
|
0
|
|
|
|
0
|
croak 'The argument "value" is mandatory' |
|
2897
|
|
|
|
|
|
|
if !defined( $value ); |
|
2898
|
|
|
|
|
|
|
|
|
2899
|
0
|
|
|
|
|
0
|
my $memcache = $self->get_info('memcache'); |
|
2900
|
|
|
|
|
|
|
return |
|
2901
|
0
|
0
|
|
|
|
0
|
if !defined( $memcache ); |
|
2902
|
|
|
|
|
|
|
|
|
2903
|
0
|
0
|
|
|
|
0
|
$memcache->set( $key, $value, $expire_time ) |
|
2904
|
|
|
|
|
|
|
|| $log->errorf( "Failed to set cache with key '%s'.", $key ); |
|
2905
|
|
|
|
|
|
|
|
|
2906
|
0
|
|
|
|
|
0
|
return; |
|
2907
|
|
|
|
|
|
|
} |
|
2908
|
|
|
|
|
|
|
|
|
2909
|
|
|
|
|
|
|
|
|
2910
|
|
|
|
|
|
|
=head1 INTERNAL METHODS |
|
2911
|
|
|
|
|
|
|
|
|
2912
|
|
|
|
|
|
|
Those methods are used internally by L, you should not subclass |
|
2913
|
|
|
|
|
|
|
them. |
|
2914
|
|
|
|
|
|
|
|
|
2915
|
|
|
|
|
|
|
|
|
2916
|
|
|
|
|
|
|
=head2 assert_dbh() |
|
2917
|
|
|
|
|
|
|
|
|
2918
|
|
|
|
|
|
|
Assert that there is a database handle, either a specific one passed as first |
|
2919
|
|
|
|
|
|
|
argument to this function (if defined) or the default one specified via |
|
2920
|
|
|
|
|
|
|
C, and return it. |
|
2921
|
|
|
|
|
|
|
|
|
2922
|
|
|
|
|
|
|
my $dbh = $class->assert_dbh(); |
|
2923
|
|
|
|
|
|
|
my $dbh = $object->assert_dbh(); |
|
2924
|
|
|
|
|
|
|
|
|
2925
|
|
|
|
|
|
|
my $dbh = $class->assert_dbh( $custom_dbh ); |
|
2926
|
|
|
|
|
|
|
my $dbh = $object->assert_dbh( $custom_dbh ); |
|
2927
|
|
|
|
|
|
|
|
|
2928
|
|
|
|
|
|
|
Note that this method also supports coderefs that return a C object |
|
2929
|
|
|
|
|
|
|
when evaluated. That way, if no database connection is needed when running the |
|
2930
|
|
|
|
|
|
|
code, no connection needs to be established. |
|
2931
|
|
|
|
|
|
|
|
|
2932
|
|
|
|
|
|
|
=cut |
|
2933
|
|
|
|
|
|
|
|
|
2934
|
|
|
|
|
|
|
sub assert_dbh |
|
2935
|
|
|
|
|
|
|
{ |
|
2936
|
158
|
|
|
158
|
1
|
43644
|
my ( $class, $specific_dbh ) = @_; |
|
2937
|
|
|
|
|
|
|
|
|
2938
|
158
|
|
|
|
|
272
|
my ( $dbh, $type ); |
|
2939
|
158
|
100
|
|
|
|
907
|
if ( defined( $specific_dbh ) ) |
|
2940
|
|
|
|
|
|
|
{ |
|
2941
|
3
|
|
|
|
|
7
|
$dbh = $specific_dbh; |
|
2942
|
3
|
|
|
|
|
9
|
$type = 'specified'; |
|
2943
|
|
|
|
|
|
|
} |
|
2944
|
|
|
|
|
|
|
else |
|
2945
|
|
|
|
|
|
|
{ |
|
2946
|
155
|
|
|
|
|
807
|
$dbh = $class->get_info('default_dbh'); |
|
2947
|
155
|
|
|
|
|
825
|
$type = 'default'; |
|
2948
|
|
|
|
|
|
|
} |
|
2949
|
|
|
|
|
|
|
|
|
2950
|
158
|
100
|
|
|
|
900
|
$dbh = $dbh->() |
|
2951
|
|
|
|
|
|
|
if Data::Validate::Type::is_coderef( $dbh ); |
|
2952
|
|
|
|
|
|
|
|
|
2953
|
158
|
100
|
|
|
|
3050
|
croak "The $type database handle is not a valid DBI::db object (" . ref( $dbh ) . ')' |
|
2954
|
|
|
|
|
|
|
if !Data::Validate::Type::is_instance( $dbh, class => 'DBI::db' ); |
|
2955
|
|
|
|
|
|
|
|
|
2956
|
153
|
|
|
|
|
4467
|
return $dbh; |
|
2957
|
|
|
|
|
|
|
} |
|
2958
|
|
|
|
|
|
|
|
|
2959
|
|
|
|
|
|
|
|
|
2960
|
|
|
|
|
|
|
=head2 build_filtering_clause() |
|
2961
|
|
|
|
|
|
|
|
|
2962
|
|
|
|
|
|
|
Create a filtering clause using the field, operator and values passed. |
|
2963
|
|
|
|
|
|
|
|
|
2964
|
|
|
|
|
|
|
my ( $clause, $clause_values ) = $class->build_filtering_clause( |
|
2965
|
|
|
|
|
|
|
field => $field, |
|
2966
|
|
|
|
|
|
|
operator => $operator, |
|
2967
|
|
|
|
|
|
|
values => $values, |
|
2968
|
|
|
|
|
|
|
); |
|
2969
|
|
|
|
|
|
|
|
|
2970
|
|
|
|
|
|
|
=cut |
|
2971
|
|
|
|
|
|
|
|
|
2972
|
|
|
|
|
|
|
sub build_filtering_clause |
|
2973
|
|
|
|
|
|
|
{ |
|
2974
|
48
|
|
|
48
|
1
|
88352
|
my ( $class, %args ) = @_; |
|
2975
|
48
|
|
|
|
|
179
|
my $field = $args{'field'}; |
|
2976
|
48
|
|
|
|
|
120
|
my $operator = $args{'operator'}; |
|
2977
|
48
|
|
|
|
|
98
|
my $values = $args{'values'}; |
|
2978
|
|
|
|
|
|
|
|
|
2979
|
48
|
|
|
|
|
77
|
my $clause; |
|
2980
|
48
|
|
|
|
|
109
|
my $clause_values = [ $values ]; |
|
2981
|
|
|
|
|
|
|
|
|
2982
|
|
|
|
|
|
|
# Quote the field name. |
|
2983
|
48
|
|
|
|
|
205
|
my $dbh = $class->assert_dbh(); |
|
2984
|
48
|
|
|
|
|
304
|
my $quoted_field = join( '.', map { $dbh->quote_identifier( $_ ) } split( /\./, $field ) ); |
|
|
74
|
|
|
|
|
1752
|
|
|
2985
|
|
|
|
|
|
|
|
|
2986
|
48
|
50
|
33
|
|
|
2024
|
croak 'A field name is required' |
|
2987
|
|
|
|
|
|
|
if !defined( $field ) || $field eq ''; |
|
2988
|
|
|
|
|
|
|
|
|
2989
|
|
|
|
|
|
|
# Between is a special case where values are an arrayref of a specific size. |
|
2990
|
48
|
100
|
|
|
|
395
|
if ( $operator eq 'between' ) ## no critic (ControlStructures::ProhibitCascadingIfElse) |
|
|
|
100
|
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
2991
|
|
|
|
|
|
|
{ |
|
2992
|
2
|
100
|
66
|
|
|
9
|
unless ( defined( $values ) && Data::Validate::Type::is_arrayref( $values ) && scalar( @$values ) == 2 ) |
|
|
|
|
66
|
|
|
|
|
|
2993
|
|
|
|
|
|
|
{ |
|
2994
|
1
|
|
|
|
|
40
|
croak '>between< requires two values to be passed as an arrayref'; |
|
2995
|
|
|
|
|
|
|
} |
|
2996
|
|
|
|
|
|
|
|
|
2997
|
1
|
|
|
|
|
35
|
$clause = "$quoted_field BETWEEN ? AND ?"; |
|
2998
|
1
|
|
|
|
|
1
|
$clause_values = $values; |
|
2999
|
|
|
|
|
|
|
} |
|
3000
|
|
|
|
|
|
|
# 'null' is also a special case with no values. |
|
3001
|
|
|
|
|
|
|
elsif ( $operator eq 'null' ) |
|
3002
|
|
|
|
|
|
|
{ |
|
3003
|
2
|
|
|
|
|
4
|
$clause = "$quoted_field IS NULL"; |
|
3004
|
2
|
|
|
|
|
5
|
$clause_values = []; |
|
3005
|
|
|
|
|
|
|
} |
|
3006
|
|
|
|
|
|
|
# 'not_null' is also a special case with no values. |
|
3007
|
|
|
|
|
|
|
elsif ( $operator eq 'not_null' ) |
|
3008
|
|
|
|
|
|
|
{ |
|
3009
|
2
|
|
|
|
|
4
|
$clause = "$quoted_field IS NOT NULL"; |
|
3010
|
2
|
|
|
|
|
4
|
$clause_values = []; |
|
3011
|
|
|
|
|
|
|
} |
|
3012
|
|
|
|
|
|
|
# More than one value passed. |
|
3013
|
|
|
|
|
|
|
elsif ( Data::Validate::Type::is_arrayref( $values ) ) |
|
3014
|
|
|
|
|
|
|
{ |
|
3015
|
35
|
100
|
100
|
|
|
5543
|
if ( $operator eq '=' ) ## no critic (ControlStructures::ProhibitCascadingIfElse) |
|
|
|
100
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
3016
|
|
|
|
|
|
|
{ |
|
3017
|
25
|
|
|
|
|
150
|
$clause = "$quoted_field IN (" . join( ', ', ( ( '?' ) x scalar( @$values ) ) ) . ")"; |
|
3018
|
25
|
|
|
|
|
63
|
$clause_values = $values; |
|
3019
|
|
|
|
|
|
|
} |
|
3020
|
|
|
|
|
|
|
elsif ( $operator eq 'not' ) |
|
3021
|
|
|
|
|
|
|
{ |
|
3022
|
2
|
|
|
|
|
12
|
$clause = "$quoted_field NOT IN (" . join( ', ', ( ( '?' ) x scalar( @$values ) ) ) . ")"; |
|
3023
|
2
|
|
|
|
|
6
|
$clause_values = $values; |
|
3024
|
|
|
|
|
|
|
} |
|
3025
|
|
|
|
|
|
|
elsif ( $operator eq '>' || $operator eq '>=' ) |
|
3026
|
|
|
|
|
|
|
{ |
|
3027
|
|
|
|
|
|
|
|
|
3028
|
|
|
|
|
|
|
# List::Util::max() really hates undefined elements and will warn |
|
3029
|
|
|
|
|
|
|
# loudly at each one it encounters. So, grep them out first. |
|
3030
|
2
|
|
|
|
|
5
|
my $max = List::Util::max( grep { defined( $_ ) } @$values ); |
|
|
4
|
|
|
|
|
24
|
|
|
3031
|
2
|
50
|
|
|
|
6
|
if ( defined( $max ) ) |
|
3032
|
|
|
|
|
|
|
{ |
|
3033
|
2
|
|
|
|
|
6
|
$clause = "$quoted_field $operator ?"; |
|
3034
|
2
|
|
|
|
|
6
|
$clause_values = [ $max ]; |
|
3035
|
|
|
|
|
|
|
} |
|
3036
|
|
|
|
|
|
|
else |
|
3037
|
|
|
|
|
|
|
{ |
|
3038
|
0
|
|
|
|
|
0
|
croak 'Could not find max of the following list: ' . dumper( $values ); |
|
3039
|
|
|
|
|
|
|
} |
|
3040
|
|
|
|
|
|
|
} |
|
3041
|
|
|
|
|
|
|
elsif ( $operator eq '<' || $operator eq '<=' ) |
|
3042
|
|
|
|
|
|
|
{ |
|
3043
|
|
|
|
|
|
|
# List::Util::max() really hates undefined elements and will warn |
|
3044
|
|
|
|
|
|
|
# loudly at each one it encounters. So, grep them out first. |
|
3045
|
2
|
|
|
|
|
5
|
my $min = List::Util::min( grep { defined( $_ ) } @$values ); |
|
|
4
|
|
|
|
|
19
|
|
|
3046
|
2
|
50
|
|
|
|
7
|
if ( defined( $min ) ) |
|
3047
|
|
|
|
|
|
|
{ |
|
3048
|
2
|
|
|
|
|
8
|
$clause = "$quoted_field $operator ?"; |
|
3049
|
2
|
|
|
|
|
7
|
$clause_values = [ $min ]; |
|
3050
|
|
|
|
|
|
|
} |
|
3051
|
|
|
|
|
|
|
else |
|
3052
|
|
|
|
|
|
|
{ |
|
3053
|
0
|
|
|
|
|
0
|
croak 'Could not find min of the following list: ' . dumper( $values ); |
|
3054
|
|
|
|
|
|
|
} |
|
3055
|
|
|
|
|
|
|
} |
|
3056
|
|
|
|
|
|
|
elsif ( $operator eq 'like' ) |
|
3057
|
|
|
|
|
|
|
{ |
|
3058
|
|
|
|
|
|
|
# Permit more than one like clause on the same field. |
|
3059
|
2
|
|
|
|
|
8
|
$clause = "$quoted_field LIKE ? OR " x scalar @{ $values }; |
|
|
2
|
|
|
|
|
9
|
|
|
3060
|
2
|
|
|
|
|
6
|
$clause = substr( $clause, 0, -4 ); |
|
3061
|
2
|
|
|
|
|
6
|
$clause_values = $values; |
|
3062
|
|
|
|
|
|
|
} |
|
3063
|
|
|
|
|
|
|
elsif ( $operator eq 'not_like' ) |
|
3064
|
|
|
|
|
|
|
{ |
|
3065
|
|
|
|
|
|
|
# Permit more than one like clause on the same field. |
|
3066
|
2
|
|
|
|
|
36
|
$clause = "$quoted_field NOT LIKE ? AND " x scalar @{ $values }; |
|
|
2
|
|
|
|
|
9
|
|
|
3067
|
2
|
|
|
|
|
5
|
$clause = substr( $clause, 0, -5 ); |
|
3068
|
2
|
|
|
|
|
4
|
$clause_values = $values; |
|
3069
|
|
|
|
|
|
|
} |
|
3070
|
|
|
|
|
|
|
# Only one value passed. |
|
3071
|
|
|
|
|
|
|
else |
|
3072
|
|
|
|
|
|
|
{ |
|
3073
|
0
|
|
|
|
|
0
|
croak "The operator '$operator' is not implemented"; |
|
3074
|
|
|
|
|
|
|
} |
|
3075
|
|
|
|
|
|
|
} |
|
3076
|
|
|
|
|
|
|
else |
|
3077
|
|
|
|
|
|
|
{ |
|
3078
|
7
|
100
|
|
|
|
115
|
$operator = '!=' |
|
3079
|
|
|
|
|
|
|
if $operator eq 'not'; |
|
3080
|
|
|
|
|
|
|
|
|
3081
|
7
|
|
|
|
|
22
|
$clause = "$quoted_field $operator ?"; |
|
3082
|
|
|
|
|
|
|
} |
|
3083
|
|
|
|
|
|
|
|
|
3084
|
47
|
|
|
|
|
286
|
return ( $clause, $clause_values ); |
|
3085
|
|
|
|
|
|
|
} |
|
3086
|
|
|
|
|
|
|
|
|
3087
|
|
|
|
|
|
|
|
|
3088
|
|
|
|
|
|
|
=head2 parse_filtering_criteria() |
|
3089
|
|
|
|
|
|
|
|
|
3090
|
|
|
|
|
|
|
Helper function that takes a list of fields and converts them into where |
|
3091
|
|
|
|
|
|
|
clauses and values that can be used by retrieve_list(). |
|
3092
|
|
|
|
|
|
|
|
|
3093
|
|
|
|
|
|
|
my ( $where_clauses, $where_values, $filtering_field_keys_passed ) = |
|
3094
|
|
|
|
|
|
|
@{ |
|
3095
|
|
|
|
|
|
|
$class->parse_filtering_criteria( |
|
3096
|
|
|
|
|
|
|
\%filtering_criteria |
|
3097
|
|
|
|
|
|
|
) |
|
3098
|
|
|
|
|
|
|
}; |
|
3099
|
|
|
|
|
|
|
|
|
3100
|
|
|
|
|
|
|
$filtering_field_keys_passed indicates whether %values had keys matching at |
|
3101
|
|
|
|
|
|
|
least one element of @field. This allows detecting whether any filtering |
|
3102
|
|
|
|
|
|
|
criteria was passed, even if the filtering criteria do not result in WHERE |
|
3103
|
|
|
|
|
|
|
clauses being returned. |
|
3104
|
|
|
|
|
|
|
|
|
3105
|
|
|
|
|
|
|
=cut |
|
3106
|
|
|
|
|
|
|
|
|
3107
|
|
|
|
|
|
|
sub parse_filtering_criteria |
|
3108
|
|
|
|
|
|
|
{ |
|
3109
|
35
|
|
|
35
|
1
|
37527
|
my ( $class, $filters ) = @_; |
|
3110
|
|
|
|
|
|
|
|
|
3111
|
|
|
|
|
|
|
# Check the arguments. |
|
3112
|
35
|
100
|
|
|
|
194179
|
if ( !Data::Validate::Type::is_hashref( $filters ) ) |
|
3113
|
|
|
|
|
|
|
{ |
|
3114
|
2
|
|
|
|
|
27
|
my $error = "The first argument must be a hashref of filtering criteria"; |
|
3115
|
2
|
|
|
|
|
10
|
$log->error( $error ); |
|
3116
|
2
|
|
|
|
|
28
|
croak $error; |
|
3117
|
|
|
|
|
|
|
}; |
|
3118
|
|
|
|
|
|
|
|
|
3119
|
|
|
|
|
|
|
# Build the list of filtering fields we allow. |
|
3120
|
30
|
|
|
|
|
102
|
my $filtering_fields = |
|
3121
|
|
|
|
|
|
|
{ |
|
3122
|
33
|
50
|
|
|
|
378
|
map { $_ => 1 } |
|
3123
|
33
|
|
|
|
|
668
|
@{ $class->get_filtering_fields() || [] } |
|
3124
|
|
|
|
|
|
|
}; |
|
3125
|
|
|
|
|
|
|
|
|
3126
|
33
|
|
|
|
|
165
|
my $primary_key_name = $class->get_info('primary_key_name'); |
|
3127
|
33
|
50
|
|
|
|
183
|
if ( defined( $primary_key_name ) ) |
|
3128
|
|
|
|
|
|
|
{ |
|
3129
|
|
|
|
|
|
|
# If there's a primary key name, allow 'id' as an alias. |
|
3130
|
33
|
|
|
|
|
103
|
$filtering_fields->{'id'} = 1; |
|
3131
|
|
|
|
|
|
|
} |
|
3132
|
|
|
|
|
|
|
|
|
3133
|
|
|
|
|
|
|
# Check if we were passed filters we don't know how to handle. This will |
|
3134
|
|
|
|
|
|
|
# help the calling code to detect typos or missing filtering fields in the |
|
3135
|
|
|
|
|
|
|
# static class declaration. |
|
3136
|
33
|
|
|
|
|
121
|
foreach my $filter ( keys %$filters ) |
|
3137
|
|
|
|
|
|
|
{ |
|
3138
|
30
|
100
|
|
|
|
170
|
next if defined( $filtering_fields->{ $filter } ); |
|
3139
|
|
|
|
|
|
|
|
|
3140
|
1
|
|
|
|
|
32
|
croak( |
|
3141
|
|
|
|
|
|
|
"The filtering criteria '$filter' passed to DBIx::NinjaORM->retrieve_list() " . |
|
3142
|
|
|
|
|
|
|
"via ${class}->retrieve_list() is not handled by the superclass. It could " . |
|
3143
|
|
|
|
|
|
|
"mean that you have a typo in the name, or that you need to add it to " . |
|
3144
|
|
|
|
|
|
|
"the list of filtering fields in ${class}->static_class_info()." |
|
3145
|
|
|
|
|
|
|
); |
|
3146
|
|
|
|
|
|
|
} |
|
3147
|
|
|
|
|
|
|
|
|
3148
|
|
|
|
|
|
|
# Find the table name to prefix it to the field names when we create where |
|
3149
|
|
|
|
|
|
|
# clauses. |
|
3150
|
32
|
|
|
|
|
141
|
my $table_name = $class->get_info('table_name'); |
|
3151
|
32
|
50
|
0
|
|
|
166
|
croak "No table name found for the class >" . ( ref( $class ) || $class ) . "<" |
|
3152
|
|
|
|
|
|
|
if !defined( $table_name ); |
|
3153
|
|
|
|
|
|
|
|
|
3154
|
32
|
|
|
|
|
64
|
my $where_clauses = []; |
|
3155
|
32
|
|
|
|
|
63
|
my $where_values = []; |
|
3156
|
32
|
|
|
|
|
58
|
my $filtering_field_keys_passed = 0; |
|
3157
|
32
|
|
|
|
|
138
|
foreach my $field ( sort keys %$filters ) |
|
3158
|
|
|
|
|
|
|
{ |
|
3159
|
|
|
|
|
|
|
# "field => undef" and "field => []" are not valid filtering |
|
3160
|
|
|
|
|
|
|
# criteria. This prevents programming errors, by forcing the |
|
3161
|
|
|
|
|
|
|
# use of the 'null' operator when you explicitely want to |
|
3162
|
|
|
|
|
|
|
# test for NULL. See: |
|
3163
|
|
|
|
|
|
|
# |
|
3164
|
|
|
|
|
|
|
# field => |
|
3165
|
|
|
|
|
|
|
# { |
|
3166
|
|
|
|
|
|
|
# operator => 'null', |
|
3167
|
|
|
|
|
|
|
# } |
|
3168
|
|
|
|
|
|
|
# |
|
3169
|
29
|
100
|
|
|
|
263
|
next unless defined( $filters->{ $field } ); |
|
3170
|
8
|
|
|
|
|
211
|
next if Data::Validate::Type::is_arrayref( $filters->{ $field } ) |
|
3171
|
28
|
100
|
100
|
|
|
193
|
&& scalar( @{ $filters->{ $field } } ) == 0; |
|
3172
|
|
|
|
|
|
|
|
|
3173
|
|
|
|
|
|
|
# We now have a valid filtering criteria. |
|
3174
|
27
|
|
|
|
|
1825
|
$filtering_field_keys_passed = 1; |
|
3175
|
|
|
|
|
|
|
|
|
3176
|
|
|
|
|
|
|
# Add the table prefix if needed, this will prevent conflicts if the |
|
3177
|
|
|
|
|
|
|
# main query performs JOINs. |
|
3178
|
27
|
50
|
66
|
|
|
312
|
my $full_field_name = defined( $primary_key_name ) && ( $field eq 'id' ) |
|
|
|
100
|
|
|
|
|
|
|
3179
|
|
|
|
|
|
|
? $table_name . '.' . $primary_key_name |
|
3180
|
|
|
|
|
|
|
: $field =~ m/\./ |
|
3181
|
|
|
|
|
|
|
? $field |
|
3182
|
|
|
|
|
|
|
: $table_name . '.' . $field; |
|
3183
|
|
|
|
|
|
|
|
|
3184
|
|
|
|
|
|
|
# Turn the value into an array of values, if needed. |
|
3185
|
27
|
100
|
|
|
|
105
|
my $values = Data::Validate::Type::is_arrayref( $filters->{ $field } ) |
|
3186
|
|
|
|
|
|
|
? $filters->{ $field } |
|
3187
|
|
|
|
|
|
|
: [ $filters->{ $field } ]; |
|
3188
|
|
|
|
|
|
|
|
|
3189
|
27
|
|
|
|
|
705
|
my @scalar_values = (); |
|
3190
|
27
|
|
|
|
|
350
|
foreach my $block ( @$values ) |
|
3191
|
|
|
|
|
|
|
{ |
|
3192
|
86
|
100
|
|
|
|
210
|
if ( Data::Validate::Type::is_hashref( $block ) ) |
|
3193
|
|
|
|
|
|
|
{ |
|
3194
|
3
|
50
|
33
|
|
|
63
|
if ( !defined( $block->{'operator'} ) ) |
|
|
|
100
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
3195
|
|
|
|
|
|
|
{ |
|
3196
|
0
|
|
|
|
|
0
|
croak 'The operator is missing or not defined'; |
|
3197
|
|
|
|
|
|
|
} |
|
3198
|
|
|
|
|
|
|
elsif ( $block->{'operator'} !~ m/^(?:=|not|<=|>=|<|>|between|null|not_null|like|not_like)$/x ) |
|
3199
|
|
|
|
|
|
|
{ |
|
3200
|
1
|
|
|
|
|
38
|
croak "The operator '$block->{'operator'}' is not a valid one. Try (=|not|<=|>=|<|>)"; |
|
3201
|
|
|
|
|
|
|
} |
|
3202
|
|
|
|
|
|
|
elsif ( !exists( $block->{'value'} ) && $block->{'operator'} !~ /^(?:null|not_null)$/ ) |
|
3203
|
|
|
|
|
|
|
{ |
|
3204
|
0
|
|
|
|
|
0
|
croak "The value key is missing for operator '$block->{'operator'}'"; |
|
3205
|
|
|
|
|
|
|
} |
|
3206
|
|
|
|
|
|
|
|
|
3207
|
2
|
|
|
|
|
12
|
my ( $clause, $clause_values ) = $class->build_filtering_clause( |
|
3208
|
|
|
|
|
|
|
field => $full_field_name, |
|
3209
|
|
|
|
|
|
|
operator => $block->{'operator'}, |
|
3210
|
|
|
|
|
|
|
values => $block->{'value'}, |
|
3211
|
|
|
|
|
|
|
); |
|
3212
|
2
|
|
|
|
|
5
|
push( @$where_clauses, $clause ); |
|
3213
|
2
|
|
|
|
|
8
|
push( @$where_values, $clause_values ); |
|
3214
|
|
|
|
|
|
|
} |
|
3215
|
|
|
|
|
|
|
else |
|
3216
|
|
|
|
|
|
|
{ |
|
3217
|
83
|
|
|
|
|
930
|
push( @scalar_values, $block ); |
|
3218
|
|
|
|
|
|
|
} |
|
3219
|
|
|
|
|
|
|
} |
|
3220
|
|
|
|
|
|
|
|
|
3221
|
26
|
100
|
|
|
|
172
|
if ( scalar( @scalar_values ) != 0 ) |
|
3222
|
|
|
|
|
|
|
{ |
|
3223
|
24
|
|
|
|
|
289
|
my ( $clause, $clause_values ) = $class->build_filtering_clause( |
|
3224
|
|
|
|
|
|
|
field => $full_field_name, |
|
3225
|
|
|
|
|
|
|
operator => '=', |
|
3226
|
|
|
|
|
|
|
values => \@scalar_values, |
|
3227
|
|
|
|
|
|
|
); |
|
3228
|
24
|
|
|
|
|
66
|
push( @$where_clauses, $clause ); |
|
3229
|
24
|
|
|
|
|
116
|
push( @$where_values, $clause_values ); |
|
3230
|
|
|
|
|
|
|
} |
|
3231
|
|
|
|
|
|
|
} |
|
3232
|
|
|
|
|
|
|
|
|
3233
|
31
|
|
|
|
|
219
|
return [ $where_clauses, $where_values, $filtering_field_keys_passed ]; |
|
3234
|
|
|
|
|
|
|
} |
|
3235
|
|
|
|
|
|
|
|
|
3236
|
|
|
|
|
|
|
|
|
3237
|
|
|
|
|
|
|
=head2 reorganize_non_native_fields() |
|
3238
|
|
|
|
|
|
|
|
|
3239
|
|
|
|
|
|
|
When we retrieve fields via SELECT in retrieve_list_nocache(), by convention we use |
|
3240
|
|
|
|
|
|
|
_[table_name]_[field_name] for fields that are not native to the underlying |
|
3241
|
|
|
|
|
|
|
table that the object represents. |
|
3242
|
|
|
|
|
|
|
|
|
3243
|
|
|
|
|
|
|
This method moves them to $object->{'_table_name'}->{'field_name'} for a |
|
3244
|
|
|
|
|
|
|
cleaner organization inside the object. |
|
3245
|
|
|
|
|
|
|
|
|
3246
|
|
|
|
|
|
|
$object->reorganize_non_native_fields(); |
|
3247
|
|
|
|
|
|
|
|
|
3248
|
|
|
|
|
|
|
=cut |
|
3249
|
|
|
|
|
|
|
|
|
3250
|
|
|
|
|
|
|
sub reorganize_non_native_fields |
|
3251
|
|
|
|
|
|
|
{ |
|
3252
|
85
|
|
|
85
|
1
|
757
|
my ( $self ) = @_; |
|
3253
|
|
|
|
|
|
|
|
|
3254
|
|
|
|
|
|
|
# Move non-native fields to their own happy place. |
|
3255
|
85
|
|
|
|
|
344
|
foreach my $field ( keys %$self ) |
|
3256
|
|
|
|
|
|
|
{ |
|
3257
|
502
|
100
|
|
|
|
1195
|
next unless $field =~ m/^(_[^_]+)_(.*)$/; |
|
3258
|
4
|
|
|
|
|
30
|
$self->{ $1 }->{ $2 } = $self->{ $field }; |
|
3259
|
4
|
|
|
|
|
13
|
delete( $self->{ $field } ); |
|
3260
|
|
|
|
|
|
|
} |
|
3261
|
|
|
|
|
|
|
|
|
3262
|
85
|
|
|
|
|
201
|
return; |
|
3263
|
|
|
|
|
|
|
} |
|
3264
|
|
|
|
|
|
|
|
|
3265
|
|
|
|
|
|
|
|
|
3266
|
|
|
|
|
|
|
=head1 BUGS |
|
3267
|
|
|
|
|
|
|
|
|
3268
|
|
|
|
|
|
|
Please report any bugs or feature requests through the web interface at |
|
3269
|
|
|
|
|
|
|
L. |
|
3270
|
|
|
|
|
|
|
I will be notified, and then you'll automatically be notified of progress on |
|
3271
|
|
|
|
|
|
|
your bug as I make changes. |
|
3272
|
|
|
|
|
|
|
|
|
3273
|
|
|
|
|
|
|
|
|
3274
|
|
|
|
|
|
|
=head1 SUPPORT |
|
3275
|
|
|
|
|
|
|
|
|
3276
|
|
|
|
|
|
|
You can find documentation for this module with the perldoc command. |
|
3277
|
|
|
|
|
|
|
|
|
3278
|
|
|
|
|
|
|
perldoc DBIx::NinjaORM |
|
3279
|
|
|
|
|
|
|
|
|
3280
|
|
|
|
|
|
|
|
|
3281
|
|
|
|
|
|
|
You can also look for information at: |
|
3282
|
|
|
|
|
|
|
|
|
3283
|
|
|
|
|
|
|
=over 4 |
|
3284
|
|
|
|
|
|
|
|
|
3285
|
|
|
|
|
|
|
=item * GitHub's request tracker |
|
3286
|
|
|
|
|
|
|
|
|
3287
|
|
|
|
|
|
|
L |
|
3288
|
|
|
|
|
|
|
|
|
3289
|
|
|
|
|
|
|
=item * AnnoCPAN: Annotated CPAN documentation |
|
3290
|
|
|
|
|
|
|
|
|
3291
|
|
|
|
|
|
|
L |
|
3292
|
|
|
|
|
|
|
|
|
3293
|
|
|
|
|
|
|
=item * CPAN Ratings |
|
3294
|
|
|
|
|
|
|
|
|
3295
|
|
|
|
|
|
|
L |
|
3296
|
|
|
|
|
|
|
|
|
3297
|
|
|
|
|
|
|
=item * MetaCPAN |
|
3298
|
|
|
|
|
|
|
|
|
3299
|
|
|
|
|
|
|
L |
|
3300
|
|
|
|
|
|
|
|
|
3301
|
|
|
|
|
|
|
=back |
|
3302
|
|
|
|
|
|
|
|
|
3303
|
|
|
|
|
|
|
|
|
3304
|
|
|
|
|
|
|
=head1 AUTHOR |
|
3305
|
|
|
|
|
|
|
|
|
3306
|
|
|
|
|
|
|
L, C<< >>. |
|
3307
|
|
|
|
|
|
|
|
|
3308
|
|
|
|
|
|
|
|
|
3309
|
|
|
|
|
|
|
=head1 CONTRIBUTORS |
|
3310
|
|
|
|
|
|
|
|
|
3311
|
|
|
|
|
|
|
=over 4 |
|
3312
|
|
|
|
|
|
|
|
|
3313
|
|
|
|
|
|
|
=item * L |
|
3314
|
|
|
|
|
|
|
|
|
3315
|
|
|
|
|
|
|
=item * Jamie McCarthy |
|
3316
|
|
|
|
|
|
|
|
|
3317
|
|
|
|
|
|
|
=item * L |
|
3318
|
|
|
|
|
|
|
|
|
3319
|
|
|
|
|
|
|
=item * L |
|
3320
|
|
|
|
|
|
|
|
|
3321
|
|
|
|
|
|
|
=back |
|
3322
|
|
|
|
|
|
|
|
|
3323
|
|
|
|
|
|
|
|
|
3324
|
|
|
|
|
|
|
=head1 ACKNOWLEDGEMENTS |
|
3325
|
|
|
|
|
|
|
|
|
3326
|
|
|
|
|
|
|
I originally developed this project for ThinkGeek |
|
3327
|
|
|
|
|
|
|
(L). Thanks for allowing me to open-source it! |
|
3328
|
|
|
|
|
|
|
|
|
3329
|
|
|
|
|
|
|
Special thanks to Kate Kirby for her help with the design of this module. |
|
3330
|
|
|
|
|
|
|
|
|
3331
|
|
|
|
|
|
|
|
|
3332
|
|
|
|
|
|
|
=head1 COPYRIGHT & LICENSE |
|
3333
|
|
|
|
|
|
|
|
|
3334
|
|
|
|
|
|
|
Copyright 2009-2014 Guillaume Aubert. |
|
3335
|
|
|
|
|
|
|
|
|
3336
|
|
|
|
|
|
|
This program is free software: you can redistribute it and/or modify it under |
|
3337
|
|
|
|
|
|
|
the terms of the GNU General Public License version 3 as published by the Free |
|
3338
|
|
|
|
|
|
|
Software Foundation. |
|
3339
|
|
|
|
|
|
|
|
|
3340
|
|
|
|
|
|
|
This program is distributed in the hope that it will be useful, but WITHOUT ANY |
|
3341
|
|
|
|
|
|
|
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A |
|
3342
|
|
|
|
|
|
|
PARTICULAR PURPOSE. See the GNU General Public License for more details. |
|
3343
|
|
|
|
|
|
|
|
|
3344
|
|
|
|
|
|
|
You should have received a copy of the GNU General Public License along with |
|
3345
|
|
|
|
|
|
|
this program. If not, see http://www.gnu.org/licenses/ |
|
3346
|
|
|
|
|
|
|
|
|
3347
|
|
|
|
|
|
|
=cut |
|
3348
|
|
|
|
|
|
|
|
|
3349
|
|
|
|
|
|
|
1; |