File Coverage

blib/lib/Class/ReluctantORM.pm
Criterion Covered Total %
statement 69 950 7.2
branch 4 368 1.0
condition 0 136 0.0
subroutine 23 133 17.2
pod 71 71 100.0
total 167 1658 10.0


line stmt bran cond sub pod time code
1             package Class::ReluctantORM;
2              
3 1     1   1158 use strict;
  1         1  
  1         37  
4 1     1   5 use warnings;
  1         2  
  1         92  
5              
6             =head1 NAME
7              
8             Class::ReluctantORM - An ORM emphasizing prefetching
9              
10             =head1 SYNOPSIS
11              
12             package Pirate;
13             use base 'Class::ReluctantORM';
14              
15             Pirate->build_class(
16             primary_key => 'pirate_id', # May be an arrayref for multi-col PKs
17             table => 'pirates',
18             schema => 'high_seas',
19             db_class => 'Some::DB::Class',
20             deletable => 0,
21             );
22             Pirate->has_one(Ship);
23              
24             # Elsewhere...
25             package main;
26              
27             # Fetch on primary key
28             my $p = Pirate->fetch(123);
29              
30             # Fetch on any field (dies on no results)
31             my @peeps = Pirate->fetch_by_name('Dread Pirate Roberts');
32              
33             # Same, with no dying
34             my @peeps = Pirate->search_by_name('Dread Pirate Roberts');
35              
36             # Make a pirate in memory
37             $matey = Pirate->new(name => 'Wesley');
38              
39             $matey->insert(); # Save to DB
40             $matey->name('Dread Pirate Roberts'); # Modify in memory
41             if ($matey->is_dirty) {
42             # Yes, we have unsaved changes
43             $matey->update(); # Commit to DB
44             }
45              
46             # Try to access a related object that hasn't been fetched
47             my $ship;
48             eval { $ship = $matey->ship(); };
49             if ($@) {
50             # Splat - Class::ReluctantORM throws exceptions if you access
51             # an unfetched relation
52             }
53              
54             # Fetch a pirate and his related ship
55             # See Class::ReluctantORM::Manual::Relationships
56             my $matey = Pirate->fetch_by_name_with_ship('Wesley');
57              
58             # Or more flexibly
59             my $matey = Pirate->fetch_deep(
60             name => 'Wesley',
61             with => { ship => {} },
62             );
63              
64             # Works
65             $ship = $matey->ship();
66              
67             # Lots more....
68              
69             =head1 DESCRIPTION
70              
71             Class::ReluctantORM, or CRO, is an ORM that uses exceptions to detect some coding practices that may lead to scalability problems while providing enhanced transparency into database accesses.
72              
73             =head2 What is an ORM?
74              
75             An ORM is an Object-Relational Mapping system. It treats tables in a database as classes, and rows in those tables as objects. Foreign key relationships among tables become aggregation (has-a) relationships among objects.
76              
77             Well-known ORMs include Perl's DBI::Class and Rose::DB, Ruby's ActiveRecord, and Java's
78              
79             =head2 Why use an ORM?
80              
81             =over
82              
83             =item Stay in the OOP mindset
84              
85             Thinking OOPishly and thinking RDBMSishly are quite different. By treating database rows as real objects, you stay in the OOP mindset. Some programmers will see a productivity gain from this.
86              
87             =item Reduce SQL usage to the hard cases
88              
89             Simple things are extremely easy, and require no SQL. Harder problems still require SQL, but you can isolate them more easily.
90              
91             =item Schema changes are much easier
92              
93             Many schema changes are detected automatically (column additions result in new methods, for example). You also have a Perl layer in which you can intercept changes at the class level, if needed.
94              
95             =item Possible RDBMS independence
96              
97             If you rely on the ORM to generate queries, it will speak a dialect specific to the database being used. You may be able to change databases later without major code changes.
98              
99             =item Reduce code duplication
100              
101             Many classes need the functionality of CRUD (create, retreive, update, delete). On WET (non-DRY) projects, many modules implement that functionality, in many places.
102              
103             =item Reduce inconsistency
104              
105             Likewise, there is no reason why 4 different modules
106             should name their search methods 4 different things.
107              
108             =back
109              
110              
111             =head2 Why NOT use an ORM?
112              
113             =over
114              
115             =item Opaque SQL generation
116              
117             The magic that goes into turning a method call into a database query can be difficult to unravel.
118              
119             =item Hiding queries behind methods hides costs
120              
121             It is easy to accidentally hammer a database by, for example, calling a single-row-fetching method in a loop.
122              
123             =item Difficult to rely on the ORM to generate efficient SQL
124              
125             Optimizing SQL usually means making vendor or dataset specific tweaks. ORMs may make that difficult or impossible, and the stuff that they generate will usually be fairly generic.
126              
127             =back
128              
129             =head2 Why use Class::ReluctantORM?
130              
131             =over
132              
133             =item It encourages you to combine fetches
134              
135             Because it is easy to detect exactly when a related, but unfetched, object is accessed (an exception is thrown), it is easy to determine exactly which fetches can be combined, and to keep those fetches trimmed down. See L
136              
137             =item Querying methods are named consistently
138              
139             Developers will generally be able to tell if a method will hit the database.
140              
141             =item A sophisticated, extensible query generation monitoring system
142              
143             You can easily create monitors to watch database activity - whether you are interested in the SQL being generated, the values returned, the data volume, or the wall time. And it is easy to write your own. See L
144              
145             =item It has a abstract SQL model
146              
147             CRO uses an abstract SQL model using real objects to represent pieces of a SQL statement. This allows more flexibility than some other approaches. See L.
148              
149             =item Perl-side triggers
150              
151             Run code before or after saves, retrieves, deletes, etc. Add and remove multiple triggers on each event. See L
152              
153             =item Mutator Filters
154              
155             Apply arbitrary transformations to data upon being read or written to the object. See L.
156              
157             =back
158              
159             =head2 Why NOT use Class::ReluctantORM?
160              
161             =over
162              
163             =item It has a tiny developer base.
164              
165             You might consider DBI::Class if you are looking for the go-to, widely-used ORM with excellent plugins and commericial support, or Rose::DB if you like the scrappy underdog approach.
166              
167             =item It is immature.
168              
169             There are some missing parts, though it is in production on our sites. But it may not support your favorite RDBMS, and there are pieces that are unpretty. It also doesn't have support that you might expect it to (like Moose integration, for example).
170              
171             =item You might not like it.
172              
173             The basic idea is that it will throw an exception if you do something stupid (well, that it can detect as stupid, anyway). The idea is that you then, thoughtfully and at implementation time (not deployment time), do something less stupid. You might not care for that approach - it's a little paternalistic. Also, its advantages are fewer in a production environment (presumably you already have all of your fetches tuned at that point).
174              
175             =back
176              
177             =head1 DOCUMENTATION ROADMAP
178              
179             =head2 The Manual
180              
181             L Start here for a narrative introduction to CRO.
182              
183             =head2 Alternate Base Classes
184              
185             Most CRO model classes will inherit directly from Class::ReluctantORM. These laternate base classes offer additional functionality for special circumstances.
186              
187             =over
188              
189             =item L - Base class for "type tables"
190              
191             =item L - Base class that audits database changes to a second, audit-log table
192              
193             =item L - Base class for instance-singleton classes, allowing behavior inheritance
194              
195             =back
196              
197             =head2 Major Core Subsystems
198              
199             =over
200              
201             =item L - RDBMS support
202              
203             =item L - SQL abstraction system
204              
205             =item L - Relationships between classes
206              
207             =item L - Database activity monitoring
208              
209             =item L - Transform data on read/write to the object.
210              
211             =item L - Cache fetched objects by their PKs
212              
213             =back
214              
215              
216             =head1 DOCUMENTATION FOR THIS MODULE ITSELF
217              
218             The remainder of this file is documentation for the Class::ReluctantORM module itself.
219              
220             =over
221              
222             =item L - methods that affect all CRO objects or classes
223              
224             =item L - How to configure your class
225              
226             =item L - Information about your class.
227              
228             =item L - various ways of creating an object
229              
230             =item L - methods related to primary keys
231              
232             =item L - create, update, and delete. Retrieve is covered under L.
233              
234             =item L - detect changes to in-memory data
235              
236             =item L - reading and writing the attributes of your objects
237              
238             =item L - methods related to Filters
239              
240             =item L - connect this class to other classes
241              
242             =item L - install and remove monitors from CRO objects.
243              
244             =item L - install and remove triggers
245              
246             =back
247              
248             =cut
249              
250              
251 1     1   5 use Carp;
  1         1  
  1         81  
252 1     1   6 use Scalar::Util qw(refaddr);
  1         1  
  1         86  
253              
254 1     1   1293 use Data::Dumper;
  1         8386  
  1         95  
255              
256 1     1   10 use base 'Class::ReluctantORM::Base';
  1         3  
  1         1381  
257 1     1   10 use base 'Class::ReluctantORM::OriginSupport';
  1         2  
  1         773  
258              
259              
260 1     1   9 use Class::ReluctantORM::Utilities qw(check_args install_method install_method_on_first_use install_method_generator conditional_load nz deprecated);
  1         2  
  1         89  
261 1     1   7 use Class::ReluctantORM::Exception;
  1         2  
  1         24  
262 1     1   850 use Class::ReluctantORM::DBH;
  1         4  
  1         33  
263 1     1   889 use Class::ReluctantORM::Driver;
  1         5  
  1         36  
264 1     1   833 use Class::ReluctantORM::Relationship;
  1         4  
  1         6  
265 1     1   28 use Class::ReluctantORM::SQL::Aliases;
  1         2  
  1         85  
266 1     1   837 use Class::ReluctantORM::SQL;
  1         5  
  1         10  
267 1     1   781 use Class::ReluctantORM::FetchDeep;
  1         2  
  1         26  
268 1     1   517 use Class::ReluctantORM::FilterSupport;
  1         4  
  1         34  
269 1     1   8 use Class::ReluctantORM::Collection;
  1         2  
  1         22  
270 1     1   573 use Class::ReluctantORM::Registry;
  1         4  
  1         9  
271 1     1   35 use Class::ReluctantORM::Registry::None;
  1         2  
  1         7  
