X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=blobdiff_plain;f=lib%2FDBIx%2FClass%2FRow.pm;h=6e348d3d420f63385cbdb9bb236e147cc1abd167;hb=c65da66155b84e36436ba5c514325dba01c29e12;hp=f9813f95a3db01904ccaec7c82e926e8ac8f24a3;hpb=ebe790dbc130d3604bb07e9636ff0458f8b464a9;p=dbsrgits%2FDBIx-Class.git diff --git a/lib/DBIx/Class/Row.pm b/lib/DBIx/Class/Row.pm index f9813f9..6e348d3 100644 --- a/lib/DBIx/Class/Row.pm +++ b/lib/DBIx/Class/Row.pm @@ -118,7 +118,6 @@ sub new { next; } } - use Data::Dumper; $new->throw_exception("No such column $key on $class") unless $class->has_column($key); $new->store_column($key => $attrs->{$key}); @@ -245,7 +244,7 @@ sub insert { my $reverse = $source->reverse_relationship_info($relname); foreach my $obj (@cands) { $obj->set_from_related($_, $self) for keys %$reverse; - $obj->insert() if(!$obj->in_storage); + $obj->insert() unless ($obj->in_storage || $obj->result_source->resultset->search({$obj->get_columns})->count); } } } @@ -284,9 +283,10 @@ UPDATE query to commit any changes to the object to the database if required. Also takes an options hashref of C<< column_name => value> pairs >> to update -first. But be aware that this hashref might be edited in place, so dont rely on -it being the same after a call to C. If you need to preserve the hashref, -it is sufficient to pass a shallow copy to C, e.g. ( { %{ $href } } ) +first. But be awawre that the hashref will be passed to +C, which might edit it in place, so dont rely on it being +the same after a call to C. If you need to preserve the hashref, it is +sufficient to pass a shallow copy to C, e.g. ( { %{ $href } } ) =cut @@ -297,38 +297,7 @@ sub update { $self->throw_exception("Cannot safely update a row in a PK-less table") if ! keys %$ident_cond; - if ($upd) { - foreach my $key (keys %$upd) { - if (ref $upd->{$key}) { - my $info = $self->relationship_info($key); - if ($info && $info->{attrs}{accessor} - && $info->{attrs}{accessor} eq 'single') - { - my $rel = delete $upd->{$key}; - $self->set_from_related($key => $rel); - $self->{_relationship_data}{$key} = $rel; - } elsif ($info && $info->{attrs}{accessor} - && $info->{attrs}{accessor} eq 'multi' - && ref $upd->{$key} eq 'ARRAY') { - my $others = delete $upd->{$key}; - foreach my $rel_obj (@$others) { - if(!Scalar::Util::blessed($rel_obj)) { - $rel_obj = $self->create_related($key, $rel_obj); - } - } - $self->{_relationship_data}{$key} = $others; -# $related->{$key} = $others; - next; - } - elsif ($self->has_column($key) - && exists $self->column_info($key)->{_inflate_info}) - { - $self->set_inflated_column($key, delete $upd->{$key}); - } - } - } - $self->set_columns($upd); - } + $self->set_inflated_columns($upd) if $upd; my %to_update = $self->get_dirty_columns; return $self unless keys %to_update; my $rows = $self->result_source->storage->update( @@ -355,7 +324,7 @@ usable, but C<< ->in_storage() >> will now return 0 and the object must reinserted using C<< ->insert() >> before C<< ->update() >> can be used on it. If you delete an object in a class with a C relationship, all the related objects will be deleted as well. To turn -this behavior off, pass C 0> in the C<$attr> +this behavior off, pass C<< cascade_delete => 0 >> in the C<$attr> hashref. Any database-level cascade or restrict will take precedence over a DBIx-Class-based cascading delete. See also L. @@ -515,6 +484,52 @@ sub set_columns { return $self; } +=head2 set_inflated_columns + + my $copy = $orig->set_inflated_columns({ $col => $val, $rel => $obj, ... }); + +Sets more than one column value at once, taking care to respect inflations and +relationships if relevant. Be aware that this hashref might be edited in place, +so dont rely on it being the same after a call to C. If +you need to preserve the hashref, it is sufficient to pass a shallow copy to +C, e.g. ( { %{ $href } } ) + +=cut + +sub set_inflated_columns { + my ( $self, $upd ) = @_; + foreach my $key (keys %$upd) { + if (ref $upd->{$key}) { + my $info = $self->relationship_info($key); + if ($info && $info->{attrs}{accessor} + && $info->{attrs}{accessor} eq 'single') + { + my $rel = delete $upd->{$key}; + $self->set_from_related($key => $rel); + $self->{_relationship_data}{$key} = $rel; + } elsif ($info && $info->{attrs}{accessor} + && $info->{attrs}{accessor} eq 'multi' + && ref $upd->{$key} eq 'ARRAY') { + my $others = delete $upd->{$key}; + foreach my $rel_obj (@$others) { + if(!Scalar::Util::blessed($rel_obj)) { + $rel_obj = $self->create_related($key, $rel_obj); + } + } + $self->{_relationship_data}{$key} = $others; +# $related->{$key} = $others; + next; + } + elsif ($self->has_column($key) + && exists $self->column_info($key)->{_inflate_info}) + { + $self->set_inflated_column($key, delete $upd->{$key}); + } + } + } + $self->set_columns($upd); +} + =head2 copy my $copy = $orig->copy({ change => $to, ... }); @@ -536,17 +551,31 @@ sub copy { bless $new, ref $self; $new->result_source($self->result_source); - $new->set_columns($changes); + $new->set_inflated_columns($changes); $new->insert; + + # 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 esle we'll violate unique + # constraints + my $rels_copied = {}; + foreach my $rel ($self->result_source->relationships) { my $rel_info = $self->result_source->relationship_info($rel); - if ($rel_info->{attrs}{cascade_copy}) { - my $resolved = $self->result_source->resolve_condition( - $rel_info->{cond}, $rel, $new); - foreach my $related ($self->search_related($rel)) { - $related->copy($resolved); - } + + next unless $rel_info->{attrs}{cascade_copy}; + + my $resolved = $self->result_source->resolve_condition( + $rel_info->{cond}, $rel, $new + ); + + my $copied = $rels_copied->{ $rel_info->{source} } ||= {}; + foreach my $related ($self->search_related($rel)) { + my $id_str = join("\0", $related->id); + next if $copied->{$id_str}; + $copied->{$id_str} = 1; + my $rel_copy = $related->copy($resolved); } + } return $new; } @@ -741,6 +770,22 @@ sub throw_exception { } } +=head2 id + +Returns the primary key(s) for a row. Can't be called as a class method. +Actually implemented in L + +=head2 discard_changes + +Re-selects the row from the database, losing any changes that had +been made. + +This method can also be used to refresh from storage, retrieving any +changes made since the row was last read from storage. Actually +implemented in L + +=cut + 1; =head1 AUTHORS