File Coverage

blib/lib/Class/ReluctantORM/Collection.pm
Criterion Covered Total %
statement 3 47 6.3
branch 0 10 0.0
condition 0 3 0.0
subroutine 1 19 5.2
pod 15 16 93.7
total 19 95 20.0


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;