272              
273             our $VERSION = "0.52_0";
274              
275             our $DEBUG = 0;
276             our $SOFT_TODO_MESSAGES = 0;
277             our $DEBUG_SQL = 0; # Set to true to print all SQL to STDERR
278              
279             our %PENDING_RELATIONS = (); # Delayed loading mechanism
280              
281             our %METHODS_TO_BUILD_ON_FIRST_USE = ();
282             our %METHOD_GENERATORS = ();
283             our @GLOBAL_MONITORS = ();
284             our %CLASS_METADATA = ();
285             our %REGISTRY_BY_CLASS;
286             our %GLOBAL_OPTIONS;
287             BEGIN {
288 1     1   189 $GLOBAL_OPTIONS{parse_where} = 1;
289 1         3 $GLOBAL_OPTIONS{parse_where_hard} = 1;
290 1         1 $GLOBAL_OPTIONS{populate_inverse_relationships} = 1;
291 1         4 $GLOBAL_OPTIONS{schema_cache_policy} = 'None';
292 1         9836 $GLOBAL_OPTIONS{schema_cache_file} = undef; # No sane default
293             }
294              
295             =head1 CRO-GLOBAL METHODS
296              
297             =head2 $setting = Class::ReluctantORM->get_global_option('option');
298              
299             =head2 Class::ReluctantORM->set_global_option('option', 'value');
300              
301             Reads or sets a global option. Global options take effect immediately, and affect all CRO classes and objects.
302              
303             Some options may be set on a per-class basis - see set_class_option.
304              
305             The option name provided must be on the following list:
306              
307             =over
308              
309             =item parse_where
310              
311             Boolean, default true. If true, try to convert SQL strings passed as the value of the 'where' option to search(), fetch_deep(), delete_where() and update() into Class::ReluctantORM::SQL::Where objects. (If the parsing attempt fails, see parse_where_hard for behavior.) If false, do not even attempt to parse; all strings are treated as raw_where (but SQl::Where objects you have constructed are handled normally).
312              
313             You can also control this on a per-query basis using the parse_where option to fetch_deep() and others.
314              
315             =item parse_where_hard
316              
317             Boolean, default true. If true, when a Where parsing attempt fails, throw an exception. If false, instead use the SQL string as a raw_where clause, and continue.
318              
319             =back
320              
321             =item populate_inverse_relationships
322              
323             Boolean, default true. Relationships may have an inverse (for example, if a Ship has-many Pirates, the Pirate has-one Ship). So when fetching a Ship and its Pirates, we can optionally set each Pirate to have its Ship already populated, as well.
324              
325             =item schema_cache_policy
326              
327             String enum. Controls behvior of schema scanning (column listings) at startup.
328              
329             =over
330              
331             =item NONE (default) Perform no schema caching. Columns will be listed on each table referenced in a build_class call; the scan will happen at process start (usually compile phase).
332              
333             =item SIMPLE If a cache file exists, read it and use it for all column info. If no cache file exists, perform the scan, then write the cache file. If the database schema changes, you'll need to manually delete the cache file to regenerate it.
334              
335             =item CLEAR_ON_ERROR Like SIMPLE, but will delete the cache file if a database error (of any kind, may not be related to schema changes) occurs. Provides a bit of auto-recovery if your process is restartable.
336              
337             =back
338              
339             =item schema_cache_file
340              
341             String absolute path to a writable file, where schema data will be cached. Ignored if schema_cache_policy is NONE. The file will be in JSON format. No default provided.
342              
343             =back
344              
345             =cut
346              
347             sub get_global_option {
348 0     0 1   my $inv = shift;
349 0           my $opt = shift;
350 0 0         unless (exists $GLOBAL_OPTIONS{$opt}) {
351 0           Class::ReluctantORM::Exception::Param::BadValue->croak
352             (
353             param => 'option_name',
354             value => $opt,
355             expected => 'one of ' . join(',', sort keys %GLOBAL_OPTIONS),
356             );
357             }
358 0           return $GLOBAL_OPTIONS{$opt};
359             }
360              
361             sub set_global_option {
362 0     0 1   my $inv = shift;
363 0           my $opt = shift;
364 0           my $val = shift;
365 0 0         unless (exists $GLOBAL_OPTIONS{$opt}) {
366 0           Class::ReluctantORM::Exception::Param::BadValue->croak
367             (
368             param => 'option_name',
369             value => $opt,
370             expected => 'one of ' . join(',', sort keys %GLOBAL_OPTIONS),
371             );
372             }
373 0           my $subname = '__' . $opt . '_setter';
374 0 0         if ($inv->can($subname)) {
375 0           $inv->$subname($val);
376             } else {
377 0           $GLOBAL_OPTIONS{$opt} = $val;
378             }
379             }
380              
381             sub __schema_cache_policy_setter {
382 0     0     my $inv = shift;
383 0           my $val = shift;
384 0           my @policies = Class::ReluctantORM::SchemaCache->policy_names;
385 0 0         unless ($val =~ (join('|', @policies))) {
386 0           Class::ReluctantORM::Exception::Param::BadValue->croak
387             (
388             param => 'schema_cache_policy',
389             value => $val,
390             expected => 'one of ' . (join(', ', @policies)),
391             );
392             }
393 0           $GLOBAL_OPTIONS{schema_cache_policy} = $val;
394             }
395              
396             =head2 @class_names = Class::ReluctantORM->list_all_classes();
397              
398             Lists all classes that are CRO derivates, and have had build_class called.
399              
400             =cut
401              
402             sub list_all_classes {
403 0     0 1   return keys %CLASS_METADATA;
404             }
405              
406             =head2 $driver_class = Class::ReluctantORM->default_driver_class();
407              
408             Returns the class name of the Driver used by the most CRO subclasses.
409              
410             =cut
411              
412             sub default_driver_class {
413 0     0 1   my $cro = shift;
414 0           my %votes_by_driver = ();
415 0           foreach my $class ($cro->list_all_classes) {
416 0           $votes_by_driver{ref($class->driver())}++;
417             }
418 0           my @winners =
419 0           map { $_->[0] }
420 0           sort { $b->[1] <=> $a->[1] }
421 0           map { [ $_, $votes_by_driver{$_} ] } keys %votes_by_driver;
422 0           return $winners[0];
423             }
424              
425             =head2 $bool = Class::ReluctantORM->is_class_available($cro_class);
426              
427             Returns a boolean indicating whether the given CRO class has been loaded yet.
428              
429             Note: If passed the special value 'SCALAR', always returns true.
430              
431             =cut
432              
433             sub is_class_available {
434 0     0 1   my $class = shift;
435 0           my $cro_class = shift;
436 0   0       return exists($CLASS_METADATA{$cro_class}) || ($cro_class eq 'SCALAR');
437             }
438              
439              
440             =head1 MODEL CLASS CONFIGURATION
441              
442             =head2 $class->build_class(%args);
443              
444             Sets up the class. Arguments:
445              
446             =over
447              
448             =item dbh
449              
450             The database handle used to talk to the database. This may be either a DBI handle or a Class::ReluctantORM::DBH subclass instance. You must provide either this arg or the db_class arg.
451              
452             =item db_class
453              
454             A class that knows how to connect to the database when its new() method is called with no arguments. The instance must be a Class::ReluctantORM::DBH subclass.
455              
456             =item schema
457              
458             Schema name in the database.
459              
460             =item table
461              
462             Table name in the database.
463              
464             =item primary_key
465              
466             Required. Must either be auto-populated, or you must explicitly provide value(s) when you do an insert.
467             New in v0.4, this may either be a string (for single-column keys) or
468             an arrayref of strings (for multi-column keys).
469              
470             =item fields (optional, array ref or hashref)
471              
472             If not provided, the $db_class->table_info
473             will be be called to determine the field list.
474              
475             You may also decouple field names from column names by passing a
476             hashref instead of an array ref. The hashref should
477             map class field names to table column names.
478              
479             =item ro_fields (optional)
480              
481             Unsettable fields. Default: all fields updatable.
482              
483             =item volatile_fields (optional)
484              
485             Optional arrayref of strings. Read-write accessors will be created for these fields, allowing you to store volatile information. This data will not be loaded or saved to the database, and the fields will not be listed by field_names() etc.
486              
487             =item insertable (optional)
488              
489             Default true. If present and false, insert() will throw an exception.
490              
491             =item updatable (optional)
492              
493             Default true. If present and false, update() will throw an exception.
494              
495             =item deletable (optional)
496              
497             Default true. If present and false, delete() will throw an exception.
498              
499             =item refresh_on_update (optional)
500              
501             Optional list of fields that should be refreshed after performing an UPDATE or INSERT
502             (perhaps because they were updated by a database trigger).
503              
504             =item registry (optional)
505              
506             Name of a Class::ReluctantORM::Registry subclass to use as the Registry for this class. If not
507             provided, defaults to Class::ReluctantORM::Registry->default_registry_class() . See Class::ReluctantORM::Registry for details.
508              
509             =back
510              
511             =cut
512              
513             # Move this out so that subclasses can use it
514             sub __build_class_arg_spec {
515             return (
516 0     0     one_of => [
517             [qw(db_class dbh)],
518             ],
519             mutex => [
520             [qw(lazy_fields non_lazy_fields)],
521             ],
522             optional => [qw(fields ro_fields volatile_fields insertable deletable updatable refresh_fields registry)],
523             required => [qw(primary_key schema table)],
524             );
525             }
526              
527             sub build_class {
528 0     0 1   my $class = shift;
529 0           my %args = check_args(
530             args => \@_,
531             $class->__build_class_arg_spec(),
532             );
533              
534 0 0         if ($DEBUG > 1) { print STDERR __PACKAGE__ . ':' . __LINE__ . " - In CRO build_class:\nClass: $class\nArgs:" . Dumper(\%args); }
  0            
535              
536 0 0         if (defined $CLASS_METADATA{$class}) { Class::ReluctantORM::Exception::Call::NotPermitted->croak("It appears that $class has already been initialized. You cannot call build_class twice."); }
  0            
537              
538             # Record class metadata
539 0           my %metadata = ();
540 0           $CLASS_METADATA{$class} = \%metadata;
541 0 0         for my $flag (qw(updatable deletable insertable)) { $metadata{$flag} = defined($args{$flag}) ? $args{$flag} : 1; }
  0            
542              
543 0           $class->__build_class_init_driver(\%metadata, \%args);
544 0           $class->__build_class_setup_fields(\%metadata, \%args);
545 0           $class->__build_class_setup_refresh_list(\%metadata, \%args);
546              
547             # OK, call super to setup field list and accessors
548 0           $class->SUPER::build_class(%args, fields => [ keys %{$metadata{fieldmap}} ]);
  0            
549              
550             # Setup fetchers and searchers
551 0           $class->__build_class_setup_fetchers($class->field_names());
552 0           $class->__build_class_setup_aggregators($class->field_names());
553              
554 0           $class->__build_class_setup_registry($args{registry});
555              
556             # Setup Relationships
557 0           $metadata{relations} = {};
558              
559             # Setup lazy/non-lazy
560 0           my @lazy_fields;
561 0 0         if ($args{lazy_fields}) {
    0          
562 0           @lazy_fields = @{$args{lazy_fields}};
  0            
563             } elsif ($args{non_lazy_fields}) {
564 0           my %non_lazy = map { $_ => 1 } (@{$args{non_lazy_fields}}, $class->primary_key_fields);
  0            
  0            
565 0           @lazy_fields = grep { ! exists $non_lazy{$_} } $class->field_names();
  0            
566             }
567 0 0         if ($DEBUG > 1) { print STDERR __PACKAGE__ . ':' . __LINE__ . " - In CRO build_class, have lazy fields:" . Dumper(\@lazy_fields); }
  0            
568 0           foreach my $field (@lazy_fields) {
569 0           $class->has_lazy($field);
570             }
571              
572             # Setup all other relationships
573 0           Class::ReluctantORM::Relationship->notify_class_available($class);
574             }
575              
576             sub __build_class_init_driver {
577 0     0     my ($class, $metadata, $args) = @_;
578              
579 0           for my $f (qw(table schema primary_key)) {
580 0           $metadata->{$f} = $args->{$f};
581             }
582              
583             # Repack primary key as an array if it's not already
584 0 0         $metadata->{primary_key} = ref($metadata->{primary_key}) eq 'ARRAY' ? $metadata->{primary_key} : [ $metadata->{primary_key} ];
585              
586             # Make sure we have a dbh
587 0           my ($dbh, $dbc);
588 0 0         if ($args->{db_class}) {
589 0           $dbc = $args->{db_class};
590 0           conditional_load($dbc);
591 0           Class::ReluctantORM::DBH->_quack_check($dbc);
592 0           $dbh = $dbc->new();
593             } else {
594 0           $dbh = $args->{dbh};
595             }
596              
597 0           $metadata->{driver} = Class::ReluctantORM::Driver->make_driver($class, $dbh, $dbc);
598             }
599              
600              
601             sub __build_class_setup_fields {
602 0     0     my ($class, $metadata, $args) = @_;
603              
604 0           my $dbc = $args->{db_class};
605              
606             # Get field-column map
607 0           my $fields = $args->{fields};
608 0 0         if ($fields) {
609 0 0 0       unless (ref($fields) eq 'ARRAY' || ref($fields) eq 'HASH') { Class::ReluctantORM::Exception::Param::ExpectedArrayRef->croak(param => 'fields'); }
  0            
610             # Turn arrays into hashes
611 0 0         if (ref($fields) eq 'ARRAY') { $fields = { map { $_ => $_ } @$fields }; }
  0            
  0            
612 0 0         unless (%$fields) { Class::ReluctantORM::Exception::Param::Empty->croak(param => 'fields'); }
  0            
613             } else {
614             # Load fields from table info
615 0 0         unless ($dbc->can('column_info')) {
616 0           Class::ReluctantORM::Exception::Param->croak(message => "If you are going to omit fields, db_class must support column_info method.", param => 'db_class');
617             }
618 0           $fields = $metadata->{driver}->read_fields($metadata->{schema}, $metadata->{table});
619              
620             # Confirm we got something
621 0 0         unless (keys %{$fields}) {
  0            
622 0           Class::ReluctantORM::Exception::Param->croak(message => 'Empty column list for schema ' . $metadata->{schema} . ', table ' . $metadata->{table} . ' - does table exist?',
623             param => 'table',
624             value => $metadata->{table},
625             )
626              
627             }
628             }
629 0           $metadata->{fieldmap} = $fields;
630              
631             # Make sure each primary key is in the field list
632 0           foreach my $pk (@{$metadata->{primary_key}}) {
  0            
633 0 0         unless (exists $fields->{$pk}) {
634 0           Class::ReluctantORM::Exception::Param->croak(message => 'Primary key(s) not found in column list for class ' . $class,
635             param => 'primary_key',
636             value => $pk,
637             );
638             }
639             }
640              
641             # Setup volatiles
642 0 0         if ($args->{volatile_fields}) {
643 0           foreach my $vf (@{$args->{volatile_fields}}) {
  0            
644 0           $class->add_volatile_field($vf);
645             }
646             }
647             }
648              
649             sub __build_class_setup_refresh_list {
650 0     0     my ($class, $metadata, $args) = @_;
651              
652 0   0       my $refreshes = $args->{refresh_on_update} || [];
653 0 0 0       if ($refreshes && ref($refreshes) ne 'ARRAY') {
654 0           Class::ReluctantORM::Exception::Param::ExpectedArrayRef->croak(param => 'refresh_on_update');
655             }
656              
657             # Make sure each primary keys are all on the list
658 0           foreach my $pk (@{$metadata->{primary_key}}) {
  0            
659 0 0         unless (grep {$_ eq $pk} @{$refreshes}) {
  0            
  0            
660 0           push @{$refreshes}, $pk;
  0            
661             }
662             }
663              
664 0           my $fields = $metadata->{fieldmap};
665              
666             # Make sure they're all on the field list
667 0           foreach my $rf (@{$refreshes}) {
  0            
668 0 0         unless (exists $fields->{$rf}) {
669 0           Class::ReluctantORM::Exception::Param->croak(message => "refresh on update fields must be present in field list", param => 'refresh_on_update');
670             }
671             }
672 0           $metadata->{refresh_on_update} = $refreshes;
673              
674             }
675              
676             sub __build_class_setup_fetchers {
677 0     0     my $class = shift;
678 0           my @fields = @_;
679 0           foreach my $field (@fields) {
680 0           foreach my $type ('search', 'fetch') {
681 0           my $name = $type . '_by_' . $field;
682             # install_method_on_first_use( ... ); # inlined
683             $Class::ReluctantORM::METHODS_TO_BUILD_ON_FIRST_USE{$class}{$name}
684 0     0     = sub { $class->_make_fetcher(
685             $field,
686             ($type eq 'fetch'),
687             undef,
688 0           ) };
689             }
690             }
691             }
692              
693             sub __build_class_setup_aggregators {
694 0     0     my $class = shift;
695 0           my @fields = @_;
696              
697             install_method_generator
698             (
699             $class,
700             sub {
701 0     0     my ($class, $proposed_method_name) = @_;
702 0           my %aggregators_by_name = map { lc($_->name) => $_ } Function->list_aggregate_functions();
  0            
703 0           my $regex = '^(' . join('|', keys %aggregators_by_name) . ')_of_(' . join('|', @fields) . ')$';
704 0           my ($aggregator_name, $field_name) = $proposed_method_name =~ $regex;
705 0 0         if ($aggregator_name) {
706 0           return $class->_make_aggregator(
707             $field_name,
708             $aggregators_by_name{$aggregator_name},
709             );
710             }
711              
712             # No patterns left - decline
713 0           return undef;
714 0           });
715             }
716              
717             sub __build_class_setup_registry {
718 0     0     my $class = shift;
719 0   0       my $registry_class = shift || Class::ReluctantORM::Registry->default_registry_class();
720 0 0         unless ($registry_class->isa('Class::ReluctantORM::Registry')) {
721 0           Class::ReluctantORM::Exception::Param::BadValue->croak(param => 'registry', error => 'Registry class must inherit from Class::ReluctantORM::Registry', value => $registry_class);
722             }
723 0           my $registry = $registry_class->new($class);
724 0           $REGISTRY_BY_CLASS{$class} = $registry;
725             }
726              
727             =head2 MyClass->add_volatile_field('field_name')
728              
729             Creates a volatile accessor/mutator method (getter/setter) with the given name. The field is volatile in the sense that its value is never saved to the database. Setting a volatile field does not affect dirtiness.
730              
731             =cut
732              
733             sub add_volatile_field {
734 0     0 1   my $class = shift;
735 0           my $vf = shift;
736             my $sub = sub {
737 0     0     my $self = shift;
738 0 0         if (@_) { $self->set($vf, shift); }
  0            
739 0           return $self->get($vf);
740 0           };
741 0           install_method($class, $vf, $sub);
742             }
743              
744             =head1 CLASS METADATA METHODS
745              
746             =head2 $reg = CroClass->registry();
747              
748             Returns the Registry associated with this CRO class, which provides an object caching mechanism. See Class::ReluctantORM::Registry.
749              
750             =cut
751              
752             sub registry {
753 0     0 1   my $inv = shift;
754 0 0         my $class = ref($inv) ? ref($inv) : $inv;
755 0           return $REGISTRY_BY_CLASS{$class};
756             }
757              
758              
759             sub __metadata {
760 0     0     my $inv = shift;
761 0 0         my $class = ref($inv) ? ref($inv) : $inv;;
762              
763 0           my $hash = $CLASS_METADATA{$class};
764 0 0         unless (defined $hash) {
765 0           Class::ReluctantORM::Exception::Call::ExpectationFailure->croak
766             (
767             error => "$class appears to be unitialized. Must call build_class before calling __metadata().",
768             );
769             }
770 0           return $hash;
771             }
772              
773             sub __alias_metadata {
774 0     0     my $cro = shift;
775 0           my $target_class = shift;
776 0           my $alias = shift;
777 0           $CLASS_METADATA{$alias} = $CLASS_METADATA{$target_class};
778 0           $REGISTRY_BY_CLASS{$alias} = $REGISTRY_BY_CLASS{$target_class};
779             }
780              
781             =begin devdocs
782              
783             =head2 $CroClass->_change_registry($reg_obj);
784              
785             =head2 $CroClass->_change_registry($reg_class);
786              
787             Changes the Registry object used to cache objects for this class. You can pass a constructed Registry subclass, or the class name (in which case we will call new() on it).
788              
789             The existing registry is purged before switching.
790              
791             =end devdocs
792              
793             =cut
794              
795             sub _change_registry {
796 0     0     my $cro_inv = shift;
797 0 0         if (ref($cro_inv)) {
798 0           Class::ReluctantORM::Exception::Call::NotPermitted::ClassMethodOnly->croak(method => '_change_registry');
799             }
800 0           my $cro_class = $cro_inv;
801              
802 0           my $reg_arg = shift;
803 0 0         unless ($reg_arg->isa('Class::ReluctantORM::Registry')) {
804 0           Class::ReluctantORM::Exception::Param::WrongType->croak(param => 'registry', value => $reg_arg, error => 'registry must inherit from Class::ReluctantORM::Registry.');
805             }
806              
807             # OK, purge existing reg
808 0           $cro_class->registry->purge_all();
809              
810 0           my $reg;
811 0 0         unless (ref($reg_arg)) {
812 0           $reg = $reg_arg->new($cro_class);
813             }
814              
815 0           $REGISTRY_BY_CLASS{$cro_class} = $reg;
816             }
817              
818             =head2 $driver = $class->driver();
819              
820             Returns the Class::ReluctantORM::Driver object that provides backend-specific functionality.
821              
822             =cut
823              
824             sub driver {
825 0     0 1   my $inv = shift;
826 0   0       my $class = ref($inv) || $inv;
827 0           return $class->__metadata()->{driver};
828             }
829              
830             =head2 $tablename = $class->table_name();
831              
832             Returns the name of the table for this class, in the case expected by the database.
833              
834             =cut
835              
836             sub table_name {
837 0     0 1   my $inv = shift;
838 0   0       my $class = ref($inv) || $inv;
839 0           my $name = $class->__metadata()->{table};
840 0           return $class->driver->table_case($name);
841             }
842              
843             =head2 $schemaname = $class->schema_name();
844              
845             Returns the name of the schema for this class.
846              
847             =cut
848              
849             sub schema_name {
850 0     0 1   my $inv = shift;
851 0   0       my $class = ref($inv) || $inv;
852 0           my $name = $class->__metadata()->{schema};
853 0 0         unless ($name) { return ''; }
  0            
854 0           return $class->driver->schema_case($name);
855             }
856              
857             =head2 $str = $class->full_table_name();
858              
859             Returns a quoted, dotted version of the name, using the quote character and name spearator that the database expects.
860              
861             Postgres example: "foo_schema"."bar_table"
862              
863             =cut
864              
865             sub full_table_name {
866 0     0 1   my $inv = shift;
867 0   0       my $class = ref($inv) || $inv;
868 0           my $d = $class->driver();
869 0 0         return ($class->schema_name ?
870             $d->open_quote() . $class->schema_name . $d->close_quote . $d->name_separator : '')
871             . $d->open_quote() . $class->table_name . $d->close_quote();
872             }
873              
874             =head2 $colname = $class->column_name($field_name, $field_name2, ..);
875              
876             Returns the database column underlying the given field.
877              
878             If more than one field is given, returns a list or arrayref,
879             depending on context.
880              
881             =cut
882              
883             sub column_name {
884 0     0 1   my $inv = shift;
885 0   0       my $class = ref($inv) || $inv;
886              
887 0           my $driver = $class->driver;
888 0           my @cols;
889 0           foreach my $fieldname (@_) {
890 0           push @cols, $driver->column_case($class->__metadata()->{fieldmap}{$fieldname});
891             }
892 0 0         return wantarray ? @cols : ((@_ > 1) ? \@cols : $cols[0]);
    0          
893             }
894              
895             =head2 $fieldname = $class->field_name($column_name, $column_name2,...);
896              
897             Returns the object field that represents the given database column.
898              
899             If more than one column is given, returns a list or arrayref,
900             depending on context.
901              
902             =cut
903              
904             sub field_name {
905 0     0 1   my $inv = shift;
906 0   0       my $class = ref($inv) || $inv;
907 0           my @colnames = @_;
908 0           my %invmap = reverse %{$class->__metadata()->{fieldmap}};
  0            
909 0           my @fields = @invmap{@colnames};
910 0 0         return wantarray ? @fields : ((@_ > 1) ? \@fields : $fields[0]);
    0          
911             }
912              
913             =head2 $fieldname = $class->first_primary_key_field();
914              
915             Returns the name of the first primary key field for this class.
916              
917             This is probably a bad idea - you may want to use primary_key_fields instead.
918              
919             =cut
920              
921             sub first_primary_key_field {
922 0     0 1   my @pks = shift->primary_key_fields();
923 0           return $pks[0];
924             }
925              
926             =head2 @pks = $class->primary_key_fields();
927              
928             Returns the names of the primary key fields for this class. Returns an
929             array ref in scalar context.
930              
931             =cut
932              
933             sub primary_key_fields {
934 0     0 1   my $inv = shift;
935 0   0       my $class = ref($inv) || $inv;
936 0           my $pks = $class->__metadata()->{primary_key};
937 0 0         return wantarray ? @$pks : $pks;
938             }
939              
940             =head2 $bool = $o->is_field_primary_key('fieldname');
941              
942             Returns true if the named field is a primary key.
943              
944             =cut
945              
946             sub is_field_primary_key {
947 0     0 1   my $self = shift;
948 0           my $fieldname = shift;
949 0           return grep { $_ eq $fieldname } $self->primary_key_fields();
  0            
950             }
951              
952             =head2 $fieldname = $class->first_primary_key_column();
953              
954             Returns the name of the first primary key column for this class, in database column case.
955              
956             This is probably a bad idea - you may want to use primary_key_columns instead.
957              
958             =cut
959              
960              
961             sub first_primary_key_column {
962 0     0 1   my @pks = shift->primary_key_columns();
963 0           return $pks[0];
964             }
965              
966             =head2 @pks = $class->primary_key_columns();
967              
968             Returns the name of the primary key columns for this class, in database column case. Returns an
969             array ref in scalar context.
970              
971             =cut
972              
973             sub primary_key_columns {
974 0     0 1   my $inv = shift;
975 0   0       my $class = ref($inv) || $inv;
976 0           my @pks = $class->column_name(@{$class->__metadata()->{primary_key}});
  0            
977 0 0         return wantarray ? @pks : \@pks;
978             }
979              
980             =head2 $int = $class->primary_key_column_count();
981              
982             Returns the number of primary key columns for the class.
983              
984             =cut
985              
986             sub primary_key_column_count {
987 0     0 1   my $self = shift;
988 0           my @cols = $self->primary_key_columns();
989 0           return scalar(@cols);
990             }
991              
992             =head2 @cols = $class->column_names();
993              
994             Returns the list of database columns, in the same order as field_names.
995              
996             =cut
997              
998             sub column_names {
999 0     0 1   my $inv = shift;
1000 0   0       my $class = ref($inv) || $inv;
1001 0   0       return @{$class->__metadata()->{columns} ||= [ map { $class->column_name($_) } $class->field_names ]};
  0            
  0            
1002             }
1003              
1004             =head2 @columns = $class->audit_columns();
1005              
1006             Returns a list of any columns that are expected to be automatically populated as auditing data. If the class is not being audited, this list is empty. See L.
1007              
1008             =cut
1009              
1010 0     0 1   sub audit_columns { return (); }
1011              
1012             # Takes a list of field or column names and returns a list
1013             # of things that are definitely columns
1014             sub __to_column_name {
1015 0     0     my $inv = shift;
1016 0   0       my $class = ref($inv) || $inv;
1017 0           my @candidates = @_;
1018 0           my @results;
1019 0           my %columns = map { $_ => 1 } $class->column_names();
  0            
1020 0           my %columns_by_fields = %{$class->__metadata()->{fieldmap}};
  0            
1021              
1022 0           foreach my $c (@candidates) {
1023 0 0         if (exists $columns{$c}) {
    0          
1024 0           push @results, $c;
1025             } elsif (exists $columns_by_fields{$c}) {
1026 0           push @results, $columns_by_fields{$c};
1027             } else {
1028 0           Class::ReluctantORM::Exception::Param::BadValue->croak(param => 'column or field name', value => $c, frames => 2);
1029             }
1030             }
1031              
1032 0           return @results;
1033             }
1034              
1035              
1036             =head2 @fields = $class->fields();
1037              
1038             =head2 @fields = $class->field_names();
1039              
1040             Returns a list of the fields in the class.
1041              
1042             =cut
1043              
1044             # field_names() inherited from Class
1045              
1046 0     0 1   sub fields { return shift->field_names(); }
1047              
1048             =head2 @fields = $class->field_names_including_relations()
1049              
1050             Returns a merged list of both the direct fields as well fields defined via Relationships.
1051              
1052             =cut
1053              
1054             sub field_names_including_relations {
1055 0     0 1   my $inv = shift;
1056 0           return ($inv->field_names(), $inv->relationship_names());
1057             }
1058              
1059             =head2 @fields = $class->refresh_fields();
1060              
1061             =head2 @cols = $class->refresh_columns();
1062              
1063             Returns a list of the fields or columns that should be refreshed
1064             on each update or insert.
1065              
1066             =cut
1067              
1068             sub refresh_fields {
1069 0     0 1   my $inv = shift;
1070 0   0       my $class = ref($inv) || $inv;
1071 0           return @{$class->__metadata()->{refresh_on_update}};
  0            
1072             }
1073             sub refresh_columns {
1074 0     0 1   my $inv = shift;
1075 0   0       my $class = ref($inv) || $inv;
1076 0           return $class->column_name($class->refresh_fields());
1077             }
1078              
1079              
1080             =head2 @fields = $class->essential_fields();
1081              
1082             Returns a list of the fields that are always fetched when an object of
1083             this type is fetched from the database. Normally this is the same as
1084             fields(), but some Relationships (HasLazy, for example) will modify this.
1085              
1086             =cut
1087              
1088             sub essential_fields {
1089 0     0 1   my $inv = shift;
1090 0   0       my $class = ref($inv) || $inv;
1091              
1092             # If a field appears on the relations list, remove it from the
1093             # essentials list.
1094 0           my %rels_by_name = %{$class->relationships()};
  0            
1095 0           my @essentials = grep { not(exists($rels_by_name{$_})) } $class->fields();
  0            
1096 0           return @essentials;
1097             }
1098              
1099             =head2 @fields = $class->essential_sql_columns($table);
1100              
1101             Returns a list of SQL::Column objects that are always fetched when an object of
1102             this type is fetched from the database. Normally this is the same as
1103             sql_columns(), but some Relationships (HasLazy, for example) will modify this.
1104              
1105             Optionally, pass in a SQL::Table reference to specify the Table instance to link each column to.
1106              
1107             =cut
1108              
1109             sub essential_sql_columns {
1110 0     0 1   my $inv = shift;
1111 0   0       my $class = ref($inv) || $inv;
1112 0   0       my $table = shift || Class::ReluctantORM::SQL::Table->new($class);
1113              
1114 0           my @col_names = $class->column_name($class->essential_fields);
1115             #print STDERR "Hvae essential columns for table " . $table->table . ":\n" . Dumper(\@col_names);
1116 0           my @cols = map {
1117 0           Class::ReluctantORM::SQL::Column->new(
1118             table => $table,
1119             column => $_
1120             );
1121             } @col_names;
1122 0           return @cols;
1123              
1124             }
1125              
1126              
1127             =head2 $bool = $class->is_static();
1128              
1129             Returns true if the class is "static" - usually implemented via Class::ReluctantORM::Static. Such classes fetch all rows on the first fetch, and tehn cache thier results for the life of the process.
1130              
1131             =cut
1132              
1133 0     0 1   sub is_static { return 0; }
1134              
1135             =head2 $bool = $class->updatable();
1136              
1137             Returns true if this class permits update() to be called.
1138              
1139             =cut
1140              
1141             sub updatable {
1142 0     0 1   my $inv = shift;
1143 0   0       my $class = ref($inv) || $inv;
1144 0           return $class->__metadata()->{updatable};
1145             }
1146              
1147             =head2 $bool = $class->deletable();
1148              
1149             Returns true if this class permits delete() to be called.
1150              
1151             =cut
1152              
1153             sub deletable {
1154 0     0 1   my $inv = shift;
1155 0   0       my $class = ref($inv) || $inv;
1156 0           return $class->__metadata()->{deletable};
1157             }
1158              
1159             =head2 $bool = $class->insertable();
1160              
1161             Returns true if this class permits insert() to be called.
1162              
1163             =cut
1164              
1165             sub insertable {
1166 0     0 1   my $inv = shift;
1167 0   0       my $class = ref($inv) || $inv;
1168 0           return $class->__metadata()->{insertable};
1169             }
1170              
1171              
1172              
1173             #==============================================================#
1174             # Constructors
1175             #==============================================================#
1176              
1177             =head1 CONSTRUCTORS
1178              
1179             There are three classes of constructors:
1180              
1181             =over
1182              
1183             =item memory only
1184              
1185             These constructors, new() and clone(), only create an
1186             object in memory. Use insert() to commit them to the database.
1187              
1188             =item database fetch
1189              
1190             These constructors, fetch() and search(), take an existing
1191             database row and turn it into an object in memory.
1192              
1193             =item memory and database
1194              
1195             The create() constructor creates a new row in the database and
1196             returns the new object.
1197              
1198             =back
1199              
1200             Fetch and Search differ in their handling of empty result
1201             sets: fetch methods throw an exception if nothing is found,
1202             while search methods simply return undef or an empty list.
1203              
1204              
1205             =cut
1206              
1207             =head2 $o = $class->new(field1 => $value1, ...);
1208              
1209             Creates a new object in memory only (no database contact).
1210              
1211             =cut
1212              
1213             sub new {
1214 0     0 1   my $class = shift;
1215              
1216             # Allow passing hash or hashref
1217 0           my $hash_ref = {};
1218 0 0         if (@_ == 1) {
    0          
1219 0           $hash_ref = shift;
1220 0 0         unless (ref($hash_ref) eq 'HASH') { Class::ReluctantORM::Exception::Param::ExpectedHashRef->croak(); }
  0            
1221             } elsif (@_ % 2) {
1222 0           Class::ReluctantORM::Exception::Param::ExpectedHash->croak();
1223             } else {
1224 0           $hash_ref = { @_ };
1225             }
1226              
1227 0 0         if ($DEBUG > 1) { print STDERR __PACKAGE__ . ":" . __LINE__ . " - have new params:\n" . Dumper($hash_ref); }
  0            
1228              
1229 0           my @allowable_args = ($class->field_names(), $class->relationship_names());
1230 0           foreach my $arg (keys %$hash_ref) {
1231 0 0         unless (grep {$arg eq $_} @allowable_args) {
  0            
1232 0           Class::ReluctantORM::Exception::Param::Spurious->croak(param => $arg);
1233             }
1234             }
1235              
1236 0           my $self = $class->SUPER::new($hash_ref);
1237 0           $self->{_dirty_fields} = {};
1238 0           $self->{_is_inserted} = 0;
1239              
1240             # Check registry for a hit
1241 0           my $existing;
1242 0 0         if ($self->has_all_primary_keys_defined()) {
1243 0           my $existing = $class->registry->fetch($self->id());
1244 0 0         if ($existing) {
1245 0           $self = $existing; # will cause a registry purge
1246             }
1247             }
1248              
1249             # Force store of this object in registry (either it is new or it was just purged)
1250 0           $class->registry->store($self);
1251              
1252 0 0         unless ($existing) {
1253             # Set fields dirty - have to do this manually here since SUPER::new calls set(),
1254             # not the actual mutator.
1255 0           foreach my $f ($class->field_names) {
1256 0 0         if (exists $hash_ref->{$f}) {
1257 0 0         if ($DEBUG > 1) { print STDERR __PACKAGE__ . ':' . __LINE__ . " - in new, marking dirty field: $f\n"; }
  0            
1258 0           $self->_mark_field_dirty($f);
1259             }
1260             }
1261 0 0         if ($DEBUG > 1) { print STDERR __PACKAGE__ . ':' . __LINE__ . " - after new, have dirty fields :" . Dumper([$self->dirty_fields]); }
  0            
1262             }
1263              
1264             # Look for relations and perform implicit setup
1265 0           foreach my $rel ($self->relationships) {
1266 0           my $rel_field = $rel->method_name();
1267 0 0         next unless exists $hash_ref->{$rel_field};
1268 0 0 0       if ($existing && $existing->is_fetched($rel_field)) {
1269 0           $rel->merge_children($self, $hash_ref->{$rel_field});
1270             } else {
1271 0           $rel->_handle_implicit_new($self, $hash_ref);
1272             }
1273             }
1274              
1275 0           $self->capture_origin();
1276              
1277 0           return $self;
1278             }
1279              
1280             =head2 $o = $class->create(field1 => $value1, ...);
1281              
1282             Creates a new object in memory, and creates a matching row in the database.
1283              
1284             =cut
1285              
1286             sub create {
1287 0     0 1   my $class = shift;
1288 0           my $self = $class->new(@_);
1289 0           $self->insert();
1290 0           foreach my $rel ($self->relationships) {
1291 0           $rel->_handle_implicit_create($self, { @_ });
1292             }
1293 0           return $self;
1294             }
1295              
1296              
1297             =head2 $o = $class->fetch(123);
1298              
1299             =head2 $o = $class->fetch(key1_name => $key1_val, key2_name => key2_val...);
1300              
1301             Retrieves the object from the database whose primary
1302             key matches the given argument(s).
1303              
1304             In the first form, valid only for classes with a single-column
1305             primary key, the one primary value must be provided.
1306              
1307             In the second form, you may specify values for multi-column
1308             primary keys. Any PK columns not specified will be interpreted
1309             as null. You may specify either field names or column names; they
1310             will be interpreted first as column names, and if that fails,
1311             will be treated as field names.
1312              
1313             If no such object exists, an Class::ReluctantORM::Exception::Data::NotFound is thrown.
1314             For a gentler approach, use the search() family.
1315              
1316             =cut
1317              
1318             sub fetch {
1319 0     0 1   my $class = shift;
1320 0           my %pk;
1321              
1322             # Check args
1323 0 0         if (!@_) {
    0          
    0          
1324 0           Class::ReluctantORM::Exception::Param::Missing->croak(param => 'primary key value');
1325             } elsif (@_ == 1) {
1326 0 0         unless ($class->primary_key_column_count == 1) { Class::ReluctantORM::Exception::Data::NeedMoreKeys->croak(); }
  0            
1327 0           $pk{$class->first_primary_key_column} = shift;
1328             } elsif (@_ % 2) {
1329 0           Class::ReluctantORM::Exception::Param::ExpectedHash->croak();
1330             } else {
1331 0           my %args = @_;
1332 0           my @cols = keys %args;
1333 0           @pk{$class->__to_column_name(@cols)} = @args{@cols};
1334             }
1335              
1336             # Build Where clause
1337 0           my $where = Where->new();
1338 0           my $table = Table->new($class);
1339 0           foreach my $colname (keys %pk) {
1340 0           my $col = Column->new(
1341             table => $table,
1342             column => $colname,
1343             );
1344 0           my $prm = Param->new();
1345 0           $prm->bind_value($pk{$colname});
1346              
1347 0           $where->and(Criterion->new('=', $col, $prm));
1348             }
1349              
1350 0           return $class->fetch_deep(where => $where, with => {});
1351              
1352             }
1353              
1354             =head2 @objects = $class->fetch_all([order => 'order_clause']);
1355              
1356             Fetches all rows from the table, optionally ordered by the given order clause.
1357              
1358             For pagination support, see search().
1359              
1360             =cut
1361              
1362             sub fetch_all {
1363 0     0 1   my $class = shift;
1364 0           return $class->search(where => Where->new(), @_);
1365             }
1366              
1367             =head2 @objects = $class->fetch_deep( FIELD_NAME => $value, %common_options);
1368              
1369             =head2 @objects = $class->fetch_deep( where => $where_obj, %common_options);
1370              
1371             =head2 @objects = $class->fetch_deep( where => $sql_string, execargs => \@binds, parse_where => 0, %common_options);
1372              
1373             Performs a query with broad and/or deep prefetching. The three forms offer different ways of specifying search criteria.
1374              
1375             In the first form, provide exactly one field name with value. The search operator will be an '='.
1376              
1377             In the second form, provide a Class::ReluctantORM::SQL::Where object. It may contain Params, which must have their bind values already set.
1378              
1379             In the third form, provide a SQL string in a dialect that your Driver will understand. You may use '?' to represent a bind placeholder, and provide the bind values in the execargs argument. Depending on the values of the global options 'parse_where' and 'parse_where_hard', CRO may attempt to use the Driver to parse the SQL string into a Where object (which has certain advantages internally, especially for object inflation). If this fails, a ParseError exception will be thrown. You may disable this behavior with parse_where. Even if parse_where is false, the SQL string will still be mangled - we need to perform table-realiasing. Table alias macros are supported.
1380              
1381              
1382              
1383             =head2 @objects = $class->fetch_deep( where => $clause, execargs => [], with => { subfield => {}}, hint => '', limit => 5, offset => 6, order_by => '', parse_where => 0 );
1384              
1385             Common options:
1386              
1387             =over
1388              
1389             =item limit
1390              
1391             Optional integer. order_by is required if you use this (otherwise your results are nondeterministic). Limits the number of top-level objects. Due to JOINs, more rows may be actually returned. Better drivers can do this in SQL, but some drivers may be obliged to implement this in Perl. Some drivers may place restrictions on the WHERE clause if you use limit (like only permitting a where to reference the main table).
1392              
1393             =item offset
1394              
1395             Option integer, onlly permitted if limit is provided. Skip this many records.
1396              
1397             =item order_by
1398              
1399             Optional sort instructions. Provide either a Class::ReluctantORM::SQL::OrderBy, or a SQL string. You may only reference columns from the primary table. Some drivers may be obliged to implement this in Perl.
1400              
1401             =item hint
1402              
1403             Optional driver hints. See your driver documentation.
1404              
1405             =item with
1406              
1407             Prefetching instructions. See below and Class::ReluctantORM::Manual::Prefetching .
1408              
1409             =back
1410              
1411             To specify the prefetch tree, provide the 'with' parameter as a hashref. Name each subfield by
1412             method_name, using an empty hashref to denote a leaf. For example, if you are calling
1413             Pirate->fetch_deep, and you want the pirate's ship and parrot to be
1414             prefetched, use with => {parrot => {}, ship => {}}. To get the ship's home port as well, use
1415             with => {parrot => {}, ship => { home_port => {}}} .
1416              
1417              
1418             It is an error to pass unrecognized parameters to this method.
1419              
1420             In list context, all results are returned as a list. In scalar context, only the first top-level
1421             result is returned. If the query results in empty results, an exception is thrown. See search_deep
1422             for an exceptionless alternative.
1423              
1424             =cut
1425              
1426             # Implemented in Class::ReluctantORM::FetchDeep
1427              
1428             =head2 $object = $class->search($id);
1429              
1430             =head2 @objects = $class->search(where => $clause, execargs => [execargs], order_by => $clause, limit => 5, offset => 3);
1431              
1432             In the first form, acts as a non-fatal fetch(). You may only use this form if your class has a single-column primary key.
1433              
1434             In the second form, full-fledged search facility.
1435             ou
1436             In either form, returns all results as a list in array context, or first result in scalar context. In the case of no results, returns an empty list in list context or undef in scalar context.
1437              
1438             The where clause is the only required option. Use column names, not field names (though they are usually the same). Do not include the word 'WHERE'. You may use placeholders ('?'), so long as you include the execargs argument as well, which should be an arrayref of your arguments.
1439              
1440             Supports pagination.
1441              
1442             =cut
1443              
1444             sub search {
1445 0     0 1   my $class = shift;
1446 0 0         if (@_ == 1) {
1447 0 0         unless (@{[$class->primary_key_columns]} == 1) {
  0            
1448 0           Class::ReluctantORM::Exception::Call::NotPermitted->croak('You may only use the single-argument form of search() with classes that have single-column primary keys.');
1449             }
1450 0           my $pkc = ($class->primary_key_columns)[0];
1451 0           my $prm = Param->new();
1452 0           $prm->bind_value($_[0]);
1453 0           @_ = (
1454             where => Where->new(Criterion->new('=', Column->new(column => $pkc), $prm)),
1455             );
1456             }
1457 0 0         if (@_ % 2) { Class::ReluctantORM::Exception::Param::ExpectedHash->croak(); }
  0            
1458 0           my %args = @_;
1459              
1460 0 0         if (exists $args{with}) {
1461 0           Class::ReluctantORM::Exception::Param::Spurious->croak(value => $args{with}, param => 'with', error => 'search() does not take a "with" parameter. Did you mean search_deep()?');
1462             }
1463              
1464 0           $args{with} = {};
1465 0           return $class->search_deep(%args);
1466             }
1467              
1468             =head2 $o = $class->search_by_FIELD($value);
1469              
1470             =head2 @objects = $class->search_by_FIELD($value);
1471              
1472             Similar to fetch_by_FIELD, but returns undef or an empty list
1473             when no results are available, rather than throwing an exception.
1474              
1475             =cut
1476              
1477             # Created during call to build_class
1478              
1479              
1480             =head2 @objects = $class->search_deep( FIELD_NAME => $value, with => { subfield => {}}, hint => '', limit => 5, offset => 6, order_by => '' );
1481              
1482             =head2 @objects = $class->search_deep( where => $clause, execargs => [], with => { subfield => {}}, hint => '', limit => 5, offset => 6, order_by => '' );
1483              
1484             Operates identically to fetch_deep, but does not
1485             throw an exception if no results are found.
1486              
1487             =cut
1488              
1489             # Implemented in Class::ReluctantORM::FetchDeep
1490              
1491             =head2 $pirate->fetch_deep_overlay(with => \%with);
1492              
1493             =head2 Pirate->fetch_deep_overlay(with => \%with, objects => \@pirates);
1494              
1495             Given an existing, already-fetched object, performs an afterthought fetch - returning to the database to fetch additional related objects.
1496              
1497             Other methods allow you to do this on a per-relation-basis (ie, $pirate->fetch_ship()) or to fetch deeply, starting with one relation ($ship->pirates->fetch_deep(with => {booties => {}})) . This method, however, acts on the parent object, allowing you to fetch accross multiple relations in one query.
1498              
1499             In the first form, one query is performed to "re-fetch" a copy of the object, then the original is merged with the copy.
1500              
1501             In the second form, multiple objects may be re-fetched with one query.
1502              
1503             While merging, the fresh copy from the database wins all conflicts. Additionally, if you re-fetch over a relation you have modified, the changes are lost. Finally, there is nothing stopping you from fetching a "shallower" tree than you originally fetched.
1504              
1505             =cut
1506              
1507             #==============================================================#
1508             # Primary Keys
1509             #==============================================================#
1510              
1511              
1512             =head1 PRIMARY KEYS
1513              
1514             =cut
1515              
1516             =head2 $key = $o->id();
1517              
1518             =head2 $key_href = $o->id();
1519              
1520             =head2 @keys = $o->id();
1521              
1522             Returns the primary key value(s) for this object. If $o->is_inserted()
1523             is false, this will return undef.
1524              
1525             In the first form, (scalar context), if the class has only one
1526             primary key column, the primary key value is returned. If the object has not been inserted, undef is returned.
1527              
1528             In the second form (scalar context), if the class a multi-column primary key, a hashref is returned with the primary keys listed by their field names. If the object has not been inserted, undef is returned.
1529              
1530             In the third form, (list context), the primary key values are returned
1531             as a list, guarenteed to be in proper PK definition order. If the
1532             object has not been inserted, an empty list is returned (NOT a
1533             list of undefs, which could be confused with an all-NULL primary key)
1534              
1535             Use $class->primary_key_fields to get the names of the primary key fields.
1536              
1537             =cut
1538              
1539             =head2 $key = $o->primary_key();
1540              
1541             =head2 $key_href = $o->primary_key();
1542              
1543             =head2 @keys = $o->primary_key();
1544              
1545             =head2 $key = $o->primary_keys();
1546              
1547             =head2 $key_href = $o->primary_keys();
1548              
1549             =head2 @keys = $o->primary_keys();
1550              
1551             primary_key() and primary_keys() are aliases for id().
1552              
1553             =cut
1554              
1555 0     0 1   sub primary_key { return shift->id(); }
1556 0     0 1   sub primary_keys { return shift->id(); }
1557              
1558             sub id {
1559 0     0 1   my $self = shift;
1560 0           my @pk_fields = $self->primary_key_fields();
1561 0 0         if (@pk_fields == 1) {
1562 0           my $method = $pk_fields[0];
1563 0 0         return wantarray ? ($self->$method()) : $self->$method;
1564             } else {
1565 0 0         if (wantarray) {
1566 0 0         unless ($self->is_inserted()) { return (); }
  0            
1567 0           return map { $self->$_ } @pk_fields;
  0            
1568             } else {
1569 0 0         unless ($self->is_inserted()) { return undef; }
  0            
1570 0           return { map { $_ => $self->$_ } @pk_fields };
  0            
1571             }
1572             }
1573             }
1574              
1575             =head2 $bool = $obj->has_all_primary_keys_defined();
1576              
1577             Returns true if all primary key columns have a defined value.
1578              
1579             If this is true, we can reliably identify this object in a unique way.
1580              
1581             =cut
1582              
1583             sub has_all_primary_keys_defined {
1584 0     0 1   my $self = shift;
1585 0           foreach my $pkf ($self->primary_key_fields()) {
1586 0 0         unless (defined($self->raw_field_value($pkf))) {
1587 0           return 0;
1588             }
1589             }
1590 0           return 1;
1591             }
1592              
1593             #==============================================================#
1594             # CRUD
1595             #==============================================================#
1596              
1597             =head1 CRUD
1598              
1599             =head2 $o->insert();
1600              
1601             Commits a newly created object into the database.
1602              
1603             If the class was built with 'refresh_on_update' fields, these fields are fetched,
1604             using a single query for the insert and the fetch. The primary key is always fetched.
1605              
1606             If the object already has been inserted, dies.
1607              
1608             =cut
1609              
1610             sub insert {
1611 0     0 1   my $self = shift;
1612              
1613             # Must allow insert
1614 0 0         unless ($self->insertable) {
1615 0           Class::ReluctantORM::Exception::Call::NotPermitted->croak(message => 'This class is configured to not permit inserts. See Class::ReluctantORM->build_class().');
1616             }
1617              
1618             # Prevent obvious double inserts
1619 0 0         if ($self->is_inserted()) {
1620 0           Class::ReluctantORM::Exception::Data::AlreadyInserted->croak(primary_key => Dumper((scalar $self->primary_key)));
1621             }
1622              
1623 0           $self->__run_triggers('before_insert');
1624              
1625 0           $self->_check_for_cascade_on_upsert();
1626              
1627             # Build SQL
1628 0           my $sql = Class::ReluctantORM::SQL->new('insert');
1629 0           my $table = Class::ReluctantORM::SQL::Table->new($self);
1630 0           $sql->table($table);
1631              
1632             # Build input columns
1633 0           foreach my $f ($self->dirty_fields()) {
1634 0           my $col = Class::ReluctantORM::SQL::Column->new(
1635             column => $self->column_name($f),
1636             table => $table,
1637             );
1638 0           my $param = Class::ReluctantORM::SQL::Param->new();
1639 0 0         if ($DEBUG > 2) {
1640 0           my ($colname, $val) = ($col->column, $self->raw_field_value($f));
1641 0 0         $val = defined($val) ? $val : 'NULL';
1642 0           print STDERR __PACKAGE__ . ':' . __LINE__ . "- in insert, binding $colname to $val\n";
1643             }
1644 0           $param->bind_value($self->raw_field_value($f));
1645 0           $sql->add_input($col, $param);
1646             }
1647              
1648             # Build output columns
1649 0           $self->__add_refresh_output_columns_to_sql($sql, $table);
1650              
1651             # Run SQL
1652             # Use run_sql, not prepare/execute - this allows the driver
1653             # to split the query (SQLite needs this, for example)
1654 0           $self->driver->run_sql($sql);
1655 0           $self->_refresh_from_sql($sql);
1656              
1657             # Clear dirty flags
1658 0           $self->_mark_all_clean();
1659 0           $self->{_is_inserted} = 1;
1660              
1661             # Alert relations of new primary key
1662 0           foreach my $rel ($self->relationships) {
1663 0           $rel->_notify_key_change_on_linking_object($self);
1664             }
1665              
1666             # (re) store in registry - registries should refuse to
1667             # store an object with any nulls in the primary keys, so
1668             # this should be a new entry
1669 0           $self->registry->store($self);
1670              
1671 0           $self->__run_triggers('after_insert');
1672              
1673 0           return 1;
1674             }
1675              
1676             sub _refresh_from_sql {
1677 0     0     my $self = shift;
1678 0           my $sql = shift;
1679              
1680 0           $self->__run_triggers('before_refresh');
1681              
1682 0           foreach my $oc ($sql->output_columns) {
1683 0 0         if ($oc->expression->is_column()) {
1684 0           my $field = $self->field_name($oc->expression->column);
1685 0           $self->raw_field_value($field, $oc->output_value);
1686             }
1687             }
1688              
1689 0           $self->__run_triggers('after_refresh');
1690             }
1691              
1692             sub __add_refresh_output_columns_to_sql {
1693 0     0     my $self = shift;
1694 0           my $sql = shift;
1695 0           my $table = shift;
1696              
1697 0           my %is_pk = map { $_ => 1 } $self->primary_key_columns();
  0            
1698              
1699 0           foreach my $c ($self->refresh_columns) {
1700 0           my $col = Class::ReluctantORM::SQL::Column->new(
1701             column => $c,
1702             table => $table,
1703             );
1704 0           my $oc = OutputColumn->new(expression => $col, is_primary_key => $is_pk{$c});
1705 0           $sql->add_output($oc);
1706             }
1707             }
1708              
1709              
1710             =head2 $o->update();
1711              
1712             Commits any changes to an object to the database, and clears the dirty flag.
1713              
1714             If the class was built with 'refresh_on_update' fields, these fields are fetched,
1715             using a single query for the update and the fetch.
1716              
1717             If the class was built with the updatable flag false, this always dies.
1718              
1719             If the object is not dirty, does nothing.
1720              
1721             If the object already not been inserted, dies.
1722              
1723             =cut
1724              
1725             sub update {
1726 0     0 1   my $self = shift;
1727              
1728             # Must allow update
1729 0 0         unless ($self->updatable) {
1730 0           Class::ReluctantORM::Exception::Call::NotPermitted->croak(message => 'This class is configured to not permit updates. See Class::ReluctantORM->build_class().');
1731             }
1732              
1733             # Must be already inserted
1734 0 0         unless ($self->is_inserted()) {
1735 0           Class::ReluctantORM::Exception::Data::UpdateWithoutInsert->croak();
1736             }
1737              
1738 0           $self->_check_for_cascade_on_upsert();
1739              
1740             # Must be dirty
1741 0 0         unless ($self->is_dirty) { return; }
  0            
1742              
1743 0           $self->__run_triggers('before_update');
1744              
1745             # Build SQL
1746 0           my $sql = Class::ReluctantORM::SQL->new('update');
1747 0           my $table = Class::ReluctantORM::SQL::Table->new($self);
1748 0           $sql->table($table);
1749              
1750             # Build input columns
1751 0           foreach my $f ($self->dirty_fields()) {
1752 0           my $p = Param->new();
1753 0           $p->bind_value($self->raw_field_value($f));
1754 0           my $col = Column->new(
1755             column => $self->column_name($f),
1756             table => $table,
1757             );
1758 0           $sql->add_input($col, $p);
1759             }
1760              
1761             # Build Where Clause
1762 0           $sql->where($self->__make_pk_where_clause($table));
1763              
1764             # Build output columns
1765 0           $self->__add_refresh_output_columns_to_sql($sql, $table);
1766              
1767             # Run SQL
1768 0           $self->driver->run_sql($sql);
1769 0           $self->_refresh_from_sql($sql);
1770              
1771             # Clear firty flags
1772 0           $self->_mark_all_clean();
1773              
1774 0           $self->__run_triggers('after_update');
1775 0           return 1;
1776             }
1777              
1778              
1779             # Ensure that if the object has any fetched relation with local keys,
1780             # that the related items are already saved
1781             sub _check_for_cascade_on_upsert {
1782 0     0     my $self = shift;
1783             RELATION:
1784 0           foreach my $rel ($self->relationships()) {
1785 0 0         next RELATION unless ($rel->local_key_fields()); # Skip it if it has no local key fields (eg, has_many)
1786 0           my $field = $rel->method_name();
1787 0 0         next RELATION unless ($self->is_relation_fetched($field)); # Skip it unless we've tried to put something there
1788 0           my $related = $self->$field();
1789 0 0 0       next RELATION if (ref($related) && $related->isa('Class::ReluctantORM::Collection')); # Ignore collections
1790 0 0 0       next RELATION unless (ref($related) && $related->isa('Class::ReluctantORM')); # SKip it unless it is something that can be inserted
1791 0 0         unless ($related->is_inserted()) {
1792 0           Class::ReluctantORM::Exception::Data::UnsupportedCascade->croak
1793             ("Cannot update or insert, because related object in '$field' has not been saved first");
1794             }
1795             }
1796             }
1797              
1798             sub __make_pk_where_clause {
1799 0     0     my $self = shift;
1800 0           my $table = shift;
1801              
1802             # Build WHERE
1803 0           my $where = Class::ReluctantORM::SQL::Where->new();
1804 0           foreach my $f ($self->primary_key_fields()) {
1805 0           my $p = Param->new();
1806 0           $p->bind_value($self->$f);
1807 0           $where->and(Criterion->new(
1808             '=',
1809             Column->new(
1810             column => $self->column_name($f),
1811             table => $table,
1812             ),
1813             $p,
1814             )
1815             );
1816             }
1817 0           return $where;
1818             }
1819              
1820              
1821             =head2 $o->save();
1822              
1823             Convenience method. Calls either insert() or update(),
1824             depending on is_inserted. Does nothing if the object was not dirty.
1825              
1826             =cut
1827              
1828             sub save {
1829 0     0 1   my $self = shift;
1830 0 0         unless ($self->is_dirty()) { return; }
  0            
1831              
1832 0           $self->__run_triggers('before_save');
1833              
1834 0 0         if ($self->is_inserted()) {
1835 0           $self->update();
1836             } else {
1837 0           $self->insert();
1838             }
1839              
1840 0           $self->__run_triggers('after_save');
1841             }
1842              
1843              
1844             =head2 $o->delete();
1845              
1846             Deletes the corresponding row from the database.
1847              
1848             If the class was built with the deletable flag false, this always dies.
1849              
1850             If the object has not been inserted, dies.
1851              
1852             =cut
1853              
1854             sub delete {
1855 0     0 1   my $self = shift;
1856              
1857             # Must allow delete
1858 0 0         unless ($self->deletable) {
1859 0           Class::ReluctantORM::Exception::Call::NotPermitted->croak(message => 'This class is configured to not permit deletes. See Class::ReluctantORM->build_class().');
1860             }
1861              
1862             # Must be already inserted
1863 0 0         unless ($self->is_inserted()) { Class::ReluctantORM::Exception::Data::DeleteWithoutInsert->croak(); }
  0            
1864              
1865 0           $self->__run_triggers('before_delete');
1866              
1867 0           my $class = ref($self);
1868 0           $class->delete_where(where => $self->__make_pk_where_clause(Table->new($class)));
1869              
1870             # Clear the primary key
1871 0           foreach my $pk ($self->primary_key_fields) {
1872 0           $self->set($pk, undef);
1873             }
1874              
1875             # Clear the dirty field trackers
1876 0           $self->_mark_all_clean();
1877              
1878             # Not in the Db anymore
1879 0           $self->{_is_inserted} = 0;
1880              
1881 0           $self->__run_triggers('after_delete');
1882              
1883 0           return 1;
1884             }
1885              
1886             =head2 $class->delete_where(where => '...', execargs => [ ... ]);
1887              
1888             Delete arbitrary rows from the database. Does not affect objects already fetched.
1889              
1890             If the class was built with the deletable flag false, this always dies.
1891              
1892             'where' may be a SQL string, or a Class::ReluctantORM::SQL::Where object.
1893             If where is a sql string and contains '?' characters, you must also provide the execargs option with bindings.
1894              
1895             =cut
1896              
1897             sub delete_where {
1898 0     0 1   my $class = shift;
1899 0   0       $class = ref($class) || $class;
1900 0 0         if (@_ % 2) { Class::ReluctantORM::Exception::Param::ExpectedHash->croak(); }
  0            
1901 0           my %args = @_;
1902 0 0         unless (exists $args{where}) { Class::ReluctantORM::Exception::Param::Missing->croak(param => 'where'); }
  0            
1903              
1904             # Must allow delete
1905 0 0         unless ($class->deletable) {
1906 0           Class::ReluctantORM::Exception::Call::NotPermitted->croak(message => 'This class is configured to not permit deletes. See Class::ReluctantORM->build_class().');
1907             }
1908              
1909             # Build SQL
1910 0           my $sql = Class::ReluctantORM::SQL->new('delete');
1911 0           my $table = Class::ReluctantORM::SQL::Table->new($class);
1912 0           $sql->table($table);
1913              
1914 0           my $where;
1915 0 0         if (UNIVERSAL::isa($args{where}, Where)) {
1916 0           $where = $args{where};
1917             } else {
1918 0           $where = $class->driver->parse_where($args{where});
1919 0 0         $where->bind_params(@{$args{execargs} || []});
  0            
1920             }
1921 0           $sql->where($where);
1922              
1923             # Run SQL
1924 0           $class->driver->run_sql($sql);
1925              
1926 0           return 1;
1927              
1928             }
1929              
1930             DESTROY {
1931 0     0     my $self = shift;
1932             #print "# CRO DESTROY called\n";
1933 0 0 0       if ($self && $self->registry) {
1934 0           $self->registry->purge($self);
1935             }
1936             }
1937              
1938             #==============================================================#
1939             # Dirty Facility
1940             #==============================================================#
1941              
1942             =head1 DIRTINESS
1943              
1944             "Dirtiness" refers to whether the data in-memory has been modified since being read from the database. If so, we know we need to save that data, and call it "dirty".
1945              
1946             =head2 $bool = $o->is_dirty();
1947              
1948             Returns true if the object has been modified since it was
1949             thawed from the database, or if it has never been inserted at all.
1950              
1951             =cut
1952              
1953             sub is_dirty {
1954 0     0 1   my $self = shift;
1955 0           my $dirty_fields = scalar $self->dirty_fields();
1956 0   0       return $dirty_fields || !$self->is_inserted();
1957             }
1958              
1959             sub _mark_field_dirty {
1960 0     0     my $self = shift;
1961 0           my $field = shift;
1962 0           $self->{_dirty_fields}{$field} = 1;
1963             }
1964              
1965             sub _mark_field_clean {
1966 0     0     my $self = shift;
1967 0           my $field = shift;
1968 0           $self->{_dirty_fields}{$field} = 0;
1969             }
1970              
1971             sub _mark_all_clean {
1972 0     0     my $self = shift;
1973 0           $self->{_dirty_fields} = {};
1974             }
1975              
1976             =head2 $bool = $o->is_field_dirty('field_name');
1977              
1978             Checks an individual field for dirtiness.
1979              
1980             =cut
1981              
1982             sub is_field_dirty {
1983 0     0 1   my $self = shift;
1984 0           my $field = shift;
1985 0   0       return $self->{_dirty_fields}{$field} || 0;
1986             }
1987              
1988             =head2 @fields = $o->dirty_fields();
1989              
1990             =head2 @cols = $o->dirty_columns();
1991              
1992             Returns a list of fields or columns that are due for
1993             an update. Fields get added to this list whenever you call a mutator.
1994              
1995             =cut
1996              
1997             sub dirty_fields {
1998 0     0 1   my $self = shift;
1999 0           return grep { $self->{_dirty_fields}{$_} } keys %{$self->{_dirty_fields}};
  0            
  0            
2000             }
2001              
2002 0     0 1   sub dirty_columns { return $_[0]->column_name($_[0]->dirty_fields); }
2003              
2004              
2005             =head2 $bool = $o->is_inserted();
2006              
2007             Returns true if the object originated from the database, or has
2008             been inserted into the database since its creation.
2009              
2010             =cut
2011              
2012 0     0 1   sub is_inserted { return shift->{_is_inserted}; }
2013             sub _is_inserted {
2014 0     0     my $self = shift;
2015 0 0         if (@_) {
2016 0           $self->{_is_inserted} = shift;
2017             }
2018 0           return $self->{_is_inserted};
2019             }
2020              
2021             #=========================================================#
2022             # Code Generation and
2023             # AUTOLOAD Facility
2024             #=========================================================#
2025              
2026             =head1 FIELD ACCESSORS
2027              
2028             These methods correspond to data attributes (member variables) on the OO side, and table columns on the relational side.
2029              
2030             At startup, as each model class calls build_class, CRO will list the columns on your table and create a method for each column.
2031              
2032             Two caveats are in order if you are in a long-running process like mod_perl. First, this column detection only happens once, at compile time,
2033             so adding a column while running is safe, but to see the column in your
2034             datamodel, you'll need to restart. Secondly, since the running code
2035             expects the columns to always be there, renaming or deleting columns
2036             may be a breaking change (of course, if you're using those accessors or
2037             mutators, that's a breaking change anyway). The concern is that the
2038             problem will not be detected until the code hits the database.
2039              
2040             Primitive aggregate functionality is provided, but unless your needs are simple, you will be a sad little panda.
2041              
2042             =head2 $value = $obj->foo_column()
2043              
2044             =head2 $obj->foo_column($foo_value)
2045              
2046             To read the value, just call the method with no arguments. The value will be passed through any Filters, then returned.
2047              
2048             To set the value, call the method with the new value. CRO will pass the new value through any Filters, then update the object with the value. The data is not saved to the database until you call save() or update().
2049              
2050             To set a column to NULL, pass undef as the value.
2051              
2052             =head2 $number = $class->count_of_foo(where => $where, execargs => \@args)
2053              
2054             =head2 $number = $class->avg_of_foo(where => $where, execargs => \@arg)
2055              
2056             =head2 .. etc ..
2057              
2058             For each column, methods are created on first use that have the name _of_. The list of aggregate functions is determined by your Driver; but you do get a handful by default - see L.
2059              
2060             You may optionally provide a where clause, with optional execargs, as for the search() methods.
2061              
2062             =cut
2063              
2064             =begin devdocs
2065              
2066             =head2 $coderef = $class->_make_fetcher($field, $fatal, $rel);
2067              
2068             Builds a coderef that forms the body of the
2069             fetch_by_FIELD, fetch_with_REL, and fetch_by_FIELD_with_REL auto-generated methods.
2070              
2071             $field is the name of the field to search on.
2072              
2073             $fatal is whether a "miss" search should throw a NotFound exception.
2074              
2075             $rel is the name of the relationship to deep-fetch. If undef,
2076             no relations will be fetched.
2077              
2078             =end devdocs
2079              
2080             =cut
2081              
2082             sub _make_fetcher {
2083 0     0     my ($class, $field, $fatal, $rel_name) = @_;
2084             my $code = sub {
2085 0     0     my $class2 = shift;
2086 0           my $value = shift;
2087 0 0 0       if (defined($field) && !defined($value)) { Class::ReluctantORM::Exception::Param::Missing->croak(param => $field . ' value'); }
  0            
2088              
2089             #my $table = Table->new($class);
2090 0           my $table = Table->new(table => 'MACRO__base__');
2091              
2092 0           my %deep_args;
2093              
2094             my $where;
2095 0 0         if (ref($field) eq 'ARRAY') {
    0          
2096             # Searching on multiple fields (ie, multiple keys)
2097              
2098             # Better hope $value is an array ref too...
2099 0 0         unless (ref($value) eq 'ARRAY') {
2100 0           Class::ReluctantORM::Exception::Param::ExpectedArrayRef->croak(param => 'value');
2101             }
2102 0 0         unless (@$value == @$field) {
2103 0           Class::ReluctantORM::Exception::Param::BadValue->croak(param => 'value', error => 'Must be an array ref with ' . (scalar @$field) . ' elements, to match ' . join(',', @$field));
2104             }
2105              
2106 0           my $root_crit;
2107 0           foreach my $i (0..(scalar @$field -1)) {
2108 0           my $crit = Criterion->new(
2109             '=',
2110             Column->new(table => $table, column => $class->column_name($field->[$i])),
2111             Param->new($value->[$i]),
2112             );
2113 0 0         $root_crit = $root_crit ? Criterion->new('AND', $root_crit, $crit) : $crit;
2114             }
2115 0           $where = Where->new($root_crit);
2116              
2117             } elsif ($field) {
2118 0           $where = Where->new
2119             (Criterion->new(
2120             '=',
2121             Column->new(table => $table, column => $class->column_name($field)),
2122             Param->new($value),
2123             ));
2124             } else {
2125 0           $where = Where->new(); # always true
2126             }
2127 0           $deep_args{where} = $where;
2128              
2129 0 0         if (!wantarray) {
2130 0           $deep_args{limit} = 1;
2131             # We're required to provide an order by if we send an limit, so order by base table PK
2132 0           my $ob = Class::ReluctantORM::SQL::OrderBy->new();
2133 0           foreach my $pk_col ($class->primary_key_columns) {
2134 0           $ob->add(Column->new(table => $table, column => $pk_col));
2135             }
2136 0           $deep_args{order_by} = $ob;
2137             }
2138              
2139 0 0         if ($rel_name) {
2140 0           $deep_args{with} = { $rel_name => {} };
2141             }
2142 0           my @results = $class2->search_deep(%deep_args);
2143 0 0         unless (@results) {
2144 0 0         if ($fatal) { Class::ReluctantORM::Exception::Data::NotFound->croak(criteria => $value); }
  0            
2145 0 0         return wantarray ? () : undef;
2146             }
2147              
2148 0 0         return wantarray ? @results : $results[0];
2149 0           };
2150 0           return $code;
2151             }
2152              
2153             =begin devdocs
2154              
2155             =head2 make_accessor
2156              
2157             Override this (defined by Class::Accessor) so that we track dirty status
2158             And catch foreign key changes on has_ones
2159              
2160             =end devdocs
2161              
2162             =cut
2163              
2164             sub make_accessor {
2165 0     0 1   my ($class, $field) = @_;
2166              
2167             # Build a closure around $field.
2168             return sub {
2169 0     0     my $self = shift;
2170              
2171 0 0         if(@_) {
2172 0           my $new_val = shift;
2173 0           $new_val = $self->__apply_field_write_filters($field, $new_val);
2174 0 0         if (nz($self->get($field),'UNDEF') ne nz($new_val, 'UNDEF')) {
2175 0           $self->_mark_field_dirty($field);
2176              
2177             # If the field is the local foreign key field of a relation,
2178             # clear the fetched flag.
2179 0           foreach my $relation_name (keys %{$class->__metadata()->{relations}}) {
  0            
2180 0           my $rel = $class->__metadata()->{relations}{$relation_name};
2181 0 0         if (grep { $_ eq $field } $rel->local_key_fields()) {
  0            
2182 0           $rel->_mark_unpopulated_in_object($self);
2183             }
2184             }
2185              
2186 0           return $self->set($field, $new_val);
2187             }
2188             else {
2189 0           my $raw_value = $self->get($field);
2190 0           my $cooked_value = $self->__apply_field_read_filters($field, $raw_value);
2191 0           return $cooked_value;
2192             }
2193             }
2194             else {
2195 0           my $raw_value = $self->get($field);
2196 0           my $cooked_value = $self->__apply_field_read_filters($field, $raw_value);
2197 0           return $cooked_value;
2198             }
2199 0           };
2200             }
2201              
2202             sub _make_aggregator {
2203 0     0     my $class = shift;
2204 0           my $field = shift;
2205 0           my $aggrfunc = shift;
2206              
2207 0           my $column = $class->column_name($field);
2208             return sub {
2209 0     0     my $class2 = shift;
2210 0           my %args = check_args(args => \@_, optional => [qw(where execargs)]);
2211              
2212 0           my $where = $args{where};
2213 0 0         if (!$where) {
    0          
2214 0           $where = Where->new();
2215             } elsif (UNIVERSAL::isa($where, Where)) {
2216 0 0         $where->bind_params(@{$args{execargs} || []});
  0            
2217             } else {
2218 0           my $driver = $class->driver();
2219 0           $where = $driver->parse_where($args{where});
2220 0 0         $where->bind_params(@{$args{execargs} || [] });
  0            
2221             }
2222              
2223 0           my $table = Table->new($class);
2224 0           my $fc = FunctionCall->new($aggrfunc,
2225             Column->new(table => $table,
2226             column => $column));
2227 0           my $oc = OutputColumn->new(expression => $fc, alias => 'aggr_result');
2228              
2229 0           my $sql = SQL->new('SELECT');
2230 0           $sql->where($where);
2231 0           $sql->from(From->new($table));
2232 0           $sql->add_output($oc);
2233 0           $sql->set_reconcile_option(add_output_columns => 0);
2234              
2235              
2236 0           my $driver = $class2->driver();
2237 0           $driver->run_sql($sql);
2238              
2239 0           my $result = $oc->output_value();
2240 0           return $result;
2241              
2242 0           };
2243              
2244             }
2245              
2246             sub AUTOLOAD {
2247             # Mainly, we're here to auto-generate methods as requested by Class::ReluctantORM::Utilities::install_method_on_first_use() and install_method_generator
2248 0     0     our $AUTOLOAD;
2249 0           my ($class, $method_name) = $AUTOLOAD =~ /(.+)::([^:]+)$/;
2250              
2251 0           my $method_body_coderef;
2252              
2253 0           my $method_maker = $METHODS_TO_BUILD_ON_FIRST_USE{$class}{$method_name};
2254 0 0         if ($method_maker) {
2255 0           $method_body_coderef = $method_maker->();
2256             } else {
2257 0 0         foreach my $generator (@{$METHOD_GENERATORS{$class} || []}) {
  0            
2258 0 0         last if $method_body_coderef = $generator->($class, $method_name);
2259             }
2260             }
2261 0 0         unless ($method_body_coderef) {
2262 0           Class::ReluctantORM::Exception::Call::NoSuchMethod->croak("No such method $AUTOLOAD");
2263             }
2264              
2265 0           install_method($class, $method_name, $method_body_coderef);
2266 0           goto &$method_body_coderef;
2267             }
2268              
2269              
2270             #=========================================================#
2271             # Filter Support #
2272             #=========================================================#
2273              
2274             =head1 FILTER SUPPORT
2275              
2276             These methods provide support for transforming the value of a field when it is being read from an object, or being written to the object.
2277              
2278             One common use of this is to escape all HTML entities, for example.
2279              
2280             =cut
2281              
2282             # Default implementations - in case Class::ReluctantORM::FilterSupport is disabled
2283             BEGIN {
2284 1 50   1   17 unless (__PACKAGE__->can('__apply_field_read_filters')) {
2285 0         0 eval 'sub __apply_field_read_filters { return $_[2]; }';
2286             }
2287 1 50       220 unless (__PACKAGE__->can('__apply_field_write_filters')) {
2288 0         0 eval 'sub __apply_field_write_filters { return $_[2]; }';
2289             }
2290             }
2291              
2292             =begin devdocs
2293              
2294             =head2 $obj->attach_filter()
2295              
2296             Bad method name, add an alias.
2297              
2298             =end devdocs
2299              
2300             =cut
2301              
2302             =head2 $obj->append_filter($filter)
2303              
2304             See L.
2305              
2306             =head2 $class->attach_class_filter($filter)
2307              
2308             See L.
2309              
2310             =head2 $obj->set_filters(...)
2311              
2312             See L.
2313              
2314             =head2 $obj->clear_filters()
2315              
2316             See L.
2317              
2318             =head2 $obj->remove_filter(...)
2319              
2320             See L.
2321              
2322             =head2 @filters = $obj->read_filters_on_field(...)
2323              
2324             See L.
2325              
2326             =head2 @filters = $obj->write_filters_on_field(...)
2327              
2328             See L.
2329              
2330             =cut
2331              
2332             =head2 $val = $obj->raw_field_value('field');
2333              
2334             =head2 $obj->raw_field_value('field', $newval);
2335              
2336             Gets or sets the raw, internal value of a field. This method bypasses the filtering mechanism.
2337              
2338             =cut
2339              
2340             sub raw_field_value {
2341 0     0 1   my $self = shift;
2342 0           my $field = shift;
2343              
2344 0 0         if (my $rel = $self->relationships($field)) {
2345 0           return $rel->_raw_mutator($self, @_);
2346             } else {
2347 0 0         if (@_) {
2348 0           my $new_value = shift;
2349 0           $self->set($field, $new_value);
2350 0           $self->_mark_field_dirty($field);
2351             }
2352 0           return $self->get($field);
2353             }
2354             }
2355              
2356             #=========================================================#
2357             # Origin Support #
2358             #=========================================================#
2359             # Default implementation in case OriginSupport is not loaded
2360             BEGIN {
2361 1 50   1   22 unless (__PACKAGE__->can('capture_origin')) {
2362 0         0 eval 'sub capture_origin { }';
2363             }
2364 1 50       528 unless (__PACKAGE__->can('is_origin_tracking_enabled')) {
2365 0         0 eval 'sub is_origin_tracking_enabled { 0; }';
2366             }
2367             }
2368              
2369              
2370             #=========================================================#
2371             # Relationship Facility
2372             #=========================================================#
2373              
2374             =head1 RELATIONSHIP SUPPORT
2375              
2376             =head2 $rel = $class->relationships('field');
2377              
2378             =head2 $rel_by_name_href = $class->relationships();
2379              
2380             =head2 @rels = $class->relationships();
2381              
2382             Accesses information about the relationships this class has with other Class::ReluctantORM classes.
2383              
2384             In the first form, returns a Class::ReluctantORM::Relationship object (or a subclass thereof), for the given field. For example, you might say:
2385              
2386             $rel = Pirate->relationships('ship');
2387              
2388             In the second form (scalar context), returns a hashref of all relationships the class participates in, keyed by field name.
2389              
2390             In the third form (list context), returns an array of all relationships the class participates in.
2391              
2392             =cut
2393              
2394             sub relationships {
2395 0     0 1   my $inv = shift;
2396 0   0       my $class = ref($inv) || $inv;
2397 0           my $field = shift;
2398 0   0       my $hash = $class->__metadata()->{relations} || {} ;
2399 0 0         if ($field) { return $hash->{$field}; }
  0            
2400 0 0         return wantarray ? (values %$hash) : $hash;
2401             }
2402              
2403             =head2 @relnames = $class->relationship_names();
2404              
2405             Returns the names of all relationships on the class. These are the method names used to access the related object or collection.
2406              
2407             =cut
2408              
2409 0     0 1   sub relationship_names { return keys %{shift->relationships}; }
  0            
2410              
2411             =head2 $bool = $o->is_relation_fetched('relname');
2412              
2413             =head2 $bool = $o->is_field_fetched('fieldname');
2414              
2415             Returns true or false, depending on whether the named field or relation has been fetched.
2416             If true, you may call the accessor without rish of a FetchRequired exception.
2417              
2418             =cut
2419              
2420             =begin devdocs
2421              
2422             =head2 $bool = $o->is_fetched('relname');
2423              
2424             Deprecated alias
2425              
2426             =end devdocs
2427              
2428             =cut
2429              
2430             sub is_fetched {
2431 0     0 1   my $self = shift;
2432 0           my $fieldname = shift;
2433              
2434 0           my $rel = $self->relationships($fieldname);
2435 0 0         if ($rel) {
2436 0           return $rel->is_populated_in_object($self);
2437             } else {
2438 0           return 1;
2439             }
2440             }
2441              
2442              
2443 0     0 1   sub is_relation_fetched { return $_[0]->is_fetched($_[1]); }
2444 0     0 1   sub is_field_fetched { return $_[0]->is_fetched($_[1]); }
2445              
2446              
2447             =begin devdocs
2448              
2449             =head2 $class->register_relationship($rel);
2450              
2451             Attaches a relationship to this class without modifying the relationship. Should only be used by people implementing their own relationships.
2452              
2453             =end devdocs
2454              
2455             =cut
2456              
2457             sub register_relationship {
2458 0     0 1   my $inv = shift;
2459 0   0       my $class = ref($inv) || $inv;
2460 0           my $rel = shift;
2461 0           my $name = $rel->method_name();
2462 0           $class->__metadata()->{relations}->{$name} = $rel;
2463             }
2464              
2465             =head2 $class->clone_relationship($rel);
2466              
2467             Copies a relationship to this class, so that this class is the linking class on the new relationship. The linked class remains the same.
2468              
2469             For this to work, this class must have the same foreign keys that the orginal linking class used.
2470              
2471             This is useful when you are using table-based inheritance (for example, as under PostgreSQL) and you want your inheriting class to have the same relationships as the parent. Then you can just do:
2472              
2473             foreach my $rel (Parent->relationships) {
2474             Child->clone_relationship($rel);
2475             }
2476              
2477             =cut
2478              
2479             sub clone_relationship {
2480 0     0 1   my $inv = shift;
2481 0   0       my $class = ref($inv) || $inv;
2482 0           my $rel = shift;
2483 0 0         unless ($rel->isa('Class::ReluctantORM::Relationship')) {
2484 0           Class::ReluctantORM::Exception::Param::WrongType->croak
2485             (
2486             error => 'clone_relationship takes a real Relationship object',
2487             expected => 'Class::ReluctantORM::Relationship',
2488             param => 'relationship',
2489             value => $rel,
2490             );
2491             }
2492              
2493 0 0         my @original_args = @{$rel->_original_args_arrayref() || []};
  0            
2494 0           my $method = $rel->_setup_method_name();
2495 0           $class->$method(@original_args);
2496             }
2497              
2498              
2499             #=========================================================#
2500             # Abstract SQL Support
2501             #=========================================================#
2502              
2503             our $ENABLE_JOIN_CACHE;
2504              
2505             {
2506 1     1   13 no warnings qw(void); # Test scripts that do use_ok('Class::ReluctantORM') will trigger a 'Too late for CHECK block' warning
  1         3  
  1         2882  
2507             CHECK {
2508             # It is imperative that we enable the cache only after all relationships are defined
2509             $ENABLE_JOIN_CACHE = 1;
2510             }
2511             }
2512              
2513             our %JOIN_TABLE_CACHE = (by_schema => {}, by_table => {}, cache_initted => 0);
2514              
2515             sub __build_join_table_cache {
2516 0 0   0     return if $JOIN_TABLE_CACHE{cache_initted};
2517 0           foreach my $cro_class (keys %CLASS_METADATA) {
2518 0           foreach my $rel ($cro_class->relationships) {
2519 0           my $jst = $rel->join_sql_table();
2520 0 0         if ($jst) {
2521 0           $JOIN_TABLE_CACHE{by_schema}{$jst->schema()}{$jst->table()} = $rel;
2522 0           $JOIN_TABLE_CACHE{by_table}{$jst->table()} = $rel;
2523             }
2524             }
2525             }
2526 0 0         if ($ENABLE_JOIN_CACHE) {
2527 0           $JOIN_TABLE_CACHE{cache_initted} = 1;
2528             }
2529             }
2530              
2531             sub _is_join_table {
2532 0     0     my $class = shift;
2533 0           my %args = check_args(args => \@_, one_of => [[qw(table_obj table_name)]], optional => [qw(schema_name)]);
2534              
2535 0 0         my $table_name = $args{table_obj} ? $args{table_obj}->table() : $args{table_name};
2536 0 0         my $schema_name = $args{table_obj} ? $args{table_obj}->schema() : $args{schema_name};
2537              
2538 0           $class->__build_join_table_cache();
2539              
2540             # If it's a class table, it's not a join table
2541 0 0         if ($class->_find_class_by_table(%args)) {
2542 0           return 0;
2543             }
2544              
2545 0           my $result;
2546 0 0         if ($schema_name) {
2547 0           $result = $JOIN_TABLE_CACHE{by_schema}{$schema_name}{$table_name};
2548             } else {
2549 0           $result = $JOIN_TABLE_CACHE{by_table}{$table_name};
2550             }
2551              
2552 0 0         return $result ? 1 : undef;
2553             }
2554              
2555             sub _find_sql_table_for_join_table {
2556 0     0     my $class = shift;
2557 0           my %args = check_args(args => \@_, one_of => [[qw(table_obj table_name)]], optional => [qw(schema_name)]);
2558              
2559 0 0         my $table_name = $args{table_obj} ? $args{table_obj}->table() : $args{table_name};
2560 0 0         my $schema_name = $args{table_obj} ? $args{table_obj}->schema() : $args{schema_name};
2561              
2562 0           $class->__build_join_table_cache();
2563              
2564 0           my $rel;
2565 0 0         if ($schema_name) {
2566 0           $rel = $JOIN_TABLE_CACHE{by_schema}{$schema_name}{$table_name};
2567             } else {
2568 0           $rel = $JOIN_TABLE_CACHE{by_table}{$table_name};
2569             }
2570              
2571 0 0         unless ($rel) { return undef; }
  0            
2572 0           my $sql_table = $rel->join_sql_table(); # This copy of the table has manual-set columns
2573 0           return $sql_table;
2574             }
2575              
2576             sub _find_class_by_table {
2577 0     0     my $class = shift;
2578 0           my %args = check_args(args => \@_, one_of => [[qw(table_obj table_name)]], optional => [qw(schema_name)]);
2579              
2580 0 0         my $table_name = $args{table_obj} ? $args{table_obj}->table() : $args{table_name};
2581 0 0         my $schema_name = $args{table_obj} ? $args{table_obj}->schema() : $args{schema_name};
2582              
2583 0           foreach my $cro_class (keys %CLASS_METADATA) {
2584 0           my $cc_table = $CLASS_METADATA{$cro_class}{table};
2585 0           my $cc_schema = $CLASS_METADATA{$cro_class}{schema};
2586 0 0 0       if (($table_name eq $cc_table) && (!$schema_name || ($schema_name eq $cc_schema))) {
      0        
2587 0           return $cro_class;
2588             }
2589             }
2590 0           return undef;
2591             }
2592              
2593             our %RELATIONSHIP_CACHE = (
2594             by_local => { by_schema => {}, by_table => {}},
2595             by_remote => { by_schema => {}, by_table => {}},
2596             by_join => { by_schema => {}, by_table => {}},
2597             initted => 0,
2598             );
2599              
2600             sub __init_relationship_cache {
2601 0 0   0     return if $RELATIONSHIP_CACHE{initted};
2602 0           foreach my $cro_class (keys %CLASS_METADATA) {
2603 0           foreach my $rel ($cro_class->relationships()) {
2604 0           my $lt = $rel->local_sql_table();
2605 0 0         if ($lt) {
2606 0   0       $RELATIONSHIP_CACHE{by_local}{by_schema}{$lt->schema}{$lt->table} ||= [];
2607 0           push @{$RELATIONSHIP_CACHE{by_local}{by_schema}{$lt->schema}{$lt->table}}, $rel;
  0            
2608 0   0       $RELATIONSHIP_CACHE{by_local}{by_table}{$lt->table} ||= [];
2609 0           push @{$RELATIONSHIP_CACHE{by_local}{by_table}{$lt->table}}, $rel;
  0            
2610             }
2611              
2612 0           my $jt = $rel->join_sql_table();
2613 0 0         if ($jt) {
2614 0   0       $RELATIONSHIP_CACHE{by_join}{by_schema}{$jt->schema}{$jt->table} ||= [];
2615 0           push @{$RELATIONSHIP_CACHE{by_join}{by_schema}{$jt->schema}{$jt->table}}, $rel;
  0            
2616 0   0       $RELATIONSHIP_CACHE{by_join}{by_table}{$jt->table} ||= [];
2617 0           push @{$RELATIONSHIP_CACHE{by_join}{by_table}{$jt->table}}, $rel;
  0            
2618             }
2619              
2620 0           my $rt = $rel->remote_sql_table();
2621 0 0         if ($rt) {
2622 0   0       $RELATIONSHIP_CACHE{by_remote}{by_schema}{$rt->schema}{$rt->table} ||= [];
2623 0           push @{$RELATIONSHIP_CACHE{by_remote}{by_schema}{$rt->schema}{$rt->table}}, $rel;
  0            
2624 0   0       $RELATIONSHIP_CACHE{by_remote}{by_table}{$rt->table} ||= [];
2625 0           push @{$RELATIONSHIP_CACHE{by_remote}{by_table}{$rt->table}}, $rel;
  0            
2626             }
2627             }
2628             }
2629              
2630 0           $RELATIONSHIP_CACHE{initted} = 1;
2631             }
2632              
2633             sub _find_relationships_by_local_table {
2634 0     0     my $class = shift;
2635 0           my %args = check_args(args => \@_, one_of => [[qw(table_obj table_name)]], optional => [qw(schema_name)]);
2636              
2637 0 0         my $table_name = $args{table_obj} ? $args{table_obj}->table() : $args{table_name};
2638 0 0         my $schema_name = $args{table_obj} ? $args{table_obj}->schema() : $args{schema_name};
2639              
2640 0           __init_relationship_cache();
2641              
2642 0 0         if ($schema_name) {
2643 0 0         return @{$RELATIONSHIP_CACHE{by_local}{by_schema}{$schema_name}{$table_name} || []};
  0            
2644             } else {
2645 0 0         return @{$RELATIONSHIP_CACHE{by_local}{by_table}{$table_name} || []};
  0            
2646             }
2647             }
2648              
2649             sub _find_relationships_by_remote_table {
2650 0     0     my $class = shift;
2651 0           my %args = check_args(args => \@_, one_of => [[qw(table_obj table_name)]], optional => [qw(schema_name)]);
2652              
2653 0 0         my $table_name = $args{table_obj} ? $args{table_obj}->table() : $args{table_name};
2654 0 0         my $schema_name = $args{table_obj} ? $args{table_obj}->schema() : $args{schema_name};
2655              
2656 0           __init_relationship_cache();
2657              
2658 0 0         if ($schema_name) {
2659 0 0         return @{$RELATIONSHIP_CACHE{by_remote}{by_schema}{$schema_name}{$table_name} || []};
  0            
2660             } else {
2661 0 0         return @{$RELATIONSHIP_CACHE{by_remote}{by_table}{$table_name} || []};
  0            
2662             }
2663             }
2664              
2665             sub _find_relationships_by_join_table {
2666 0     0     my $class = shift;
2667 0           my %args = check_args(args => \@_, one_of => [[qw(table_obj table_name)]], optional => [qw(schema_name)]);
2668              
2669 0 0         my $table_name = $args{table_obj} ? $args{table_obj}->table() : $args{table_name};
2670 0 0         my $schema_name = $args{table_obj} ? $args{table_obj}->schema() : $args{schema_name};
2671              
2672 0           __init_relationship_cache();
2673              
2674 0 0         if ($schema_name) {
2675 0 0         return @{$RELATIONSHIP_CACHE{by_join}{by_schema}{$schema_name}{$table_name} || []};
  0            
2676             } else {
2677 0 0         return @{$RELATIONSHIP_CACHE{by_join}{by_table}{$table_name} || []};
  0            
2678             }
2679             }
2680              
2681              
2682             #=========================================================#
2683             # Monitoring Facility
2684             #=========================================================#
2685              
2686             =head1 MONITORING SUPPORT
2687              
2688             =head2 Class::ReluctantORM->install_global_monitor($mon);
2689              
2690             Installs a monitor that will be used on all Class::ReluctantORM queries.
2691              
2692             $mon should be a Class::ReluctantORM::Monitor.
2693              
2694             =cut
2695              
2696             sub install_global_monitor {
2697 0     0 1   my $self = shift;
2698 0           my $mon = shift;
2699              
2700 0 0         unless ($mon) { Class::ReluctantORM::Exception::Param::Missing->croak(param => 'monitor'); }
  0            
2701 0 0         unless (UNIVERSAL::isa($mon, 'Class::ReluctantORM::Monitor')) {
2702 0           Class::ReluctantORM::Exception::Param::WrongType->croak(param => 'monitor', expected => 'Class::ReluctantORM::Monitor', value => $mon);
2703             }
2704              
2705 0           push @GLOBAL_MONITORS, $mon;
2706 0           return 1;
2707             }
2708              
2709             =head2 @mons = Class::ReluctantORM->global_monitors();
2710              
2711             Returns a list of globally applicable monitors.
2712              
2713             =cut
2714              
2715 0     0 1   sub global_monitors { return @GLOBAL_MONITORS; }
2716              
2717             =head2 Class::ReluctantORM->remove_global_monitors();
2718              
2719             Removes all globally applicable monitors.
2720              
2721             =cut
2722              
2723 0     0 1   sub remove_global_monitors { @GLOBAL_MONITORS = (); }
2724              
2725             =head2 Class::ReluctantORM->remove_global_monitor($mon);
2726              
2727             Removes one global monitor.
2728              
2729             =cut
2730              
2731             sub remove_global_monitor {
2732 0     0 1   my $class = shift;
2733 0           my $monitor = shift;
2734 0           @GLOBAL_MONITORS = grep { refaddr($_) != refaddr($monitor) } @GLOBAL_MONITORS;
  0            
2735             }
2736              
2737             =head2 MyClass->install_class_monitor($mon);
2738              
2739             Installs a monitor that will only monitor this specific subclass. The monitor is actually attached to the driver of this class.
2740              
2741             =cut
2742              
2743             sub install_class_monitor {
2744 0     0 1   my $class = shift;
2745 0           my $mon = shift;
2746 0           $class->driver->install_monitor($mon);
2747             }
2748              
2749             =head2 @mons = MyClass->class_monitors();
2750              
2751             Lists all monitors specific to this class.
2752              
2753             =cut
2754              
2755 0     0 1   sub class_monitors { shift->driver->driver_monitors(); }
2756              
2757             =head2 MyClass->remove_class_monitors();
2758              
2759             Removes all class-specific monitors.
2760              
2761             =cut
2762              
2763 0     0 1   sub remove_class_monitors { shift->driver->remove_driver_monitors(); }
2764              
2765              
2766              
2767              
2768              
2769              
2770              
2771             #==============================================================#
2772             # Trigger Support
2773             #==============================================================#
2774              
2775              
2776             =head1 TRIGGER SUPPORT
2777              
2778             Class::ReluctantORM supports Perl-side triggers. (You are also free to implement db-side triggers, of course.)
2779              
2780             A trigger is a coderef that will be called before or after certain events. The args will be the CRO object, followed by the name of the trigger event.
2781              
2782             Triggers are assigned at the class level. You can assign multiple triggers to the event by making repeated calls to add_trigger. They will be called in the order they were added.
2783              
2784             The following events are currently supported:
2785              
2786             =over
2787              
2788             =item after_retrieve
2789              
2790             =item before_insert, after_insert
2791              
2792             =item before_update, after_update
2793              
2794             =item before_delete, after_delete
2795              
2796             =item before_save, after_save
2797              
2798             =item before_refresh, after_refresh
2799              
2800             =back
2801              
2802             Before/after save is a little unusual - it is called within save(), and either the insert or update triggers will be called as well. The order is:
2803              
2804             =over
2805              
2806             =item 1
2807              
2808             before_save
2809              
2810             =item 2
2811              
2812             before_insert OR before_update
2813              
2814             =item 3
2815              
2816             after_insert OR after_update
2817              
2818             =item 4
2819              
2820             after_save
2821              
2822             =back
2823              
2824             =cut
2825              
2826             our %TRIGGER_EVENTS =
2827             map { $_ => 1 }
2828             qw(
2829             after_retrieve
2830             before_refresh after_refresh
2831             before_update after_update
2832             before_insert after_insert
2833             before_save after_save
2834             before_delete after_delete
2835             );
2836              
2837             =head2 MyClass->add_trigger('event', $coderef);
2838              
2839             Arranges for $coderef to be called whenever 'event' occurs. $coderef will be passed the CRO object and the event name as the two arguments.
2840              
2841             =cut
2842              
2843             sub add_trigger {
2844 0     0 1   my $inv = shift;
2845 0 0         my $class = ref($inv) ? ref($inv) : $inv;
2846 0 0         unless (@_ > 1) { Class::ReluctantORM::Exception::Param::Missing->croak(param => 'event, coderef'); }
  0            
2847 0 0         if (@_ > 2) { Class::ReluctantORM::Exception::Param::Spurious->croak(); }
  0            
2848 0           my ($event, $coderef) = @_;
2849 0 0         unless (exists $TRIGGER_EVENTS{$event}) {
2850 0           Class::ReluctantORM::Exception::Param::BadValue->croak(param => 'event', value => $event, error => 'Must be one of ' . join(',', keys %TRIGGER_EVENTS));
2851             }
2852 0 0         unless (ref($coderef) eq 'CODE') {
2853 0           Class::ReluctantORM::Exception::Param::BadValue->croak(param => 'coderef', value => $coderef, expected => 'CODE reference');
2854             }
2855              
2856 0           my $meta = $class->__metadata();
2857 0   0       $meta->{triggers} ||= {};
2858 0   0       $meta->{triggers}{$event} ||= [];
2859 0           push @{$meta->{triggers}{$event}}, $coderef;
  0            
2860              
2861             }
2862              
2863             =head2 remove_trigger('event', $codref);
2864              
2865             Removes the given trigger from the event.
2866              
2867             =cut
2868              
2869             sub remove_trigger {
2870 0     0 1   my $inv = shift;
2871 0 0         my $class = ref($inv) ? ref($inv) : $inv;
2872 0 0         unless (@_ > 1) { Class::ReluctantORM::Exception::Param::Missing->croak(param => 'event, coderef'); }
  0            
2873 0 0         if (@_ > 2) { Class::ReluctantORM::Exception::Param::Spurious->croak(); }
  0            
2874 0           my ($event, $coderef) = @_;
2875 0 0         unless (exists $TRIGGER_EVENTS{$event}) {
2876 0           Class::ReluctantORM::Exception::Param::BadValue->croak(param => 'event', value => $event, error => 'Must be one of ' . join(',', keys %TRIGGER_EVENTS));
2877             }
2878 0 0         unless (ref($coderef) eq 'CODE') {
2879 0           Class::ReluctantORM::Exception::Param::BadValue->croak(param => 'coderef', value => $coderef, expected => 'CODE reference');
2880             }
2881              
2882 0           my $meta = $class->__metadata();
2883 0   0       $meta->{triggers} ||= {};
2884 0   0       $meta->{triggers}{$event} ||= [];
2885 0           $meta->{triggers}{$event} =
2886 0           [ grep { $_ ne $coderef } @{$meta->{triggers}{$event}} ];
  0            
2887             }
2888              
2889             =head2 MyClass->remove_all_triggers();
2890              
2891             =head2 MyClass->remove_all_triggers('event');
2892              
2893             In the first form, removes all triggers from all events.
2894              
2895             In the second form, removes all triggers from the given event.
2896              
2897             =cut
2898              
2899             sub remove_all_triggers {
2900 0     0 1   my $inv = shift;
2901 0 0         my $class = ref($inv) ? ref($inv) : $inv;
2902 0 0         if (@_ > 1) { Class::ReluctantORM::Exception::Param::Spurious->croak(); }
  0            
2903 0           my ($event) = @_;
2904              
2905 0           my $meta = $class->__metadata();
2906 0   0       $meta->{triggers} ||= {};
2907              
2908 0 0         if ($event) {
2909 0 0         unless (exists $TRIGGER_EVENTS{$event}) {
2910 0           Class::ReluctantORM::Exception::Param::BadValue->croak(param => 'event', value => $event, error => 'Must be one of ' . join(',', keys %TRIGGER_EVENTS));
2911             }
2912 0           $meta->{triggers}{$event} = [];
2913             } else {
2914 0           $meta->{triggers} = {};
2915             }
2916             }
2917              
2918             =head2 @trigs = MyClass->list_triggers('event');
2919              
2920             Lists all triggers from the given event, in the order they will be applied.
2921              
2922             =cut
2923              
2924             sub list_triggers {
2925 0     0 1   my $inv = shift;
2926 0 0         my $class = ref($inv) ? ref($inv) : $inv;
2927 0 0         if (@_ < 1) { Class::ReluctantORM::Exception::Param::Missing->croak(param => 'event'); }
  0            
2928 0 0         if (@_ > 1) { Class::ReluctantORM::Exception::Param::Spurious->croak(); }
  0            
2929 0           my ($event) = @_;
2930              
2931 0 0         unless (exists $TRIGGER_EVENTS{$event}) {
2932 0           Class::ReluctantORM::Exception::Param::BadValue->croak(param => 'event', value => $event, error => 'Must be one of ' . join(',', keys %TRIGGER_EVENTS));
2933             }
2934 0           my $meta = $class->__metadata();
2935 0   0       $meta->{triggers} ||= {};
2936 0   0       $meta->{triggers}{$event} ||= [];
2937 0           return @{$meta->{triggers}{$event}};
  0            
2938             }
2939              
2940             sub __run_triggers {
2941 0     0     my $self = shift;
2942 0           my $event = shift;
2943              
2944 0 0         unless (exists $TRIGGER_EVENTS{$event}) {
2945 0           Class::ReluctantORM::Exception::Param::BadValue->croak(param => 'event', value => $event, error => 'Must be one of ' . join(',', keys %TRIGGER_EVENTS));
2946             }
2947              
2948             # LEGACY In TableBacked, triggers were defined by inheritance.
2949             # Check for a trigger defined in such a way.
2950 0           my $method = '_' . $event . '_trigger';
2951 0 0         if ($self->can($method)) {
2952 0           deprecated("Using inheritance to define a $event trigger - use add_trigger() instead");
2953 0           $self->$method();
2954             }
2955              
2956 0           my $class = ref($self);
2957 0 0         foreach my $trig (@{$class->__metadata()->{triggers}{$event} || []}) {
  0            
2958 0           $trig->($self, $event);
2959             }
2960             }
2961              
2962             =head1 AUTHOR
2963              
2964             Clinton Wolfe (clwolfe@cpan.org) 2008-2012
2965              
2966             With extensive real-world usage from the fine folks at OmniTI (www.omniti.com).
2967              
2968             =cut
2969              
2970             =head1 COPYRIGHT
2971              
2972             Copyright OmniTI 2012. All Rights Reserved.
2973              
2974             This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself.
2975              
2976             =cut
2977              
2978             =head1 BUGS
2979              
2980             Let's track them in RT, shall we. https://rt.cpan.org/Dist/Browse.html?Name=Class-ReluctantORM
2981              
2982             =cut
2983              
2984              
2985              
2986             1;
2987