root/trunk/lib/Data/ObjectDriver/BaseObject.pm @ 573

Revision 573, 36.6 kB (checked in by athomason, 6 months ago)

Merge inflate speedup from Yann's tree. See changelog for r211 for potential client impact.
http://code.sixapart.com/trac/Data-ObjectDriver/changeset/211
http://github.com/yannk/perl-data-objectdriver/commit/3c1d410b777cdcdfebc857125d5dfb8ee8d36ad5

  • Property svn:keywords set to Id Revision
Line 
1# $Id$
2
3package Data::ObjectDriver::BaseObject;
4use strict;
5use warnings;
6
7our $HasWeaken;
8eval q{ use Scalar::Util qw(weaken) }; ## no critic
9$HasWeaken = !$@;
10
11use Carp ();
12
13use Class::Trigger qw( pre_save post_save post_load pre_search
14                       pre_insert post_insert pre_update post_update
15                       pre_remove post_remove post_inflate );
16
17use Data::ObjectDriver::ResultSet;
18
19## Global Transaction variables
20our @WorkingDrivers;
21our $TransactionLevel = 0;
22
23sub install_properties {
24    my $class = shift;
25    my($props) = @_;
26    my $columns = delete $props->{columns};
27    $props->{columns} = [];
28    {
29        no strict 'refs'; ## no critic
30        *{"${class}::__properties"} = sub { $props };
31    }
32
33    foreach my $col (@$columns) {
34        $class->install_column($col);
35    }
36    return $props;
37}
38
39sub install_column {
40    my($class, $col, $type) = @_;
41    my $props = $class->properties;
42
43    push @{ $props->{columns} }, $col;
44    $props->{column_names}{$col} = ();
45    # predefine getter/setter methods here
46    # Skip adding this method if the class overloads it.
47    # this lets the SUPER::columnname magic do it's thing
48    if (! $class->can($col)) {
49        no strict 'refs'; ## no critic
50        *{"${class}::$col"} = $class->column_func($col);
51    }
52    if ($type) {
53        $props->{column_defs}{$col} = $type;
54    }
55}
56
57sub properties {
58    my $this = shift;
59    my $class = ref($this) || $this;
60    $class->__properties;
61}
62
63# see docs below
64
65sub has_a {
66    my $class = shift;
67    my @args = @_;
68
69    # Iterate over each remote object
70    foreach my $config (@args) {
71        my $parentclass = $config->{class};
72
73        # Parameters
74        my $column = $config->{column};
75        my $method = $config->{method};
76        my $cached = $config->{cached} || 0;
77        my $parent_method = $config->{parent_method};
78
79        # column is required
80        if (!defined($column)) {
81            die "Please specify a valid column for $parentclass"
82        }
83
84        # create a method name based on the column
85        if (! defined $method) {
86            if (!ref($column)) {
87                $method = $column;
88                $method =~ s/_id$//;
89                $method .= "_obj";
90            } elsif (ref($column) eq 'ARRAY') {
91                foreach my $col (@{$column}) {
92                    my $part = $col;
93                    $part =~ s/_id$//;
94                    $method .= $part . '_';
95                }
96                $method .= "obj";
97            }
98        }
99
100        # die if we have clashing methods method
101        if (! defined $method || defined(*{"${class}::$method"})) {
102            die "Please define a valid method for $class->$column";
103        }
104
105        if ($cached) {
106            # Store cached item inside this object's namespace
107            my $cachekey = "__cache_$method";
108
109            no strict 'refs'; ## no critic
110            *{"${class}::$method"} = sub {
111                my $obj = shift;
112
113                return $obj->{$cachekey}
114                    if defined $obj->{$cachekey};
115
116                my $id = (ref($column) eq 'ARRAY')
117                    ? [ map { $obj->{column_values}->{$_} } @{$column}]
118                    : $obj->{column_values}->{$column}
119                    ;
120                ## Hold in a variable here too, so we don't lose it immediately
121                ## by having only the weak reference.
122                my $ret = $parentclass->lookup($id);
123                if ($HasWeaken) {
124                    $obj->{$cachekey} = $ret;
125                    weaken($obj->{$cachekey});
126                }
127                return $ret;
128            };
129        } else {
130            if (ref($column)) {
131                no strict 'refs'; ## no critic
132                *{"${class}::$method"} = sub {
133                    my $obj = shift;
134                    return $parentclass->lookup([ map{ $obj->{column_values}->{$_} } @{$column}]);
135                };
136            } else {
137                no strict 'refs'; ## no critic
138                *{"${class}::$method"} = sub {
139                    return $parentclass->lookup(shift()->{column_values}->{$column});
140                };
141            }
142        }
143
144        # now add to the parent
145        if (!defined $parent_method) {
146            $parent_method = lc($class);
147            $parent_method =~ s/^.*:://;
148
149            $parent_method .= '_objs';
150        }
151        if (ref($column)) {
152            no strict 'refs'; ## no critic
153            *{"${parentclass}::$parent_method"} = sub {
154                my $obj = shift;
155                my $terms = shift || {};
156                my $args = shift;
157
158                my $primary_key = $obj->primary_key;
159
160                # inject pk search into given terms.
161                # composite key, ugh
162                foreach my $key (@$column) {
163                    $terms->{$key} = shift(@{$primary_key});
164                }
165
166                return $class->search($terms, $args);
167            };
168        } else {
169            no strict 'refs'; ## no critic
170            *{"${parentclass}::$parent_method"} = sub {
171                my $obj = shift;
172                my $terms = shift || {};
173                my $args = shift;
174                # TBD - use primary_key_to_terms
175                $terms->{$column} = $obj->primary_key;
176                return $class->search($terms, $args);
177            };
178        };
179    } # end of loop over class names
180    return;
181}
182
183sub driver {
184    my $class = shift;
185    $class->properties->{driver} ||= $class->properties->{get_driver}->();
186}
187
188sub get_driver {
189    my $class = shift;
190    $class->properties->{get_driver} = shift if @_;
191}
192
193sub new {
194    my $obj = bless {}, shift;
195
196    return $obj->init(@_);
197}
198
199sub init {
200    my $self = shift;
201
202    while (@_) {
203        my $field = shift;
204        my $val   = shift;
205        $self->$field($val);
206    }
207    return $self;
208}
209
210sub is_pkless {
211    my $obj = shift;
212    my $prop_pk = $obj->properties->{primary_key};
213    return 1 if ! $prop_pk;
214    return 1 if ref $prop_pk eq 'ARRAY' && ! @$prop_pk;
215}
216
217sub is_primary_key {
218    my $obj = shift;
219    my($col) = @_;
220
221    my $prop_pk = $obj->properties->{primary_key};
222    if (ref($prop_pk)) {
223        for my $pk (@$prop_pk) {
224            return 1 if $pk eq $col;
225        }
226    } else {
227        return 1 if $prop_pk eq $col;
228    }
229
230    return;
231}
232
233sub primary_key_tuple {
234    my $obj = shift;
235    my $pk = $obj->properties->{primary_key} || return;
236    $pk = [ $pk ] unless ref($pk) eq 'ARRAY';
237    $pk;
238}
239
240sub primary_key {
241    my $obj = shift;
242    my $pk = $obj->primary_key_tuple;
243    my @val = map { $obj->$_() }  @$pk;
244    @val == 1 ? $val[0] : \@val;
245}
246
247sub is_same_array {
248    my($a1, $a2) = @_;
249    return if ($#$a1 != $#$a2);
250    for (my $i = 0; $i <= $#$a1; $i++) {
251        return if $a1->[$i] ne $a2->[$i];
252    }
253    return 1;
254}
255
256sub primary_key_to_terms {
257    my($obj, $id) = @_;
258    my $pk = $obj->primary_key_tuple;
259    if (! defined $id) {
260        $id = $obj->primary_key;
261    } else {
262        if (ref($id) eq 'HASH') {
263            my @keys = sort keys %$id;
264            unless (is_same_array(\@keys, [ sort @$pk ])) {
265                Carp::confess("keys don't match with primary keys: @keys|@$pk");
266            }
267            return $id;
268        }
269    }
270    $id = [ $id ] unless ref($id) eq 'ARRAY';
271    my %terms;
272    @terms{@$pk} = @$id;
273    \%terms;
274}
275
276sub is_same {
277    my($obj, $other) = @_;
278
279    my @a;
280    for my $o ($obj, $other) {
281        push @a, [ map { $o->$_() } @{ $o->primary_key_tuple }];
282    }
283    return is_same_array( @a );
284}
285
286sub object_is_stored {
287    my $obj = shift;
288    return $obj->{__is_stored} ? 1 : 0;
289}
290sub pk_str {
291    my ($obj) = @_;
292    my $pk = $obj->primary_key;
293    return $pk unless ref ($pk) eq 'ARRAY';
294    return join (":", @$pk);
295}
296
297sub has_primary_key {
298    my $obj = shift;
299    return unless @{$obj->primary_key_tuple};
300    my $val = $obj->primary_key;
301    $val = [ $val ] unless ref($val) eq 'ARRAY';
302    for my $v (@$val) {
303        return unless defined $v;
304    }
305    1;
306}
307
308sub datasource { $_[0]->properties->{datasource} }
309
310sub columns_of_type {
311    my $obj = shift;
312    my($type) = @_;
313    my $props = $obj->properties;
314    my $cols = $props->{columns};
315    my $col_defs = $props->{column_defs};
316    my @cols;
317    for my $col (@$cols) {
318        push @cols, $col if $col_defs->{$col} && $col_defs->{$col} eq $type;
319    }
320    \@cols;
321}
322
323sub set_values {
324    my $obj = shift;
325    my $values = shift;
326    for my $col (keys %$values) {
327        unless ( $obj->has_column($col) ) {
328            Carp::croak("You tried to set non-existent column $col to value $values->{$col} on " . ref($obj));
329        }
330        $obj->$col($values->{$col});
331    }
332}
333
334sub set_values_internal {
335    my $obj = shift;
336    my $values = shift;
337    for my $col (keys %$values) {
338        # Not needed for the internal version of this method
339        #unless ( $obj->has_column($col) ) {
340        #    Carp::croak("You tried to set inexistent column $col to value $values->{$col} on " . ref($obj));
341        #}
342
343        $obj->column_values->{$col} = $values->{$col};
344    }
345}
346
347sub clone {
348    my $obj = shift;
349    my $clone = $obj->clone_all;
350    for my $pk (@{ $obj->primary_key_tuple }) {
351        $clone->$pk(undef);
352    }
353    $clone;
354}
355
356sub clone_all {
357    my $obj = shift;
358    my $clone = ref($obj)->new();
359    $clone->set_values_internal($obj->column_values);
360    $clone->{changed_cols} = defined $obj->{changed_cols} ? { %{$obj->{changed_cols}} } : undef;
361    $clone;
362}
363
364sub has_column {
365    return exists $_[0]->properties->{column_names}{$_[1]};
366}
367
368sub column_names {
369    ## Reference to a copy.
370    [ @{ shift->properties->{columns} } ]
371}
372
373sub column_values { $_[0]->{'column_values'} ||= {} }
374
375## In 0.1 version we didn't die on inexistent column
376## which might lead to silent bugs
377## You should override column if you want to find the old
378## behaviour
379sub column {
380    my $obj = shift;
381    my $col = shift or return;
382    unless ($obj->has_column($col)) {
383        Carp::croak("Cannot find column '$col' for class '" . ref($obj) . "'");
384    }
385
386    # set some values
387    if (@_) {
388        $obj->{column_values}->{$col} = shift;
389        unless ($_[0] && ref($_[0]) eq 'HASH' && $_[0]->{no_changed_flag}) {
390            $obj->{changed_cols}->{$col}++;
391        }
392    }
393
394    $obj->{column_values}->{$col};
395}
396
397sub column_func {
398    my $obj = shift;
399    my $col = shift or die "Must specify column";
400
401    return sub {
402        my $obj = shift;
403        # getter
404        return $obj->{column_values}->{$col} unless (@_);
405
406        # setter
407        my ($val, $flags) = @_;
408        $obj->{column_values}->{$col} = $val;
409        unless ($flags && ref($flags) eq 'HASH' && $flags->{no_changed_flag}) {
410            $obj->{changed_cols}->{$col}++;
411        }
412
413        return $obj->{column_values}->{$col};
414    };
415}
416
417
418sub changed_cols_and_pk {
419    my $obj = shift;
420    keys %{$obj->{changed_cols}};
421}
422
423sub changed_cols {
424    my $obj = shift;
425    my $pk = $obj->primary_key_tuple;
426    my %pk = map { $_ => 1 } @$pk;
427    grep !$pk{$_}, $obj->changed_cols_and_pk;
428}
429
430sub is_changed {
431    my $obj = shift;
432    if (@_) {
433        return exists $obj->{changed_cols}->{$_[0]};
434    } else {
435        return $obj->changed_cols > 0;
436    }
437}
438
439sub exists {
440    my $obj = shift;
441    return 0 unless $obj->has_primary_key;
442    $obj->_proxy('exists', @_);
443}
444
445sub save {
446    my $obj = shift;
447    if ($obj->exists(@_)) {
448        return $obj->update(@_);
449    } else {
450        return $obj->insert(@_);
451    }
452}
453
454sub bulk_insert {
455    my $class = shift;
456    my $driver = $class->driver;
457
458    return $driver->bulk_insert($class, @_);
459}
460
461sub lookup {
462    my $class = shift;
463    my $driver = $class->driver;
464    my $obj = $driver->lookup($class, @_) or return;
465    $driver->cache_object($obj);
466    $obj;
467}
468
469sub lookup_multi {
470    my $class = shift;
471    my $driver = $class->driver;
472    my $objs = $driver->lookup_multi($class, @_) or return;
473    for my $obj (@$objs) {
474        $driver->cache_object($obj) if $obj;
475    }
476    $objs;
477}
478
479sub result {
480    my $class = shift;
481    my ($terms, $args) = @_;
482
483    return Data::ObjectDriver::ResultSet->new({
484                          class     => (ref $class || $class),
485                          page_size => delete $args->{page_size},
486                          paging    => delete $args->{no_paging},
487                          terms     => $terms,
488                          args      => $args,
489                          });
490}
491
492sub search {
493    my $class = shift;
494    my($terms, $args) = @_;
495    my $driver = $class->driver;
496    if (wantarray) {
497        my @objs = $driver->search($class, $terms, $args);
498
499        ## Don't attempt to cache objects where the caller specified fetchonly,
500        ## because they won't be complete.
501        ## Also skip this step if we don't get any objects back from the search
502        if (!$args->{fetchonly} || !@objs) {
503            for my $obj (@objs) {
504                $driver->cache_object($obj) if $obj;
505            }
506        }
507        return @objs;
508    } else {
509        my $iter = $driver->search($class, $terms, $args);
510        return $iter if $args->{fetchonly};
511
512        my $caching_iter = sub {
513            my $d = $driver;
514
515            my $o = $iter->();
516            unless ($o) {
517                $iter->end;
518                return;
519            }
520            $driver->cache_object($o);
521            return $o;
522        };
523        return Data::ObjectDriver::Iterator->new($caching_iter, sub { $iter->end });
524    }
525}
526
527sub remove         { shift->_proxy( 'remove',         @_ ) }
528sub update         { shift->_proxy( 'update',         @_ ) }
529sub insert         { shift->_proxy( 'insert',         @_ ) }
530sub replace        { shift->_proxy( 'replace',        @_ ) }
531sub fetch_data     { shift->_proxy( 'fetch_data',     @_ ) }
532sub uncache_object { shift->_proxy( 'uncache_object', @_ ) }
533
534sub refresh {
535    my $obj = shift;
536    return unless $obj->has_primary_key;
537    my $fields = $obj->fetch_data;
538    $obj->set_values_internal($fields);
539    $obj->call_trigger('post_load');
540    return 1;
541}
542
543## NOTE: I wonder if it could be useful to BaseObject superclass
544## to override the global transaction flag. If so, I'd add methods
545## to manipulate this flag and the working drivers. -- Yann
546sub _proxy {
547    my $obj = shift;
548    my($meth, @args) = @_;
549    my $driver = $obj->driver;
550    ## faster than $obj->txn_active && ! $driver->txn_active but see note.
551    if ($TransactionLevel && ! $driver->txn_active) {
552        $driver->begin_work;
553        push @WorkingDrivers, $driver;
554    }
555    $driver->$meth($obj, @args);
556}
557
558sub txn_active { $TransactionLevel }
559
560sub begin_work {
561    my $class = shift;
562    if ( $TransactionLevel > 0 ) {
563        Carp::carp(
564            $TransactionLevel > 1
565            ? "$TransactionLevel transactions already active"
566            : "Transaction already active"
567        );
568    }
569    $TransactionLevel++;
570}
571
572sub commit {
573    my $class = shift;
574    $class->_end_txn('commit');
575}
576
577sub rollback {
578    my $class = shift;
579    $class->_end_txn('rollback');
580}
581
582sub _end_txn {
583    my $class = shift;
584    my $meth  =  shift;
585   
586    ## Ignore nested transactions
587    if ($TransactionLevel > 1) {
588        $TransactionLevel--;
589        return;
590    }
591   
592    if (! $TransactionLevel) {
593        Carp::carp("No active transaction to end; ignoring $meth");
594        return;
595    }
596    my @wd = @WorkingDrivers;
597    $TransactionLevel--;
598    @WorkingDrivers = ();
599   
600    for my $driver (@wd) {
601        $driver->$meth;
602    }
603}
604
605sub txn_debug {
606    my $class = shift;
607    return {
608        txn     => $TransactionLevel,
609        drivers => \@WorkingDrivers,
610    };
611}
612
613sub deflate { { columns => shift->column_values } }
614
615sub inflate {
616    my $class = shift;
617    my($deflated) = @_;
618    my $obj = $class->new;
619    $obj->set_values_internal($deflated->{columns});
620    $obj->call_trigger('post_inflate');
621    return $obj;
622}
623
624sub DESTROY { }
625
626sub AUTOLOAD {
627    my $obj = $_[0];
628    (my $col = our $AUTOLOAD) =~ s!.+::!!;
629    Carp::croak("Cannot find method '$col' for class '$obj'") unless ref $obj;
630    unless ($obj->has_column($col)) {
631        Carp::croak("Cannot find column '$col' for class '" . ref($obj) . "'");
632    }
633
634    {
635        no strict 'refs'; ## no critic
636        *$AUTOLOAD = $obj->column_func($col);
637    }
638
639    goto &$AUTOLOAD;
640}
641
642sub has_partitions {
643    my $class = shift;
644    my(%param) = @_;
645    my $how_many = delete $param{number}
646        or Carp::croak("number (of partitions) is required");
647
648    ## save the number of partitions in the class
649    $class->properties->{number_of_partitions} = $how_many;
650
651    ## Save the get_driver subref that we were passed, so that the
652    ## SimplePartition driver can access it.
653    $class->properties->{partition_get_driver} = delete $param{get_driver}
654        or Carp::croak("get_driver is required");
655
656    ## When creating a new $class object, we should automatically fill in
657    ## the partition ID by selecting one at random, unless a partition_id
658    ## is already defined. This allows us to keep it simple but for the
659    ## caller to do something more complex, if it wants to.
660    $class->add_trigger(pre_insert => sub {
661        my($obj, $orig_obj) = @_;
662        unless (defined $obj->partition_id) {
663            my $partition_id = int(rand $how_many) + 1;
664            $obj->partition_id($partition_id);
665            $orig_obj->partition_id($partition_id);
666        }
667    });
668}
669
6701;
671
672__END__
673
674=head1 NAME
675
676Data::ObjectDriver::BaseObject - base class for modeled objects
677
678=head1 SYNOPSIS
679
680    package Ingredient;
681    use base qw( Data::ObjectDriver::BaseObject );
682
683    __PACKAGE__->install_properties({
684        columns     => [ 'ingredient_id', 'recipe_id', 'name', 'quantity' ],
685        datasource  => 'ingredient',
686        primary_key => [ 'recipe_id', 'ingredient_id' ],
687        driver      => FoodDriver->driver,
688    });
689
690    __PACKAGE__->has_a(
691        { class => 'Recipe', column => 'recipe_id', }
692    );
693
694    package main;
695
696    my ($ingredient) = Ingredient->search({ recipe_id => 4, name => 'rutabaga' });
697    $ingredient->quantity(7);
698    $ingredient->save();
699
700
701=head1 DESCRIPTION
702
703I<Data::ObjectDriver::BaseObject> provides services to data objects modeled
704with the I<Data::ObjectDriver> object relational mapper.
705
706=head1 CLASS DEFINITION
707
708=head2 C<Class-E<gt>install_properties(\%params)>
709
710Defines all the properties of the specified object class. Generally you should
711call C<install_properties()> in the body of your class definition, so the
712properties can be set when the class is C<use>d or C<require>d.
713
714Required members of C<%params> are:
715
716=over 4
717
718=item * C<columns>
719
720All the columns in the object class. This property is an arrayref.
721
722=item * C<datasource>
723
724The identifier of the table in which the object class's data are stored.
725Usually the datasource is simply the table name, but the datasource can be
726decorated into the table name by the C<Data::ObjectDriver::DBD> module if the
727database requires special formatting of table names.
728
729=item * C<driver> or C<get_driver>
730
731The driver used to perform database operations (lookup, update, etc) for the
732object class.
733
734C<driver> is the instance of C<Data::ObjectDriver> to use. If your driver
735requires configuration options not available when the properties are initially
736set, specify a coderef as C<get_driver> instead. It will be called the first
737time the driver is needed, storing the driver in the class's C<driver> property
738for subsequent calls.
739
740=back
741
742The optional members of C<%params> are:
743
744=over 4
745
746=item * C<primary_key>
747
748The column or columns used to uniquely identify an instance of the object
749class. If one column (such as a simple numeric ID) identifies the class,
750C<primary_key> should be a scalar. Otherwise, C<primary_key> is an arrayref.
751
752=item * C<column_defs>
753
754Specifies types for specially typed columns, if any, as a hashref. For example,
755if a column holds a timestamp, name it in C<column_defs> as a C<date> for
756proper handling with some C<Data::ObjectDriver::Driver::DBD> database drivers.
757Columns for which types aren't specified are handled as C<char> columns.
758
759Known C<column_defs> types are:
760
761=over 4
762
763=item * C<blob>
764
765A blob of binary data. C<Data::ObjectDriver::Driver::DBD::Pg> maps this to
766C<DBI::Pg::PG_BYTEA>, C<DBD::SQLite> to C<DBI::SQL_BLOB> and C<DBD::Oracle>
767to C<ORA_BLOB>.
768
769=item * C<bin_char>
770
771A non-blob string of binary data. C<Data::ObjectDriver::Driver::DBD::SQLite>
772maps this to C<DBI::SQL_BINARY>.
773
774=back
775
776Other types may be defined by custom database drivers as needed, so consult
777their documentation.
778
779=item * C<db>
780
781The name of the database. When used with C<Data::ObjectDriver::Driver::DBI>
782type object drivers, this name is passed to the C<init_db> method when the
783actual database handle is being created.
784
785=back
786
787Custom object drivers may define other properties for your object classes.
788Consult the documentation of those object drivers for more information.
789
790=head2 C<Class-E<gt>install_column($col, $def)>
791
792Modify the Class definition to declare a new column C<$col> of definition <$def>
793(see L<column_defs>).
794
795=head2 C<Class-E<gt>has_a(@definitions)>
796
797B<NOTE:> C<has_a> is an experimental system, likely to both be buggy and change
798in future versions.
799
800Defines a foreign key reference between two classes, creating accessor methods
801to retrieve objects both ways across the reference. For each defined reference,
802two methods are created: one for objects of class C<Class> to load the objects
803they reference, and one for objects of the referenced class to load the set of
804C<Class> objects that reference I<them>.
805
806For example, this definition:
807
808    package Ingredient;
809    __PACKAGE__->has_a(
810        { class => 'Recipe', column => 'recipe_id' },
811    );
812
813would create C<Ingredient-E<gt>recipe_obj> and C<Recipe-E<gt>ingredient_objs>
814instance methods.
815
816Each member of C<@definitions> is a hashref containing the parameters for
817creating one accessor method. The required members of these hashes are:
818
819=over 4
820
821=item * C<class>
822
823The class to associate.
824
825=item * C<column>
826
827The column or columns in this class that identify the primary key of the
828associated object. As with primary keys, use a single scalar string for a
829single column or an arrayref for a composite key.
830
831=back
832
833The optional members of C<has_a()> definitions are:
834
835=over 4
836
837=item * C<method>
838
839The name of the accessor method to create.
840
841By default, the method name is the concatenated set of column names with each
842C<_id> suffix removed, and the suffix C<_obj> appended at the end of the method
843name. For example, if C<column> were C<['recipe_id', 'ingredient_id']>, the
844resulting method would be called C<recipe_ingredient_obj> by default.
845
846=item * C<cached>
847
848Whether to keep a reference to the foreign object once it's loaded. Subsequent
849calls to the accessor method would return that reference immediately.
850
851=item * C<parent_method>
852
853The name of the reciprocal method created in the referenced class named in
854C<class>.
855
856By default, that method is named with the lowercased name of the current class
857with the suffix C<_objs>. For example, if in your C<Ingredient> class you
858defined a relationship with C<Recipe> on the column C<recipe_id>, this would
859create a C<$recipe-E<gt>ingredient_objs> method.
860
861Note that if you reference one class with multiple sets of fields, you can omit
862only one parent_method; otherwise the methods would be named the same thing.
863For instance, if you had a C<Friend> class with two references to C<User>
864objects in its C<user_id> and C<friend_id> columns, one of them would need a
865C<parent_method>.
866
867=back
868
869=head2 C<Class-E<gt>has_partitions(%param)>
870
871Defines that the given class is partitioned, configuring it for use with the
872C<Data::ObjectDriver::Driver::SimplePartition> object driver. Required members
873of C<%param> are:
874
875=over 4
876
877=item * C<number>
878
879The number of partitions in which objects of this class may be stored.
880
881=item * C<get_driver>
882
883A function that returns an object driver, given a partition ID and any extra
884parameters specified when the class's
885C<Data::ObjectDriver::Driver::SimplePartition> was instantiated.
886
887=back
888
889Note that only the parent object for use with the C<SimplePartition> driver
890should use C<has_partitions()>. See
891C<Data::ObjectDriver::Driver::SimplePartition> for more about partitioning.
892
893=head1 BASIC USAGE
894
895=head2 C<Class-E<gt>lookup($id)>
896
897Returns the instance of C<Class> with the given value for its primary key. If
898C<Class> has a complex primary key (more than one column), C<$id> should be an
899arrayref specifying the column values in the same order as specified in the
900C<primary_key> property.
901
902=head2 C<Class-E<gt>search(\%terms, [\%args])>
903
904Returns all instances of C<Class> that match the values specified in
905C<\%terms>, keyed on column names. In list context, C<search> returns the
906objects containing those values. In scalar context, C<search> returns an
907iterator function containing the same set of objects.
908
909Your search can be customized with parameters specified in C<\%args>. Commonly
910recognized parameters (those implemented by the standard C<Data::ObjectDriver>
911object drivers) are:
912
913=over 4
914
915=item * C<sort>
916
917A column by which to order the object results.
918
919=item * C<direction>
920
921If set to C<descend>, the results (ordered by the C<sort> column) are returned
922in descending order. Otherwise, results will be in ascending order.
923
924=item * C<limit>
925
926The number of results to return, at most. You can use this with C<offset> to
927paginate your C<search()> results.
928
929=item * C<offset>
930
931The number of results to skip before the first returned result. Use this with
932C<limit> to paginate your C<search()> results.
933
934=item * C<fetchonly>
935
936A list (arrayref) of columns that should be requested. If specified, only the
937specified columns of the resulting objects are guaranteed to be set to the
938correct values.
939
940Note that any caching object drivers you use may opt to ignore C<fetchonly>
941instructions, or decline to cache objects queried with C<fetchonly>.
942
943=item * C<for_update>
944
945If true, instructs the object driver to indicate the query is a search, but the
946application may want to update the data after. That is, the generated SQL
947C<SELECT> query will include a C<FOR UPDATE> clause.
948
949=back
950
951All options are passed to the object driver, so your driver may support
952additional options.
953
954=head2 C<Class-E<gt>result(\%terms, [\%args])>
955
956Takes the same I<%terms> and I<%args> arguments that I<search> takes, but
957instead of executing the query immediately, returns a
958I<Data::ObjectDriver::ResultSet> object representing the set of results.
959
960=head2 C<$obj-E<gt>exists()>
961
962Returns true if C<$obj> already exists in the database.
963
964=head2 C<$obj-E<gt>save()>
965
966Saves C<$obj> to the database, whether it is already there or not. That is,
967C<save()> is functionally:
968
969    $obj->exists() ? $obj->update() : $obj->insert()
970
971=head2 C<$obj-E<gt>update()>
972
973Saves changes to C<$obj>, an object that already exists in its database.
974
975=head2 C<$obj-E<gt>insert()>
976
977Adds C<$obj> to the database in which it should exist, according to its object
978driver and configuration.
979
980=head2 C<$obj-E<gt>remove()>
981
982Deletes C<$obj> from its database.
983
984=head2 C<$obj-E<gt>replace()>
985
986Replaces C<$obj> in the database. Does the right thing if the driver
987knows how to REPLACE object, ala MySQL.
988
989=head1 USAGE
990
991=head2 C<Class-E<gt>new(%columns)>
992
993Returns a new object of the given class, initializing its columns with the values
994in C<%columns>.
995
996=head2 C<$obj-E<gt>init(%columns)>
997
998Initializes C<$obj>i by initializing its columns with the values in
999C<%columns>.
1000
1001Override this method if you must do initial configuration to new instances of
1002C<$obj>'s class that are not more appropriate as a C<post_load> callback.
1003
1004=head2 C<Class-E<gt>properties()>
1005
1006Returns the named object class's properties as a hashref. Note that some of the
1007standard object class properties, such as C<primary_key>, have more convenient
1008accessors than reading the properties directly.
1009
1010=head2 C<Class-E<gt>driver()>
1011
1012Returns the object driver for this class, invoking the class's I<get_driver>
1013function (and caching the result for future calls) if necessary.
1014
1015=head2 C<Class-E<gt>get_driver($get_driver_fn)>
1016
1017Sets the function used to find the object driver for I<Class> objects (that is,
1018the C<get_driver> property).
1019
1020Note that once C<driver()> has been called, the C<get_driver> function is not
1021used. Usually you would specify your function as the C<get_driver> parameter to
1022C<install_properties()>.
1023
1024=head2 C<Class-E<gt>is_pkless()>
1025
1026Returns whether the given object class has a primary key defined.
1027
1028=head2 C<Class-E<gt>is_primary_key($column)>
1029
1030Returns whether the given column is or is part of the primary key for C<Class>
1031objects.
1032
1033=head2 C<$obj-E<gt>primary_key()>
1034
1035Returns the I<values> of the primary key fields of C<$obj>.
1036
1037=head2 C<Class-E<gt>primary_key_tuple()>
1038
1039Returns the I<names> of the primary key fields of C<Class> objects.
1040
1041=head2 C<$obj-E<gt>is_same($other_obj)>
1042
1043Do a primary key check on C<$obj> and $<other_obj> and returns true only if they
1044are identical.
1045
1046=head2 C<$obj-E<gt>object_is_stored()>
1047
1048Returns true if the object hasn't been stored in the database yet.
1049This is particularily useful in triggers where you can then determine
1050if the object is being INSERTED or just UPDATED.
1051
1052=head2 C<$obj-E<gt>pk_str()>
1053
1054returns the primay key has a printable string.
1055
1056=head2 C<$obj-E<gt>has_primary_key()>
1057
1058Returns whether the given object has values for all of its primary key fields.
1059
1060=head2 C<$obj-E<gt>uncache_object()>
1061
1062If you use a Cache driver, returned object will be automatically cached as a result
1063of common retrieve operations. In some rare cases you may want the cache to be cleared
1064explicitely, and this method provides you with a way to do it.
1065
1066=head2 C<$obj-E<gt>primary_key_to_terms([$id])>
1067
1068Returns C<$obj>'s primary key as a hashref of values keyed on column names,
1069suitable for passing as C<search()> terms. If C<$id> is specified, convert that
1070primary key instead of C<$obj>'s.
1071
1072=head2 C<Class-E<gt>datasource()>
1073
1074Returns the datasource for objects of class C<Class>. That is, returns the
1075C<datasource> property of C<Class>.
1076
1077=head2 C<Class-E<gt>columns_of_type($type)>
1078
1079Returns the list of columns in C<Class> objects that hold data of type
1080C<$type>, as an arrayref. Columns are of a certain type when they are set that
1081way in C<Class>'s C<column_defs> property.
1082
1083=head2 C<$obj-E<gt>set_values(\%values)>
1084
1085Sets all the columns of C<$obj> that are members of C<\%values> to the values
1086specified there.
1087
1088=head2 C<$obj-E<gt>set_values_internal(\%values)>
1089
1090Sets new specified values of C<$obj>, without using any overridden mutator
1091methods of C<$obj> and without marking the changed columns changed.
1092
1093=head2 C<$obj-E<gt>clone()>
1094
1095Returns a new object of the same class as I<$obj> containing the same data,
1096except for primary keys, which are set to C<undef>.
1097
1098=head2 C<$obj-E<gt>clone_all()>
1099
1100Returns a new object of the same class as I<$obj> containing the same data,
1101including all key fields.
1102
1103=head2 C<Class-E<gt>has_column($column)>
1104
1105Returns whether a column named C<$column> exists in objects of class <Class>.
1106
1107=head2 C<Class-E<gt>column_names()>
1108
1109Returns the list of columns in C<Class> objects as an arrayref.
1110
1111=head2 C<$obj-E<gt>column_values()>
1112
1113Returns the columns and values in the given object as a hashref.
1114
1115=head2 C<$obj-E<gt>column($column, [$value])>
1116
1117Returns the value of C<$obj>'s column C<$column>. If C<$value> is specified,
1118C<column()> sets the first.
1119
1120Note the usual way of accessing and mutating column values is through the named
1121accessors:
1122
1123    $obj->column('fred', 'barney');  # possible
1124    $obj->fred('barney');            # preferred
1125
1126=head2 C<$obj-E<gt>is_changed([$column])>
1127
1128Returns whether any values in C<$obj> have changed. If C<$column> is given,
1129returns specifically whether that column has changed.
1130
1131=head2 C<$obj-E<gt>changed_cols_and_pk()>
1132
1133Returns the list of all columns that have changed in C<$obj> since it was last
1134loaded from or saved to the database, as a list.
1135
1136=head2 C<$obj-E<gt>changed_cols()>
1137
1138Returns the list of changed columns in C<$obj> as a list, except for any
1139columns in C<$obj>'s primary key (even if they have changed).
1140
1141=head2 C<Class-E<gt>lookup_multi(\@ids)>
1142
1143Returns a list (arrayref) of objects as specified by their primary keys.
1144
1145=head2 C<Class-E<gt>bulk_insert(\@columns, \@data)>
1146
1147Adds the given data, an arrayref of arrayrefs containing column values in the
1148order of column names given in C<\@columns>, as directly to the database as
1149C<Class> records.
1150
1151Note that only some database drivers (for example,
1152C<Data::ObjectDriver::Driver::DBD::Pg>) implement the bulk insert operation.
1153
1154=head2 C<$obj-E<gt>fetch_data()>
1155
1156Returns the current values from C<$obj> as saved in the database, as a hashref.
1157
1158=head2 C<$obj-E<gt>refresh()>
1159
1160Resets the values of C<$obj> from the database. Any unsaved modifications to
1161C<$obj> will be lost, and any made meanwhile will be reflected in C<$obj>
1162afterward.
1163
1164=head2 C<$obj-E<gt>column_func($column)>
1165
1166Creates an accessor/mutator method for column C<$column>, returning it as a
1167coderef.
1168
1169Override this if you need special behavior in all accessor/mutator methods.
1170
1171=head2 C<$obj-E<gt>deflate()>
1172
1173Returns a minimal representation of the object, for use in caches where
1174you might want to preserve space (like memcached). Can also be overridden
1175by subclasses to store the optimal representation of an object in the
1176cache. For example, if you have metadata attached to an object, you might
1177want to store that in the cache, as well.
1178
1179=head2 C<Class-E<gt>inflate($deflated)>
1180
1181Inflates the deflated representation of the object I<$deflated> into a proper
1182object in the class I<Class>. That is, undoes the operation C<$deflated =
1183$obj-E<gt>deflate()> by returning a new object equivalent to C<$obj>.
1184
1185=head1 TRANSACTION SUPPORT AND METHODS
1186
1187=head2 Introduction
1188
1189When dealing with the methods on this class, the transactions are global,
1190i.e: applied to all drivers. You can still enable transactions per driver
1191if you directly use the driver API.
1192
1193=head2 C<Class-E<gt>begin_work>
1194
1195This enable transactions globally for all drivers until the next L<rollback>
1196or L<commit> call on the class.
1197
1198If begin_work is called while a transaction is still active (nested transaction)
1199then the two transactions are merged. So inner transactions are ignored and
1200a warning will be emitted.
1201
1202=head2 C<Class-E<gt>rollback>
1203
1204This rollbacks all the transactions since the last begin work, and exits
1205from the active transaction state.
1206
1207=head2 C<Class-E<gt>commit>
1208
1209Commits the transactions, and exits from the active transaction state.
1210
1211=head2 C<Class-E<gt>txn_debug>
1212
1213Just return the value of the global flag and the current working drivers
1214in a hashref.
1215
1216=head2 C<Class-E<gt>txn_active>
1217
1218Returns true if a transaction is already active.
1219
1220=head1 DIAGNOSTICS
1221
1222=over 4
1223
1224=item * C<Please specify a valid column for I<class>>
1225
1226One of the class relationships you defined with C<has_a()> was missing a
1227C<column> member.
1228
1229=item * C<Please define a valid method for I<column>>
1230
1231One of the class relationships you defined with C<has_a()> was missing its
1232C<method> member and a method name could not be generated, or the class for
1233which you specified the relationship already has a method by that name. Perhaps
1234you specified an additional accessor by the same name for that class.
1235
1236=item * C<keys don't match with primary keys: I<list>>
1237
1238The hashref of values you passed as the ID to C<primary_key_to_terms()> was
1239missing or had extra members. Perhaps you used a full C<column_values()> hash
1240instead of only including that class's key fields.
1241
1242=item * C<You tried to set inexistent column I<column name> to value I<data> on I<class name>>
1243
1244The hashref you specified to C<set_values()> contained keys that are not
1245defined columns for that class of object. Perhaps you invoked it on the wrong
1246class, or did not fully filter members of the hash out before using it.
1247
1248=item * C<Cannot find column 'I<column>' for class 'I<class>'>
1249
1250The column you specified to C<column()> does not exist for that class, you
1251attempted to use an automatically generated accessor/mutator for a column that
1252doesn't exist, or attempted to use a column accessor as a class method instead
1253of an instance method. Perhaps you performed your call on the wrong class or
1254variable, or misspelled a method or column name.
1255
1256=item * C<Must specify column>
1257
1258You invoked the C<column_func()> method without specifying a column name.
1259Column names are required to create the accessor/mutator function, so it knows
1260what data member of the object to use.
1261
1262=item * C<number (of partitions) is required>
1263
1264You attempted to define partitioning for a class without specifying the number
1265of partitions for that class in the C<number> member. Perhaps your logic for
1266determining the number of partitions resulted in C<undef> or 0.
1267
1268=item * C<get_driver is required>
1269
1270You attempted to define partitioning for a class without specifying the
1271function to find the object driver for a partition ID as the C<get_driver>
1272member.
1273
1274=back
1275
1276=head1 BUGS AND LIMITATIONS
1277
1278There are no known bugs in this module.
1279
1280=head1 SEE ALSO
1281
1282L<Data::ObjectDriver>, L<Data::ObjectDriver::Driver::DBI>,
1283L<Data::ObjectDriver::Driver::SimplePartition>
1284
1285=head1 LICENSE
1286
1287I<Data::ObjectDriver> is free software; you may redistribute it and/or modify
1288it under the same terms as Perl itself.
1289
1290=head1 AUTHOR & COPYRIGHT
1291
1292Except where otherwise noted, I<Data::ObjectDriver> is Copyright 2005-2006
1293Six Apart, cpan@sixapart.com. All rights reserved.
1294
1295=cut
1296
Note: See TracBrowser for help on using the browser.