## check Relationship::CascadeActions and Relationship::Accessor for compat
## tests!
+sub __new_related_find_or_new_helper {
+ my ($self, $relname, $data) = @_;
+ if ($self->__their_pk_needs_us($relname, $data)) {
+ return $self->result_source
+ ->related_source($relname)
+ ->resultset
+ ->new_result($data);
+ }
+ if ($self->result_source->pk_depends_on($relname, $data)) {
+ return $self->result_source
+ ->related_source($relname)
+ ->resultset
+ ->find_or_create($data);
+ }
+ return $self->find_or_new_related($relname, $data);
+}
+
+sub __their_pk_needs_us { # this should maybe be in resultsource.
+ my ($self, $relname, $data) = @_;
+ my $source = $self->result_source;
+ my $reverse = $source->reverse_relationship_info($relname);
+ my $rel_source = $source->related_source($relname);
+ my $us = { $self->get_columns };
+ foreach my $key (keys %$reverse) {
+ # if their primary key depends on us, then we have to
+ # just create a result and we'll fill it out afterwards
+ return 1 if $rel_source->pk_depends_on($key, $us);
+ }
+ return 0;
+}
+
sub new {
my ($class, $attrs) = @_;
$class = ref $class if ref $class;
if (my $handle = delete $attrs->{-source_handle}) {
$new->_source_handle($handle);
}
- if (my $source = delete $attrs->{-result_source}) {
+
+ my $source;
+ if ($source = delete $attrs->{-result_source}) {
$new->result_source($source);
}
foreach my $key (keys %$attrs) {
if (ref $attrs->{$key}) {
## Can we extract this lot to use with update(_or .. ) ?
- my $info = $class->relationship_info($key);
+ confess "Can't do multi-create without result source" unless $source;
+ my $info = $source->relationship_info($key);
if ($info && $info->{attrs}{accessor}
&& $info->{attrs}{accessor} eq 'single')
{
my $rel_obj = delete $attrs->{$key};
if(!Scalar::Util::blessed($rel_obj)) {
- $rel_obj = $new->find_or_new_related($key, $rel_obj);
+ $rel_obj = $new->__new_related_find_or_new_helper($key, $rel_obj);
}
$new->{_rel_in_storage} = 0 unless ($rel_obj->in_storage);
- $new->set_from_related($key, $rel_obj);
+ $new->set_from_related($key, $rel_obj) if $rel_obj->in_storage;
$related->{$key} = $rel_obj;
next;
} elsif ($info && $info->{attrs}{accessor}
my $others = delete $attrs->{$key};
foreach my $rel_obj (@$others) {
if(!Scalar::Util::blessed($rel_obj)) {
- $rel_obj = $new->new_related($key, $rel_obj);
- $new->{_rel_in_storage} = 0;
+ $rel_obj = $new->__new_related_find_or_new_helper($key, $rel_obj);
}
$new->{_rel_in_storage} = 0 unless ($rel_obj->in_storage);
+ $new->set_from_related($key, $rel_obj) if $rel_obj->in_storage;
}
$related->{$key} = $others;
next;
## 'filter' should disappear and get merged in with 'single' above!
my $rel_obj = delete $attrs->{$key};
if(!Scalar::Util::blessed($rel_obj)) {
- $rel_obj = $new->find_or_new_related($key, $rel_obj);
- $new->{_rel_in_storage} = 0 unless ($rel_obj->in_storage);
+ $rel_obj = $new->__new_related_find_or_new_helper($key, $rel_obj);
}
+ $new->{_rel_in_storage} = 0 unless ($rel_obj->in_storage);
$inflated->{$key} = $rel_obj;
next;
} elsif ($class->has_column($key)
next REL unless (Scalar::Util::blessed($rel_obj)
&& $rel_obj->isa('DBIx::Class::Row'));
- my $cond = $source->relationship_info($relname)->{cond};
-
- next REL unless ref($cond) eq 'HASH';
-
- # map { foreign.foo => 'self.bar' } to { bar => 'foo' }
-
- my $keyhash = { map { my $x = $_; $x =~ s/.*\.//; $x; } reverse %$cond };
-
- # assume anything that references our PK probably is dependent on us
- # rather than vice versa, unless the far side is (a) defined or (b)
- # auto-increment
-
- foreach my $p (@pri) {
- if (exists $keyhash->{$p}) {
- unless (defined($rel_obj->get_column($keyhash->{$p}))
- || $rel_obj->column_info($keyhash->{$p})
- ->{is_auto_increment}) {
- next REL;
- }
- }
- }
+ next REL unless $source->pk_depends_on(
+ $relname, { $rel_obj->get_columns }
+ );
$rel_obj->insert();
$self->set_from_related($relname, $rel_obj);
$self->store_column($auto_pri[$_] => $ids[$_]) for 0 .. $#ids;
}
+ $self->{_dirty_columns} = {};
+ $self->{related_resultsets} = {};
+
if(!$self->{_rel_in_storage}) {
## Now do the has_many rels, that need $selfs ID.
foreach my $relname (keys %related_stuff) {
my $reverse = $source->reverse_relationship_info($relname);
foreach my $obj (@cands) {
$obj->set_from_related($_, $self) for keys %$reverse;
- $obj->insert() unless ($obj->in_storage || $obj->result_source->resultset->search({$obj->get_columns})->count);
+ my $them = { $obj->get_inflated_columns };
+ if ($self->__their_pk_needs_us($relname, $them)) {
+ $obj = $self->find_or_create_related($relname, $them);
+ } else {
+ $obj->insert();
+ }
}
}
}
}
$self->in_storage(1);
- $self->{_dirty_columns} = {};
- $self->{related_resultsets} = {};
undef $self->{_orig_ident};
return $self;
}
my $self = shift;
if (ref $self) {
$self->throw_exception( "Not in database" ) unless $self->in_storage;
- my $ident_cond = $self->ident_condition;
+ my $ident_cond = $self->{_orig_ident} || $self->ident_condition;
$self->throw_exception("Cannot safely delete a row in a PK-less table")
if ! keys %$ident_cond;
foreach my $column (keys %$ident_cond) {
=cut
-*insert_or_update = \&update_or_insert;
+sub insert_or_update { shift->update_or_insert(@_) }
+
sub update_or_insert {
my $self = shift;
return ($self->in_storage ? $self->update : $self->insert);
$class->mk_group_accessors('column' => $acc);
}
-=head2 get_from_storage
+=head2 get_from_storage ($attrs)
Returns a new Row which is whatever the Storage has for the currently created
Row object. You can use this to see if the storage has become inconsistent with
whatever your Row object is.
+$attrs is expected to be a hashref of attributes suitable for passing as the
+second argument to $resultset->search($cond, $attrs);
+
=cut
sub get_from_storage {
my $self = shift @_;
- my @primary_columns = map { $self->$_ } $self->primary_columns;
- return $self->result_source->resultset->search(undef, {execute_reliably=>1})->find(@primary_columns);
+ my $attrs = shift @_;
+ my $resultset = $self->result_source->resultset;
+
+ if(defined $attrs) {
+ $resultset = $resultset->search(undef, $attrs);
+ }
+
+ return $resultset->find($self->{_orig_ident} || $self->ident_condition);
}
=head2 throw_exception