line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package Class::ReluctantORM::Collection; |
2
|
1
|
|
|
1
|
|
6
|
use strict; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
922
|
|
3
|
|
|
|
|
|
|
|
4
|
|
|
|
|
|
|
=head1 NAME |
5
|
|
|
|
|
|
|
|
6
|
|
|
|
|
|
|
Class::ReluctantORM::Collection - Represent a multirelational attribute |
7
|
|
|
|
|
|
|
|
8
|
|
|
|
|
|
|
=head1 SYNOPSIS |
9
|
|
|
|
|
|
|
|
10
|
|
|
|
|
|
|
# See Class::ReluctantORM |
11
|
|
|
|
|
|
|
package Ship; |
12
|
|
|
|
|
|
|
Ship->build_class(...); |
13
|
|
|
|
|
|
|
Ship->has_many('Pirate'); |
14
|
|
|
|
|
|
|
|
15
|
|
|
|
|
|
|
package main; |
16
|
|
|
|
|
|
|
my $ship = Ship->fetch_by_name('Lollipop'); |
17
|
|
|
|
|
|
|
my $coll = $ship->pirates(); |
18
|
|
|
|
|
|
|
|
19
|
|
|
|
|
|
|
# $coll hasn't been populated yet... |
20
|
|
|
|
|
|
|
@pirates = $coll->all_items(); # Throws 'FetchRequired' exception |
21
|
|
|
|
|
|
|
@pirates = $coll->fetch_all(); # Remembers results |
22
|
|
|
|
|
|
|
@pirates = $coll->all_items(); # no exception now |
23
|
|
|
|
|
|
|
|
24
|
|
|
|
|
|
|
# If you get ship differently, you can pre-populate the collection |
25
|
|
|
|
|
|
|
$ship = Ship->fetch_by_name_with_pirates('Lollipop'); |
26
|
|
|
|
|
|
|
$coll = $ship->pirates(); |
27
|
|
|
|
|
|
|
@pirates = $coll->all_items(); # no exception now |
28
|
|
|
|
|
|
|
@pirates = $ship->pirates->all(); # Same thing |
29
|
|
|
|
|
|
|
|
30
|
|
|
|
|
|
|
# Or try this: |
31
|
|
|
|
|
|
|
@search = $coll->search(where => 'where_clause'); |
32
|
|
|
|
|
|
|
# Never remembers results or affects populated status |
33
|
|
|
|
|
|
|
|
34
|
|
|
|
|
|
|
# Here's counting: |
35
|
|
|
|
|
|
|
my $count = $coll->count(); # Throws 'FetchNeeded' exception unless populated |
36
|
|
|
|
|
|
|
my $count = $coll->fetch_count(); # Remembers count, but does not set populated flag |
37
|
|
|
|
|
|
|
|
38
|
|
|
|
|
|
|
# Add or delete individual items |
39
|
|
|
|
|
|
|
$coll->add($pirate); |
40
|
|
|
|
|
|
|
$coll->delete($pirate); |
41
|
|
|
|
|
|
|
|
42
|
|
|
|
|
|
|
# This tries to do a delete |
43
|
|
|
|
|
|
|
$coll->delete_all(); |
44
|
|
|
|
|
|
|
$coll->delete_where(where => 'where clause', execargs => []); |
45
|
|
|
|
|
|
|
|
46
|
|
|
|
|
|
|
# This could be useful.... |
47
|
|
|
|
|
|
|
if ($coll->is_populated()) { ... } |
48
|
|
|
|
|
|
|
$coll->depopulate(); |
49
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
51
|
|
|
|
|
|
|
=head1 DESCRIPTION |
52
|
|
|
|
|
|
|
|
53
|
|
|
|
|
|
|
A simple container class for one-to-many and many-to-many relationships. |
54
|
|
|
|
|
|
|
|
55
|
|
|
|
|
|
|
=cut |
56
|
|
|
|
|
|
|
|
57
|
|
|
|
|
|
|
#=====================================================# |
58
|
|
|
|
|
|
|
# Public Virtual Methods |
59
|
|
|
|
|
|
|
#=====================================================# |
60
|
|
|
|
|
|
|
|
61
|
|
|
|
|
|
|
=head2 @items = $c->all_items(); |
62
|
|
|
|
|
|
|
|
63
|
|
|
|
|
|
|
=head2 @items = $c->all(); |
64
|
|
|
|
|
|
|
|
65
|
|
|
|
|
|
|
If the collection is already populated, returns an array of the items. |
66
|
|
|
|
|
|
|
|
67
|
|
|
|
|
|
|
If the collection is not already populated, throws a FetchRequired |
68
|
|
|
|
|
|
|
exception. |
69
|
|
|
|
|
|
|
|
70
|
|
|
|
|
|
|
Aliased as all(). |
71
|
|
|
|
|
|
|
|
72
|
|
|
|
|
|
|
=cut |
73
|
|
|
|
|
|
|
|
74
|
0
|
|
|
0
|
1
|
|
sub all_items { Class::ReluctantORM::Exception::Call::PureVirtual->croak(); } |
75
|
0
|
|
|
0
|
1
|
|
sub all { Class::ReluctantORM::Exception::Call::PureVirtual->croak(); } |
76
|
|
|
|
|
|
|
|
77
|
|
|
|
|
|
|
=head2 $o = $c->first(); |
78
|
|
|
|
|
|
|
|
79
|
|
|
|
|
|
|
Like all_items(), but only returns the first one. |
80
|
|
|
|
|
|
|
|
81
|
|
|
|
|
|
|
WARNING: Collections are generally unordered, so the identity of the object returned is unreliable. Use this method when you want _any_ object from the collection. |
82
|
|
|
|
|
|
|
|
83
|
|
|
|
|
|
|
=cut |
84
|
|
|
|
|
|
|
|
85
|
|
|
|
|
|
|
sub first { |
86
|
0
|
|
|
0
|
1
|
|
my $self = shift; |
87
|
0
|
0
|
|
|
|
|
if ($self->is_populated) { |
88
|
0
|
|
|
|
|
|
return $self->{_children}->[0]; |
89
|
|
|
|
|
|
|
} else { |
90
|
0
|
|
|
|
|
|
Class::ReluctantORM::Exception::Data::FetchRequired->croak(called => 'first', call_instead => 'fetch_all', fetch_locations => [ $self->all_origin_traces ]); |
91
|
|
|
|
|
|
|
} |
92
|
|
|
|
|
|
|
} |
93
|
|
|
|
|
|
|
|
94
|
0
|
|
|
0
|
0
|
|
sub linking_object { return shift->{linking_object}; } |
95
|
|
|
|
|
|
|
|
96
|
|
|
|
|
|
|
=head2 @items = $c->fetch_all(); |
97
|
|
|
|
|
|
|
|
98
|
|
|
|
|
|
|
Fetchs all the items represented by the collection from |
99
|
|
|
|
|
|
|
the database and sets the populated flag to true. Count is |
100
|
|
|
|
|
|
|
now also available. |
101
|
|
|
|
|
|
|
|
102
|
|
|
|
|
|
|
=cut |
103
|
|
|
|
|
|
|
|
104
|
0
|
|
|
0
|
1
|
|
sub fetch_all { Class::ReluctantORM::Exception::Call::PureVirtual->croak(); } |
105
|
|
|
|
|
|
|
|
106
|
|
|
|
|
|
|
=head2 @items = $c->fetch_deep(with => {...}); |
107
|
|
|
|
|
|
|
|
108
|
|
|
|
|
|
|
Fetchs all the items represented by the collection, along with any JOINs specified. |
109
|
|
|
|
|
|
|
Sets the populated flag to true. Count is now also available. |
110
|
|
|
|
|
|
|
|
111
|
|
|
|
|
|
|
If no results are aobtained, this does NOT die. |
112
|
|
|
|
|
|
|
|
113
|
|
|
|
|
|
|
=cut |
114
|
|
|
|
|
|
|
|
115
|
0
|
|
|
0
|
1
|
|
sub fetch_deep { Class::ReluctantORM::Exception::Call::PureVirtual->croak(); } |
116
|
|
|
|
|
|
|
|
117
|
|
|
|
|
|
|
|
118
|
|
|
|
|
|
|
=head2 $count = $c->count(); |
119
|
|
|
|
|
|
|
|
120
|
|
|
|
|
|
|
If the collection has been populated or fetch_count has been called, |
121
|
|
|
|
|
|
|
returns the integer count of items. |
122
|
|
|
|
|
|
|
|
123
|
|
|
|
|
|
|
Otherwise, throws a FetchRequired exception. |
124
|
|
|
|
|
|
|
|
125
|
|
|
|
|
|
|
=cut |
126
|
|
|
|
|
|
|
|
127
|
0
|
|
|
0
|
1
|
|
sub count { Class::ReluctantORM::Exception::Call::PureVirtual->croak(); } |
128
|
|
|
|
|
|
|
|
129
|
|
|
|
|
|
|
=head2 $count = $c->fetch_count(); |
130
|
|
|
|
|
|
|
|
131
|
|
|
|
|
|
|
If the collection has been populated, returns the existing count. |
132
|
|
|
|
|
|
|
|
133
|
|
|
|
|
|
|
Otherwise, performs a SQL COUNT. The result is stored for later |
134
|
|
|
|
|
|
|
calls to count(). |
135
|
|
|
|
|
|
|
|
136
|
|
|
|
|
|
|
=cut |
137
|
|
|
|
|
|
|
|
138
|
0
|
|
|
0
|
1
|
|
sub fetch_count { Class::ReluctantORM::Exception::Call::PureVirtual->croak(); } |
139
|
|
|
|
|
|
|
|
140
|
|
|
|
|
|
|
|
141
|
|
|
|
|
|
|
=head2 @items = $c->search(where => 'where clause', limit => '', order => ''); |
142
|
|
|
|
|
|
|
|
143
|
|
|
|
|
|
|
Performs a search on the child table, for record associated with |
144
|
|
|
|
|
|
|
the master record and also matching the given where clause |
145
|
|
|
|
|
|
|
fragment. Results are never cached and do not |
146
|
|
|
|
|
|
|
affect the populated status. |
147
|
|
|
|
|
|
|
|
148
|
|
|
|
|
|
|
Returns an empty list when there are no results. In scalar acontext, returns |
149
|
|
|
|
|
|
|
first result, or undef if no results. |
150
|
|
|
|
|
|
|
|
151
|
|
|
|
|
|
|
=cut |
152
|
|
|
|
|
|
|
|
153
|
0
|
|
|
0
|
1
|
|
sub search { Class::ReluctantORM::Exception::Call::PureVirtual->croak(); } |
154
|
|
|
|
|
|
|
|
155
|
|
|
|
|
|
|
=head2 $bool = $c->is_present($object) |
156
|
|
|
|
|
|
|
|
157
|
|
|
|
|
|
|
Returns a value indicating the presence of a candidate object among the collection. The collection must be populated. |
158
|
|
|
|
|
|
|
|
159
|
|
|
|
|
|
|
Actually returns the count of objects with the same primary key from the collection, so you can use this method to detect duplicates. |
160
|
|
|
|
|
|
|
|
161
|
|
|
|
|
|
|
=cut |
162
|
|
|
|
|
|
|
|
163
|
|
|
|
|
|
|
sub is_present { |
164
|
0
|
|
|
0
|
1
|
|
my ($self, $object) = @_; |
165
|
0
|
0
|
|
|
|
|
unless ($self->is_populated) { |
166
|
0
|
|
|
|
|
|
Class::ReluctantORM::Exception::Data::FetchRequired->croak(called => 'is_present', call_instead => 'fetch_all', fetch_locations => [ $self->linking_object->all_origin_traces ]); |
167
|
|
|
|
|
|
|
} |
168
|
0
|
|
|
|
|
|
$self->_check_correct_child_class($object); |
169
|
0
|
|
|
|
|
|
my $id = $object->id(); |
170
|
0
|
|
|
|
|
|
return scalar grep {$_->id eq $id } @{$self->{_children}}; |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
171
|
|
|
|
|
|
|
} |
172
|
|
|
|
|
|
|
|
173
|
|
|
|
|
|
|
=head2 $c->add($object, [$ignore_dupe_errors) |
174
|
|
|
|
|
|
|
|
175
|
|
|
|
|
|
|
Associates the given object (which must already exist in the database) with the collection. If the collection is populated, the object is added to the list of objects in the collection, and the count is increased by one. If the collection is not yet fetched, the collection will still not be populated after the add (because collections are always either completely fetched or completely unfetched). |
176
|
|
|
|
|
|
|
|
177
|
|
|
|
|
|
|
Database changes, which happen regardless of populated status, depend on relationship type. For one-to-many relationships, this sets the foreign key in the child object to the primary key of the parent object. For many-to-many relations, this inserts a new row in the join table with the primary keys of both the left and right classes. |
178
|
|
|
|
|
|
|
|
179
|
|
|
|
|
|
|
Adding a duplicate object is not an error, at least according to this module. Your database may think otherwise. If so, you may pass a boolean second parameter, which will then trap and ignore database errors that appear to be uniqueness constraint violations. |
180
|
|
|
|
|
|
|
|
181
|
|
|
|
|
|
|
Query count: 1 |
182
|
|
|
|
|
|
|
|
183
|
|
|
|
|
|
|
=cut |
184
|
|
|
|
|
|
|
|
185
|
0
|
|
|
0
|
1
|
|
sub add { Class::ReluctantORM::Exception::Call::PureVirtual->croak(); } |
186
|
|
|
|
|
|
|
|
187
|
|
|
|
|
|
|
=head2 $c->delete($object); |
188
|
|
|
|
|
|
|
|
189
|
|
|
|
|
|
|
Searches for the given object in the collection and deletes it if found. The collection must be populated. For one-to-many relationships, the child object is deleted outright. For many-to-many relationships, all join table rows matching the two keys are deleted. |
190
|
|
|
|
|
|
|
|
191
|
|
|
|
|
|
|
Note that for one-to-many relationships, the deletion of the child record may cause database errors if there are objects that depend on the child object (ie, grandchild objects). You can use constraint actions, such as ON DELETE CASCADE or ON DELETE SET NULL to prevent such errors. |
192
|
|
|
|
|
|
|
|
193
|
|
|
|
|
|
|
If the object is not found among the collection, no action is taken, and no exception is thrown. If the object is found, the collection object is updated the new child list and count. |
194
|
|
|
|
|
|
|
|
195
|
|
|
|
|
|
|
Query count: 1 |
196
|
|
|
|
|
|
|
|
197
|
|
|
|
|
|
|
=cut |
198
|
|
|
|
|
|
|
|
199
|
0
|
|
|
0
|
1
|
|
sub delete { Class::ReluctantORM::Exception::Call::PureVirtual->croak(); } |
200
|
|
|
|
|
|
|
|
201
|
|
|
|
|
|
|
=head2 $c->delete_all(); |
202
|
|
|
|
|
|
|
|
203
|
|
|
|
|
|
|
For one-to-many, deletes all child records associated with the master record. |
204
|
|
|
|
|
|
|
|
205
|
|
|
|
|
|
|
For many-to-many, disassociates the child record from the master record (ie, it deletes rows from the join table). |
206
|
|
|
|
|
|
|
|
207
|
|
|
|
|
|
|
=cut |
208
|
|
|
|
|
|
|
|
209
|
0
|
|
|
0
|
1
|
|
sub delete_all { Class::ReluctantORM::Exception::Call::PureVirtual->croak(); } |
210
|
|
|
|
|
|
|
|
211
|
|
|
|
|
|
|
=head2 $c->delete_where('where clause'); |
212
|
|
|
|
|
|
|
|
213
|
|
|
|
|
|
|
=head2 $c->delete_where(where => 'where clause', execargs => [1,2,3]); |
214
|
|
|
|
|
|
|
|
215
|
|
|
|
|
|
|
For one-to-many, deletes all child records associated with the master record and |
216
|
|
|
|
|
|
|
matching the given where clause fragment. |
217
|
|
|
|
|
|
|
|
218
|
|
|
|
|
|
|
For many-to-many, disassociates the child records from the master record |
219
|
|
|
|
|
|
|
where the clause matches. |
220
|
|
|
|
|
|
|
|
221
|
|
|
|
|
|
|
=cut |
222
|
|
|
|
|
|
|
|
223
|
0
|
|
|
0
|
1
|
|
sub delete_where { Class::ReluctantORM::Exception::Call::PureVirtual->croak(); } |
224
|
|
|
|
|
|
|
|
225
|
|
|
|
|
|
|
=head2 $bool = is_populated(); |
226
|
|
|
|
|
|
|
|
227
|
|
|
|
|
|
|
Returns true if fetch_all has been called, or if the collection |
228
|
|
|
|
|
|
|
started life populated. |
229
|
|
|
|
|
|
|
|
230
|
|
|
|
|
|
|
=cut |
231
|
|
|
|
|
|
|
|
232
|
0
|
|
|
0
|
1
|
|
sub is_populated { Class::ReluctantORM::Exception::Call::PureVirtual->croak(); } |
233
|
|
|
|
|
|
|
|
234
|
|
|
|
|
|
|
=head2 $c->depopulate(); |
235
|
|
|
|
|
|
|
|
236
|
|
|
|
|
|
|
Clears the populated flag, and flushes any cached results. |
237
|
|
|
|
|
|
|
|
238
|
|
|
|
|
|
|
=cut |
239
|
|
|
|
|
|
|
|
240
|
0
|
|
|
0
|
1
|
|
sub depopulate { Class::ReluctantORM::Exception::Call::PureVirtual->croak(); } |
241
|
|
|
|
|
|
|
|
242
|
|
|
|
|
|
|
|
243
|
|
|
|
|
|
|
=head2 $result = $c->sum_of_FIELD(); |
244
|
|
|
|
|
|
|
|
245
|
|
|
|
|
|
|
=head2 $result = $c->max_of_FIELD(); |
246
|
|
|
|
|
|
|
|
247
|
|
|
|
|
|
|
=head2 $result = $c->min_of_FIELD(); |
248
|
|
|
|
|
|
|
|
249
|
|
|
|
|
|
|
=head2 $result = $c->count_of_FIELD(); |
250
|
|
|
|
|
|
|
|
251
|
|
|
|
|
|
|
Aggregate functions, like in Class::ReluctantORM. You may also provide a where and execargs argument. Note that your where clause will be modified to enforce the parent-child relationship. |
252
|
|
|
|
|
|
|
|
253
|
|
|
|
|
|
|
=cut |
254
|
|
|
|
|
|
|
|
255
|
|
|
|
|
|
|
sub AUTOLOAD { |
256
|
|
|
|
|
|
|
|
257
|
0
|
|
|
0
|
|
|
my $inv = shift; |
258
|
0
|
|
0
|
|
|
|
my $class = ref($inv) || $inv; |
259
|
0
|
0
|
|
|
|
|
my $self = ref($inv) ? $inv : undef; |
260
|
0
|
|
|
|
|
|
our $AUTOLOAD; |
261
|
|
|
|
|
|
|
|
262
|
|
|
|
|
|
|
# Never autoload DESTROY |
263
|
0
|
0
|
|
|
|
|
return if ($AUTOLOAD =~ /::DESTROY$/); |
264
|
|
|
|
|
|
|
|
265
|
|
|
|
|
|
|
# Strip classname from method |
266
|
0
|
|
|
|
|
|
my $method = $AUTOLOAD; |
267
|
0
|
|
|
|
|
|
my $re = $class . '::'; |
268
|
0
|
|
|
|
|
|
$method =~ s/^$re//; |
269
|
|
|
|
|
|
|
|
270
|
|
|
|
|
|
|
#........... |
271
|
|
|
|
|
|
|
# Agregrate autoloaded methods (max_of_total) |
272
|
|
|
|
|
|
|
#........... |
273
|
0
|
|
|
|
|
|
my $re3 = '^(' . join('|', map { lc($_->name) } Class::ReluctantORM::SQL::Function->list_aggregate_functions() ) . ')'; |
|
0
|
|
|
|
|
|
|
274
|
0
|
|
|
|
|
|
$re3 .= '_of_'; |
275
|
0
|
|
|
|
|
|
my @field_names = $self->rel->linked_class->field_names; |
276
|
0
|
|
|
|
|
|
$re3 .= '(' . join('|', @field_names) . ')$'; |
277
|
0
|
0
|
|
|
|
|
if ($method =~ /$re3/) { |
278
|
0
|
|
|
|
|
|
my ($agg_type, $field) = ($1, $2); |
279
|
0
|
|
|
|
|
|
return $self->__setup_aggregate_autoload($AUTOLOAD, $method, \@_, $agg_type, $field); |
280
|
|
|
|
|
|
|
} |
281
|
|
|
|
|
|
|
|
282
|
|
|
|
|
|
|
# Otherwise fail |
283
|
0
|
|
|
|
|
|
Class::ReluctantORM::Exception::Call::NoSuchMethod->croak("Could not find method $method in package $class"); |
284
|
|
|
|
|
|
|
|
285
|
|
|
|
|
|
|
} |
286
|
|
|
|
|
|
|
|
287
|
|
|
|
|
|
|
#=====================================================# |
288
|
|
|
|
|
|
|
# Protected Virtual Methods |
289
|
|
|
|
|
|
|
#=====================================================# |
290
|
|
|
|
|
|
|
|
291
|
0
|
|
|
0
|
|
|
sub _new { Class::ReluctantORM::Exception::Call::PureVirtual->croak(); } |
292
|
|
|
|
|
|
|
|
293
|
|
|
|
|
|
|
=head1 AUTHOR |
294
|
|
|
|
|
|
|
|
295
|
|
|
|
|
|
|
Clinton Wolfe, with inspiration from Rob Speed, Chris Schammel, and Dave Hubbard. |
296
|
|
|
|
|
|
|
|
297
|
|
|
|
|
|
|
=cut |
298
|
|
|
|
|
|
|
|
299
|
|
|
|
|
|
|
1; |