X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=blobdiff_plain;f=lib%2FDBIx%2FClass%2FRow.pm;h=ac12eadbbfbf6fe23c20db208fc10ef6d7ca4a56;hb=72c2540d9f7d45625853abc5d1782020fedc3887;hp=2280c50efd78461ec24e12b7b959efd740f2d9f7;hpb=4294954ac83c42e52891e2191a1faacd454cdb47;p=dbsrgits%2FDBIx-Class.git diff --git a/lib/DBIx/Class/Row.pm b/lib/DBIx/Class/Row.pm index 2280c50..ac12ead 100644 --- a/lib/DBIx/Class/Row.pm +++ b/lib/DBIx/Class/Row.pm @@ -5,8 +5,8 @@ use warnings; use base qw/DBIx::Class/; -use DBIx::Class::Exception; use Scalar::Util 'blessed'; +use List::Util 'first'; use Try::Tiny; ### @@ -22,6 +22,8 @@ BEGIN { use namespace::clean; +__PACKAGE__->mk_group_accessors ( simple => [ in_storage => '_in_storage' ] ); + =head1 NAME DBIx::Class::Row - Basic row methods @@ -33,14 +35,31 @@ DBIx::Class::Row - Basic row methods This class is responsible for defining and doing basic operations on rows derived from L objects. -Row objects are returned from Ls using the +Result objects are returned from Ls using the L, L, L and L methods, as well as invocations of 'single' ( L, L or L) -relationship accessors of L objects. +relationship accessors of L objects. + +=head1 NOTE + +All "Row objects" derived from a Schema-attached L +object (such as a typical C<< L->L >> call) are actually Result +instances, based on your application's +L. + +L implements most of the row-based communication with the +underlying storage, but a Result class B. +Usually, Result classes inherit from L, which in turn +combines the methods from several classes, one of them being +L. Therefore, while many of the methods available to a +L-derived Result class are described in the following +documentation, it does not detail all of the methods available to Result +objects. Refer to L for more info. =head1 METHODS @@ -54,11 +73,11 @@ relationship accessors of L objects. =item Arguments: \%attrs or \%colsandvalues -=item Returns: A Row object +=item Return Value: L<$result|DBIx::Class::Manual::ResultClass> =back -While you can create a new row object by calling C directly on +While you can create a new result object by calling C directly on this class, you are better off calling it on a L object. @@ -105,26 +124,26 @@ with NULL as the default, and save yourself a SELECT. ## tests! sub __new_related_find_or_new_helper { - my ($self, $relname, $data) = @_; + my ($self, $relname, $values) = @_; my $rsrc = $self->result_source; # create a mock-object so all new/set_column component overrides will run: my $rel_rs = $rsrc->related_source($relname)->resultset; - my $new_rel_obj = $rel_rs->new_result($data); + my $new_rel_obj = $rel_rs->new_result($values); my $proc_data = { $new_rel_obj->get_columns }; if ($self->__their_pk_needs_us($relname)) { - MULTICREATE_DEBUG and warn "MC $self constructing $relname via new_result"; + MULTICREATE_DEBUG and print STDERR "MC $self constructing $relname via new_result\n"; return $new_rel_obj; } elsif ($rsrc->_pk_depends_on($relname, $proc_data )) { if (! keys %$proc_data) { # there is nothing to search for - blind create - MULTICREATE_DEBUG and warn "MC $self constructing default-insert $relname"; + MULTICREATE_DEBUG and print STDERR "MC $self constructing default-insert $relname\n"; } else { - MULTICREATE_DEBUG and warn "MC $self constructing $relname via find_or_new"; + MULTICREATE_DEBUG and print STDERR "MC $self constructing $relname via find_or_new\n"; # this is not *really* find or new, as we don't want to double-new the # data (thus potentially double encoding or whatever) my $exists = $rel_rs->find ($proc_data); @@ -143,9 +162,9 @@ sub __new_related_find_or_new_helper { sub __their_pk_needs_us { # this should maybe be in resultsource. my ($self, $relname) = @_; - my $source = $self->result_source; - my $reverse = $source->reverse_relationship_info($relname); - my $rel_source = $source->related_source($relname); + my $rsrc = $self->result_source; + my $reverse = $rsrc->reverse_relationship_info($relname); + my $rel_source = $rsrc->related_source($relname); my $us = { $self->get_columns }; foreach my $key (keys %$reverse) { # if their primary key depends on us, then we have to @@ -159,18 +178,18 @@ sub new { my ($class, $attrs) = @_; $class = ref $class if ref $class; - my $new = bless { _column_data => {} }, $class; + my $new = bless { _column_data => {}, _in_storage => 0 }, $class; if ($attrs) { $new->throw_exception("attrs must be a hashref") unless ref($attrs) eq 'HASH'; - my $source = delete $attrs->{-result_source}; + my $rsrc = delete $attrs->{-result_source}; if ( my $h = delete $attrs->{-source_handle} ) { - $source ||= $h->resolve; + $rsrc ||= $h->resolve; } - $new->result_source($source) if $source; + $new->result_source($rsrc) if $rsrc; if (my $col_from_rel = delete $attrs->{-cols_from_relations}) { @{$new->{_ignore_at_insert}={}}{@$col_from_rel} = (); @@ -182,8 +201,8 @@ sub new { if (ref $attrs->{$key}) { ## Can we extract this lot to use with update(_or .. ) ? $new->throw_exception("Can't do multi-create without result source") - unless $source; - my $info = $source->relationship_info($key); + unless $rsrc; + my $info = $rsrc->relationship_info($key); my $acc_type = $info->{attrs}{accessor} || ''; if ($acc_type eq 'single') { my $rel_obj = delete $attrs->{$key}; @@ -195,7 +214,7 @@ sub new { $new->{_rel_in_storage}{$key} = 1; $new->set_from_related($key, $rel_obj); } else { - MULTICREATE_DEBUG and warn "MC $new uninserted $key $rel_obj\n"; + MULTICREATE_DEBUG and print STDERR "MC $new uninserted $key $rel_obj\n"; } $related->{$key} = $rel_obj; @@ -215,7 +234,7 @@ sub new { $rel_obj->throw_exception ('A multi relationship can not be pre-existing when doing multicreate. Something went wrong'); } else { MULTICREATE_DEBUG and - warn "MC $new uninserted $key $rel_obj (${\($idx+1)} of $total)\n"; + print STDERR "MC $new uninserted $key $rel_obj (${\($idx+1)} of $total)\n"; } push(@objects, $rel_obj); } @@ -232,7 +251,7 @@ sub new { $new->{_rel_in_storage}{$key} = 1; } else { - MULTICREATE_DEBUG and warn "MC $new uninserted $key $rel_obj"; + MULTICREATE_DEBUG and print STDERR "MC $new uninserted $key $rel_obj\n"; } $inflated->{$key} = $rel_obj; next; @@ -242,7 +261,7 @@ sub new { next; } } - $new->throw_exception("No such column $key on $class") + $new->throw_exception("No such column '$key' on $class") unless $class->has_column($key); $new->store_column($key => $attrs->{$key}); } @@ -254,6 +273,42 @@ sub new { return $new; } +=head2 $column_accessor + + # Each pair does the same thing + + # (un-inflated, regular column) + my $val = $row->get_column('first_name'); + my $val = $row->first_name; + + $row->set_column('first_name' => $val); + $row->first_name($val); + + # (inflated column via DBIx::Class::InflateColumn::DateTime) + my $val = $row->get_inflated_column('last_modified'); + my $val = $row->last_modified; + + $row->set_inflated_column('last_modified' => $val); + $row->last_modified($val); + +=over + +=item Arguments: $value? + +=item Return Value: $value + +=back + +A column accessor method is created for each column, which is used for +getting/setting the value for that column. + +The actual method name is based on the +L name given during the +L L. Like L, this +will not store the data in the database until L or L +is called on the row. + =head2 insert $row->insert; @@ -262,7 +317,7 @@ sub new { =item Arguments: none -=item Returns: The Row object +=item Return Value: L<$result|DBIx::Class::Manual::ResultClass> =back @@ -270,8 +325,8 @@ Inserts an object previously created by L into the database if it isn't already in there. Returns the object itself. To insert an entirely new row into the database, use L. -To fetch an uninserted row object, call -L on a resultset. +To fetch an uninserted result object, call +L on a resultset. This will also insert any uninserted, related objects held inside this one, see L for more details. @@ -281,11 +336,11 @@ one, see L for more details. sub insert { my ($self) = @_; return $self if $self->in_storage; - my $source = $self->result_source; + my $rsrc = $self->result_source; $self->throw_exception("No result_source set on this object; can't insert") - unless $source; + unless $rsrc; - my $storage = $source->storage; + my $storage = $rsrc->storage; my $rollback_guard; @@ -301,14 +356,14 @@ sub insert { if (! $self->{_rel_in_storage}{$relname}) { next unless (blessed $rel_obj && $rel_obj->isa('DBIx::Class::Row')); - next unless $source->_pk_depends_on( + next unless $rsrc->_pk_depends_on( $relname, { $rel_obj->get_columns } ); # The guard will save us if we blow out of this scope via die $rollback_guard ||= $storage->txn_scope_guard; - MULTICREATE_DEBUG and warn "MC $self pre-reconstructing $relname $rel_obj\n"; + MULTICREATE_DEBUG and print STDERR "MC $self pre-reconstructing $relname $rel_obj\n"; my $them = { %{$rel_obj->{_relationship_data} || {} }, $rel_obj->get_columns }; my $existing; @@ -340,14 +395,14 @@ sub insert { MULTICREATE_DEBUG and do { no warnings 'uninitialized'; - warn "MC $self inserting (".join(', ', $self->get_columns).")\n"; + print STDERR "MC $self inserting (".join(', ', $self->get_columns).")\n"; }; # perform the insert - the storage will return everything it is asked to # (autoinc primary columns and any retrieve_on_insert columns) my %current_rowdata = $self->get_columns; my $returned_cols = $storage->insert( - $source, + $rsrc, { %current_rowdata }, # what to insert, copy because the storage *will* change it ); @@ -356,17 +411,22 @@ sub insert { # this ensures we fire store_column only once # (some asshats like overriding it) if ( - (! defined $current_rowdata{$_}) + (!exists $current_rowdata{$_}) or - ( $current_rowdata{$_} ne $returned_cols->{$_}) + (defined $current_rowdata{$_} xor defined $returned_cols->{$_}) + or + (defined $current_rowdata{$_} and $current_rowdata{$_} ne $returned_cols->{$_}) ); } + delete $self->{_column_data_in_storage}; + $self->in_storage(1); + $self->{_dirty_columns} = {}; $self->{related_resultsets} = {}; foreach my $relname (keys %related_stuff) { - next unless $source->has_relationship ($relname); + next unless $rsrc->has_relationship ($relname); my @cands = ref $related_stuff{$relname} eq 'ARRAY' ? @{$related_stuff{$relname}} @@ -375,29 +435,27 @@ sub insert { if (@cands && blessed $cands[0] && $cands[0]->isa('DBIx::Class::Row') ) { - my $reverse = $source->reverse_relationship_info($relname); + my $reverse = $rsrc->reverse_relationship_info($relname); foreach my $obj (@cands) { $obj->set_from_related($_, $self) for keys %$reverse; if ($self->__their_pk_needs_us($relname)) { if (exists $self->{_ignore_at_insert}{$relname}) { - MULTICREATE_DEBUG and warn "MC $self skipping post-insert on $relname"; + MULTICREATE_DEBUG and print STDERR "MC $self skipping post-insert on $relname\n"; } else { - MULTICREATE_DEBUG and warn "MC $self inserting $relname $obj"; + MULTICREATE_DEBUG and print STDERR "MC $self inserting $relname $obj\n"; $obj->insert; } } else { - MULTICREATE_DEBUG and warn "MC $self post-inserting $obj"; + MULTICREATE_DEBUG and print STDERR "MC $self post-inserting $obj\n"; $obj->insert(); } } } } - $self->in_storage(1); - delete $self->{_orig_ident}; - delete $self->{_orig_ident_failreason}; delete $self->{_ignore_at_insert}; + $rollback_guard->commit if $rollback_guard; return $self; @@ -412,7 +470,7 @@ sub insert { =item Arguments: none or 1|0 -=item Returns: 1|0 +=item Return Value: 1|0 =back @@ -421,16 +479,9 @@ not. This is set to true when L, L or L are used. -Creating a row object using L, or calling -L on one, sets it to false. - -=cut +Creating a result object using L, or +calling L on one, sets it to false. -sub in_storage { - my ($self, $val) = @_; - $self->{_in_storage} = $val if @_ > 1; - return $self->{_in_storage} ? 1 : 0; -} =head2 update @@ -440,11 +491,11 @@ sub in_storage { =item Arguments: none or a hashref -=item Returns: The Row object +=item Return Value: L<$result|DBIx::Class::Manual::ResultClass> =back -Throws an exception if the row object is not yet in the database, +Throws an exception if the result object is not yet in the database, according to L. This method issues an SQL UPDATE query to commit any changes to the @@ -464,12 +515,12 @@ to C, e.g. ( { %{ $href } } ) If the values passed or any of the column values set on the object contain scalar references, e.g.: - $row->last_modified(\'NOW()'); + $row->last_modified(\'NOW()')->update(); # OR $row->update({ last_modified => \'NOW()' }); The update will pass the values verbatim into SQL. (See -L docs). The values in your Row object will NOT change +L docs). The values in your Result object will NOT change as a result of the update call, if you want the object to be updated with the actual values from the database, call L after the update. @@ -494,14 +545,10 @@ sub update { my %to_update = $self->get_dirty_columns or return $self; - my $ident_cond = $self->{_orig_ident} || $self->ident_condition; $self->throw_exception( "Not in database" ) unless $self->in_storage; - $self->throw_exception($self->{_orig_ident_failreason}) - if ! keys %$ident_cond; - my $rows = $self->result_source->storage->update( - $self->result_source, \%to_update, $ident_cond + $self->result_source, \%to_update, $self->_storage_ident_condition ); if ($rows == 0) { $self->throw_exception( "Can't update ${self}: row not found" ); @@ -510,7 +557,7 @@ sub update { } $self->{_dirty_columns} = {}; $self->{related_resultsets} = {}; - delete $self->{_orig_ident}; + delete $self->{_column_data_in_storage}; return $self; } @@ -522,7 +569,7 @@ sub update { =item Arguments: none -=item Returns: The Row object +=item Return Value: L<$result|DBIx::Class::Manual::ResultClass> =back @@ -546,7 +593,7 @@ main row first> and only then attempts to delete any remaining related rows. If you delete an object within a txn_do() (see L) -and the transaction subsequently fails, the row object will remain marked as +and the transaction subsequently fails, the result object will remain marked as not being in storage. If you know for a fact that the object is still in storage (i.e. by inspecting the cause of the transaction's failure), you can use C<< $obj->in_storage(1) >> to restore consistency between the object and @@ -562,16 +609,12 @@ sub delete { if (ref $self) { $self->throw_exception( "Not in database" ) unless $self->in_storage; - my $ident_cond = $self->{_orig_ident} || $self->ident_condition; - $self->throw_exception($self->{_orig_ident_failreason}) - if ! keys %$ident_cond; - $self->result_source->storage->delete( - $self->result_source, $ident_cond + $self->result_source, $self->_storage_ident_condition ); - delete $self->{_orig_ident}; # no longer identifiable - $self->in_storage(undef); + delete $self->{_column_data_in_storage}; + $self->in_storage(0); } else { my $rsrc = try { $self->result_source_instance } @@ -592,14 +635,14 @@ sub delete { =item Arguments: $columnname -=item Returns: The value of the column +=item Return Value: The value of the column =back Throws an exception if the column name given doesn't exist according -to L. +to L. -Returns a raw column value from the row object, if it has already +Returns a raw column value from the result object, if it has already been fetched from the database or set by an accessor. If an L has been set, it @@ -636,7 +679,7 @@ sub get_column { =item Arguments: $columnname -=item Returns: 0|1 +=item Return Value: 0|1 =back @@ -660,7 +703,7 @@ sub has_column_loaded { =item Arguments: none -=item Returns: A hash of columnname, value pairs. +=item Return Value: A hash of columnname, value pairs. =back @@ -690,7 +733,7 @@ sub get_columns { =item Arguments: none -=item Returns: A hash of column, value pairs +=item Return Value: A hash of column, value pairs =back @@ -715,7 +758,7 @@ sub get_dirty_columns { =item Arguments: $columnname -=item Returns: undefined +=item Return Value: not defined =back @@ -725,6 +768,7 @@ Marks a column as having been changed regardless of whether it has really changed. =cut + sub make_column_dirty { my ($self, $column) = @_; @@ -755,7 +799,7 @@ sub make_column_dirty { =item Arguments: none -=item Returns: A hash of column, object|value pairs +=item Return Value: A hash of column, object|value pairs =back @@ -818,7 +862,7 @@ sub _is_column_numeric { =item Arguments: $columnname, $value -=item Returns: $value +=item Return Value: $value =back @@ -828,32 +872,23 @@ the column is marked as dirty for when you next call L. If passed an object or reference as a value, this method will happily attempt to store it, and a later L or L will try and stringify/numify as appropriate. To set an object to be deflated -instead, see L. +instead, see L, or better yet, use L. =cut sub set_column { my ($self, $column, $new_value) = @_; - # if we can't get an ident condition on first try - mark the object as unidentifiable - # (by using an empty hashref) and store the error for further diag - unless ($self->{_orig_ident}) { - try { - $self->{_orig_ident} = $self->ident_condition - } - catch { - $self->{_orig_ident_failreason} = $_; - $self->{_orig_ident} = {}; - }; - } + my $had_value = $self->has_column_loaded($column); + my ($old_value, $in_storage) = ($self->get_column($column), $self->in_storage) + if $had_value; - my $old_value = $self->get_column($column); $new_value = $self->store_column($column, $new_value); my $dirty = $self->{_dirty_columns}{$column} || - $self->in_storage # no point tracking dirtyness on uninserted data + $in_storage # no point tracking dirtyness on uninserted data ? ! $self->_eq_column_values ($column, $old_value, $new_value) : 1 ; @@ -866,22 +901,37 @@ sub set_column { # # FIXME - this is a quick *largely incorrect* hack, pending a more # serious rework during the merge of single and filter rels - my $rels = $self->result_source->{_relationships}; - for my $rel (keys %$rels) { + my $relnames = $self->result_source->{_relationships}; + for my $relname (keys %$relnames) { - my $acc = $rels->{$rel}{attrs}{accessor} || ''; + my $acc = $relnames->{$relname}{attrs}{accessor} || ''; - if ( $acc eq 'single' and $rels->{$rel}{attrs}{fk_columns}{$column} ) { - delete $self->{related_resultsets}{$rel}; - delete $self->{_relationship_data}{$rel}; - #delete $self->{_inflated_column}{$rel}; + if ( $acc eq 'single' and $relnames->{$relname}{attrs}{fk_columns}{$column} ) { + delete $self->{related_resultsets}{$relname}; + delete $self->{_relationship_data}{$relname}; + #delete $self->{_inflated_column}{$relname}; } - elsif ( $acc eq 'filter' and $rel eq $column) { - delete $self->{related_resultsets}{$rel}; - #delete $self->{_relationship_data}{$rel}; - delete $self->{_inflated_column}{$rel}; + elsif ( $acc eq 'filter' and $relname eq $column) { + delete $self->{related_resultsets}{$relname}; + #delete $self->{_relationship_data}{$relname}; + delete $self->{_inflated_column}{$relname}; } } + + if ( + # value change from something (even if NULL) + $had_value + and + # no storage - no storage-value + $in_storage + and + # no value already stored (multiple changes before commit to storage) + ! exists $self->{_column_data_in_storage}{$column} + and + $self->_track_storage_value($column) + ) { + $self->{_column_data_in_storage}{$column} = $old_value; + } } return $new_value; @@ -907,6 +957,13 @@ sub _eq_column_values { } } +# returns a boolean indicating if the passed column should have its original +# value tracked between column changes and commitment to storage +sub _track_storage_value { + my ($self, $col) = @_; + return defined first { $col eq $_ } ($self->primary_columns); +} + =head2 set_columns $row->set_columns({ $col => $val, ... }); @@ -915,7 +972,7 @@ sub _eq_column_values { =item Arguments: \%columndata -=item Returns: The Row object +=item Return Value: L<$result|DBIx::Class::Manual::ResultClass> =back @@ -926,10 +983,8 @@ Works as L. =cut sub set_columns { - my ($self,$data) = @_; - foreach my $col (keys %$data) { - $self->set_column($col,$data->{$col}); - } + my ($self, $values) = @_; + $self->set_column( $_, $values->{$_} ) for keys %$values; return $self; } @@ -941,16 +996,16 @@ sub set_columns { =item Arguments: \%columndata -=item Returns: The Row object +=item Return Value: L<$result|DBIx::Class::Manual::ResultClass> =back Sets more than one column value at once. Any inflated values are deflated and the raw values stored. -Any related values passed as Row objects, using the relation name as a +Any related values passed as Result objects, using the relation name as a key, are reduced to the appropriate foreign key values and stored. If -instead of related row objects, a hashref of column, value data is +instead of related result objects, a hashref of column, value data is passed, will create the related object first then store. Will even accept arrayrefs of data as a value to a @@ -973,9 +1028,9 @@ sub set_inflated_columns { my $info = $self->relationship_info($key); my $acc_type = $info->{attrs}{accessor} || ''; if ($acc_type eq 'single') { - my $rel = delete $upd->{$key}; - $self->set_from_related($key => $rel); - $self->{_relationship_data}{$key} = $rel; + my $rel_obj = delete $upd->{$key}; + $self->set_from_related($key => $rel_obj); + $self->{_relationship_data}{$key} = $rel_obj; } elsif ($acc_type eq 'multi') { $self->throw_exception( @@ -998,7 +1053,7 @@ sub set_inflated_columns { =item Arguments: \%replacementdata -=item Returns: The Row object copy +=item Return Value: L<$result|DBIx::Class::Manual::ResultClass> copy =back @@ -1038,19 +1093,19 @@ sub copy { # Its possible we'll have 2 relations to the same Source. We need to make # sure we don't try to insert the same row twice else we'll violate unique # constraints - my $rels_copied = {}; + my $relnames_copied = {}; - foreach my $rel ($self->result_source->relationships) { - my $rel_info = $self->result_source->relationship_info($rel); + foreach my $relname ($self->result_source->relationships) { + my $rel_info = $self->result_source->relationship_info($relname); next unless $rel_info->{attrs}{cascade_copy}; my $resolved = $self->result_source->_resolve_condition( - $rel_info->{cond}, $rel, $new, $rel + $rel_info->{cond}, $relname, $new, $relname ); - my $copied = $rels_copied->{ $rel_info->{source} } ||= {}; - foreach my $related ($self->search_related($rel)) { + my $copied = $relnames_copied->{ $rel_info->{source} } ||= {}; + foreach my $related ($self->search_related($relname)) { my $id_str = join("\0", $related->id); next if $copied->{$id_str}; $copied->{$id_str} = 1; @@ -1069,7 +1124,7 @@ sub copy { =item Arguments: $columnname, $value -=item Returns: The value sent to storage +=item Return Value: The value sent to storage =back @@ -1077,7 +1132,7 @@ Set a raw value for a column without marking it as changed. This method is used internally by L which you should probably be using. -This is the lowest level at which data is set on a row object, +This is the lowest level at which data is set on a result object, extend this method to catch all data setting methods. =cut @@ -1097,14 +1152,14 @@ sub store_column { =over -=item Arguments: $result_source, \%columndata, \%prefetcheddata +=item Arguments: L<$result_source|DBIx::Class::ResultSource>, \%columndata, \%prefetcheddata -=item Returns: A Row object +=item Return Value: L<$result|DBIx::Class::Manual::ResultClass> =back All L methods that retrieve data from the -database and turn it into row objects call this method. +database and turn it into result objects call this method. Extend this method in your Result classes to hook into this process, for example to rebless the result into a different class. @@ -1118,63 +1173,58 @@ L, see L. =cut sub inflate_result { - my ($class, $source, $me, $prefetch) = @_; - - $source = $source->resolve - if $source->isa('DBIx::Class::ResultSourceHandle'); + my ($class, $rsrc, $me, $prefetch) = @_; my $new = bless - { _column_data => $me, _result_source => $source }, + { _column_data => $me, _result_source => $rsrc }, ref $class || $class ; - foreach my $pre (keys %{$prefetch||{}}) { + if ($prefetch) { + for my $relname ( keys %$prefetch ) { - my $pre_source = $source->related_source($pre) - or $class->throw_exception("Can't prefetch non-existent relationship ${pre}"); + my @rel_objects; + if ( + $prefetch->{$relname} + and + @{$prefetch->{$relname}} + and + ref($prefetch->{$relname}) ne $DBIx::Class::ResultSource::RowParser::Util::null_branch_class + ) { + my $rel_rsrc = try { + $rsrc->related_source($relname) + } catch { + my $err = sprintf + "Inflation into non-existent relationship '%s' of '%s' requested", + $relname, + $rsrc->source_name, + ; + if (my ($colname) = sort { length($a) <=> length ($b) } keys %{$prefetch->{$relname}[0] || {}} ) { + $err .= sprintf ", check the inflation specification (columns/as) ending in '...%s.%s'", + $relname, + $colname, + } - my $accessor = $source->relationship_info($pre)->{attrs}{accessor} - or $class->throw_exception("No accessor for prefetched $pre"); + $rsrc->throw_exception($err); + }; - my @pre_vals; - if (ref $prefetch->{$pre}[0] eq 'ARRAY') { - @pre_vals = @{$prefetch->{$pre}}; - } - elsif ($accessor eq 'multi') { - $class->throw_exception("Implicit prefetch (via select/columns) not supported with accessor 'multi'"); - } - else { - @pre_vals = $prefetch->{$pre}; - } + @rel_objects = map { + $rel_rsrc->result_class->inflate_result( $rel_rsrc, @$_ ) + } ( ref $prefetch->{$relname}[0] eq 'ARRAY' ? @{$prefetch->{$relname}} : $prefetch->{$relname} ); + } - my @pre_objects; - for my $me_pref (@pre_vals) { - - # FIXME - this should not be necessary - # the collapser currently *could* return bogus elements with all - # columns set to undef - my $has_def; - for (values %{$me_pref->[0]}) { - if (defined $_) { - $has_def++; - last; - } - } - next unless $has_def; + my $accessor = $rsrc->relationship_info($relname)->{attrs}{accessor} + or $class->throw_exception("No accessor type declared for prefetched relationship '$relname'"); - push @pre_objects, $pre_source->result_class->inflate_result( - $pre_source, @$me_pref - ); - } + if ($accessor eq 'single') { + $new->{_relationship_data}{$relname} = $rel_objects[0]; + } + elsif ($accessor eq 'filter') { + $new->{_inflated_column}{$relname} = $rel_objects[0]; + } - if ($accessor eq 'single') { - $new->{_relationship_data}{$pre} = $pre_objects[0]; - } - elsif ($accessor eq 'filter') { - $new->{_inflated_column}{$pre} = $pre_objects[0]; + $new->related_resultset($relname)->set_cache(\@rel_objects); } - - $new->related_resultset($pre)->set_cache(\@pre_objects); } $new->in_storage (1); @@ -1189,7 +1239,7 @@ sub inflate_result { =item Arguments: none -=item Returns: Result of update or insert operation +=item Return Value: Result of update or insert operation =back @@ -1220,7 +1270,7 @@ sub update_or_insert { =item Arguments: none -=item Returns: 0|1 or @columnnames +=item Return Value: 0|1 or @columnnames =back @@ -1242,7 +1292,7 @@ sub is_changed { =item Arguments: $columname -=item Returns: 0|1 +=item Return Value: 0|1 =back @@ -1261,9 +1311,9 @@ sub is_column_changed { =over -=item Arguments: $result_source_instance +=item Arguments: L<$result_source?|DBIx::Class::ResultSource> -=item Returns: a ResultSource instance +=item Return Value: L<$result_source|DBIx::Class::ResultSource> =back @@ -1299,7 +1349,7 @@ sub result_source { =item Arguments: $columnname, \%columninfo -=item Returns: undefined +=item Return Value: not defined =back @@ -1330,11 +1380,11 @@ sub register_column { =item Arguments: \%attrs -=item Returns: A Row object +=item Return Value: A Result object =back -Fetches a fresh copy of the Row object from the database and returns it. +Fetches a fresh copy of the Result object from the database and returns it. Throws an exception if a proper WHERE clause identifying the database row can not be constructed (i.e. if the original object does not contain its entire @@ -1342,11 +1392,11 @@ entire ). If passed the \%attrs argument, will first apply these attributes to the resultset used to find the row. -This copy can then be used to compare to an existing row object, to +This copy can then be used to compare to an existing result object, to determine if any changes have been made in the database since it was created. -To just update your Row object with any latest changes from the +To just update your Result object with any latest changes from the database, use L instead. The \%attrs argument should be compatible with @@ -1363,15 +1413,10 @@ sub get_from_storage { $resultset = $resultset->search(undef, $attrs); } - my $ident_cond = $self->{_orig_ident} || $self->ident_condition; - - $self->throw_exception($self->{_orig_ident_failreason}) - if ! keys %$ident_cond; - - return $resultset->find($ident_cond); + return $resultset->find($self->_storage_ident_condition); } -=head2 discard_changes ($attrs?) +=head2 discard_changes $row->discard_changes @@ -1379,7 +1424,7 @@ sub get_from_storage { =item Arguments: none or $attrs -=item Returns: self (updates object in-place) +=item Return Value: self (updates object in-place) =back @@ -1428,7 +1473,6 @@ sub discard_changes { } } - =head2 throw_exception See L. @@ -1461,9 +1505,9 @@ sub throw_exception { Returns the primary key(s) for a row. Can't be called as a class method. Actually implemented in L -=head1 AUTHORS +=head1 AUTHOR AND CONTRIBUTORS -Matt S. Trout +See L and L in DBIx::Class =head1 LICENSE