use base qw/DBIx::Class/;
use Carp::Clan qw/^DBIx::Class/;
use Scalar::Util ();
+use Scope::Guard;
__PACKAGE__->mk_group_accessors('simple' => qw/_source_handle/);
my ($related,$inflated);
## Pretend all the rels are actual objects, unset below if not, for insert() to fix
$new->{_rel_in_storage} = 1;
+
foreach my $key (keys %$attrs) {
if (ref $attrs->{$key}) {
## Can we extract this lot to use with update(_or .. ) ?
$self->throw_exception("No result_source set on this object; can't insert")
unless $source;
- # Check if we stored uninserted relobjs here in new()
- $source->storage->txn_begin if(!$self->{_rel_in_storage});
+ my $rollback_guard;
+ # Check if we stored uninserted relobjs here in new()
my %related_stuff = (%{$self->{_relationship_data} || {}},
%{$self->{_inflated_column} || {}});
- ## Should all be in relationship_data, but we need to get rid of the
- ## 'filter' reltype..
- ## These are the FK rels, need their IDs for the insert.
- foreach my $relname (keys %related_stuff) {
- my $rel_obj = $related_stuff{$relname};
- if(Scalar::Util::blessed($rel_obj) && $rel_obj->isa('DBIx::Class::Row')) {
- $rel_obj->insert();
- $self->set_from_related($relname, $rel_obj);
+
+ if(!$self->{_rel_in_storage})
+ {
+
+ $source->storage->txn_begin;
+
+ # The guard will save us if we blow out of this scope via die
+
+ $rollback_guard = Scope::Guard->new(sub { $source->storage->txn_rollback });
+
+ ## Should all be in relationship_data, but we need to get rid of the
+ ## 'filter' reltype..
+ ## These are the FK rels, need their IDs for the insert.
+
+ my @pri = $self->primary_columns;
+
+ REL: foreach my $relname (keys %related_stuff) {
+ my $keyhash = $source->resolve_condition(
+ $source->relationship_info($relname)->{cond},
+ undef, 1
+ ); # the above argset gives me the dependent cols on self
+
+ # assume anything that references our PK probably is dependent on us
+ # rather than vice versa
+
+ foreach my $p (@pri) {
+ next REL if exists $keyhash->{$p};
+ }
+
+ my $rel_obj = $related_stuff{$relname};
+ if(Scalar::Util::blessed($rel_obj) && $rel_obj->isa('DBIx::Class::Row')) {
+ $rel_obj->insert();
+ $self->set_from_related($relname, $rel_obj);
+ delete $related_stuff{$relname};
+ }
}
}
$source->storage->insert($source, { $self->get_columns });
## PK::Auto
- my ($pri, $too_many) = grep { !defined $self->get_column($_) ||
- ref($self->get_column($_)) eq 'SCALAR'} $self->primary_columns;
- if(defined $pri) {
- $self->throw_exception( "More than one possible key found for auto-inc on ".ref $self )
- if defined $too_many;
+ my @auto_pri = grep {
+ !defined $self->get_column($_) ||
+ ref($self->get_column($_)) eq 'SCALAR'
+ } $self->primary_columns;
+
+ if (@auto_pri) {
+ #$self->throw_exception( "More than one possible key found for auto-inc on ".ref $self )
+ # if defined $too_many;
my $storage = $self->result_source->storage;
$self->throw_exception( "Missing primary key but Storage doesn't support last_insert_id" )
unless $storage->can('last_insert_id');
- my $id = $storage->last_insert_id($self->result_source,$pri);
- $self->throw_exception( "Can't get last insert id" ) unless $id;
- $self->store_column($pri => $id);
+ my @ids = $storage->last_insert_id($self->result_source,@auto_pri);
+ $self->throw_exception( "Can't get last insert id" )
+ unless (@ids == @auto_pri);
+ $self->store_column($auto_pri[$_] => $ids[$_]) for 0 .. $#ids;
}
- ## Now do the has_many rels, that need $selfs ID.
- foreach my $relname (keys %related_stuff) {
- my $relobj = $related_stuff{$relname};
- if(ref $relobj eq 'ARRAY') {
- foreach my $obj (@$relobj) {
- my $info = $self->relationship_info($relname);
- ## What about multi-col FKs ?
- my $key = $1 if($info && (keys %{$info->{cond}})[0] =~ /^foreign\.(\w+)/);
- $obj->set_from_related($key, $self);
- $obj->insert() if(!$obj->in_storage);
+ if(!$self->{_rel_in_storage})
+ {
+ ## Now do the has_many rels, that need $selfs ID.
+ foreach my $relname (keys %related_stuff) {
+ my $rel_obj = $related_stuff{$relname};
+ my @cands;
+ if (Scalar::Util::blessed($rel_obj)
+ && $rel_obj->isa('DBIx::Class::Row')) {
+ @cands = ($rel_obj);
+ } elsif (ref $rel_obj eq 'ARRAY') {
+ @cands = @$rel_obj;
+ }
+ if (@cands) {
+ 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);
+ }
}
}
+ $source->storage->txn_commit;
+ $rollback_guard->dismiss;
}
- $source->storage->txn_commit if(!$self->{_rel_in_storage});
$self->in_storage(1);
$self->{_dirty_columns} = {};
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<update>.
+it being the same after a call to C<update>. If you need to preserve the hashref,
+it is sufficient to pass a shallow copy to C<update>, e.g. ( { %{ $href } } )
=cut
keys %{$self->{_dirty_columns}};
}
+=head2 get_inflated_columns
+
+ my $inflated_data = $obj->get_inflated_columns;
+
+Similar to get_columns but objects are returned for inflated columns instead of their raw non-inflated values.
+
+=cut
+
+sub get_inflated_columns {
+ my $self = shift;
+ return map {
+ my $accessor = $self->column_info($_)->{'accessor'} || $_;
+ ($_ => $self->$accessor);
+ } $self->columns;
+}
+
=head2 set_column
$obj->set_column($col => $val);
$fetched = $pre_source->result_class->inflate_result(
$pre_source, @{$pre_val});
}
+ $new->related_resultset($pre)->set_cache([ $fetched ]);
my $accessor = $source->relationship_info($pre)->{attrs}{accessor};
$class->throw_exception("No accessor for prefetched $pre")
unless defined $accessor;
sub throw_exception {
my $self=shift;
- if (ref $self && ref $self->result_source) {
+ if (ref $self && ref $self->result_source && $self->result_source->schema) {
$self->result_source->schema->throw_exception(@_);
} else {
croak(@_);