more has_relationship_loaded tests + fix for the failing tests
[dbsrgits/DBIx-Class.git] / lib / DBIx / Class / Row.pm
index b36fe0e..8bdd34b 100644 (file)
@@ -8,6 +8,7 @@ use base qw/DBIx::Class/;
 use Scalar::Util 'blessed';
 use List::Util 'first';
 use Try::Tiny;
+use DBIx::Class::Carp;
 
 ###
 ### Internal method
@@ -47,8 +48,8 @@ relationship accessors of L<Result|DBIx::Class::Manual::ResultClass> objects.
 =head1 NOTE
 
 All "Row objects" derived from a Schema-attached L<DBIx::Class::ResultSet>
-object (such as a typical C<< L<search|DBIx::Class::ResultSet/search
->->L<next|DBIx::Class::ResultSet/next> >> call) are actually Result
+object (such as a typical C<< L<search|DBIx::Class::ResultSet/search>->
+L<next|DBIx::Class::ResultSet/next> >> call) are actually Result
 instances, based on your application's
 L<Result class|DBIx::Class::Manual::Glossary/Result_class>.
 
@@ -65,9 +66,9 @@ objects.  Refer to L<DBIx::Class::Manual::ResultClass> for more info.
 
 =head2 new
 
-  my $row = My::Class->new(\%attrs);
+  my $result = My::Class->new(\%attrs);
 
-  my $row = $schema->resultset('MySource')->new(\%colsandvalues);
+  my $result = $schema->resultset('MySource')->new(\%colsandvalues);
 
 =over
 
@@ -117,7 +118,7 @@ with NULL as the default, and save yourself a SELECT.
 =cut
 
 ## It needs to store the new objects somewhere, and call insert on that list later when insert is called on this object. We may need an accessor for these so the user can retrieve them, if just doing ->new().
-## This only works because DBIC doesnt yet care to check whether the new_related objects have been passed all their mandatory columns
+## This only works because DBIC doesn't yet care to check whether the new_related objects have been passed all their mandatory columns
 ## When doing the later insert, we need to make sure the PKs are set.
 ## using _relationship_data in new and funky ways..
 ## check Relationship::CascadeActions and Relationship::Accessor for compat
@@ -278,18 +279,18 @@ sub new {
   # Each pair does the same thing
 
   # (un-inflated, regular column)
-  my $val = $row->get_column('first_name');
-  my $val = $row->first_name;
+  my $val = $result->get_column('first_name');
+  my $val = $result->first_name;
 
-  $row->set_column('first_name' => $val);
-  $row->first_name($val);
+  $result->set_column('first_name' => $val);
+  $result->first_name($val);
 
   # (inflated column via DBIx::Class::InflateColumn::DateTime)
-  my $val = $row->get_inflated_column('last_modified');
-  my $val = $row->last_modified;
+  my $val = $result->get_inflated_column('last_modified');
+  my $val = $result->last_modified;
 
-  $row->set_inflated_column('last_modified' => $val);
-  $row->last_modified($val);
+  $result->set_inflated_column('last_modified' => $val);
+  $result->last_modified($val);
 
 =over
 
@@ -311,7 +312,7 @@ is called on the row.
 
 =head2 insert
 
-  $row->insert;
+  $result->insert;
 
 =over
 
@@ -463,8 +464,8 @@ sub insert {
 
 =head2 in_storage
 
-  $row->in_storage; # Get value
-  $row->in_storage(1); # Set value
+  $result->in_storage; # Get value
+  $result->in_storage(1); # Set value
 
 =over
 
@@ -485,7 +486,7 @@ calling L</delete> on one, sets it to false.
 
 =head2 update
 
-  $row->update(\%columns?)
+  $result->update(\%columns?)
 
 =over
 
@@ -496,7 +497,7 @@ calling L</delete> on one, sets it to false.
 =back
 
 Throws an exception if the result object is not yet in the database,
-according to L</in_storage>.
+according to L</in_storage>. Returns the object itself.
 
 This method issues an SQL UPDATE query to commit any changes to the
 object to the database if required (see L</get_dirty_columns>).
@@ -515,9 +516,9 @@ to C<update>, 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()')->update();
+  $result->last_modified(\'NOW()')->update();
   # OR
-  $row->update({ last_modified => \'NOW()' });
+  $result->update({ last_modified => \'NOW()' });
 
 The update will pass the values verbatim into SQL. (See
 L<SQL::Abstract> docs).  The values in your Result object will NOT change
@@ -525,7 +526,7 @@ as a result of the update call, if you want the object to be updated
 with the actual values from the database, call L</discard_changes>
 after the update.
 
-  $row->update()->discard_changes();
+  $result->update()->discard_changes();
 
 To determine before calling this method, which column values have
 changed and will be updated, call L</get_dirty_columns>.
@@ -563,7 +564,7 @@ sub update {
 
 =head2 delete
 
-  $row->delete
+  $result->delete
 
 =over
 
@@ -629,7 +630,7 @@ sub delete {
 
 =head2 get_column
 
-  my $val = $row->get_column($col);
+  my $val = $result->get_column($col);
 
 =over
 
@@ -650,7 +651,7 @@ will be deflated and returned.
 
 Note that if you used the C<columns> or the C<select/as>
 L<search attributes|DBIx::Class::ResultSet/ATTRIBUTES> on the resultset from
-which C<$row> was derived, and B<did not include> C<$columnname> in the list,
+which C<$result> was derived, and B<did not include> C<$columnname> in the list,
 this method will return C<undef> even if the database contains some value.
 
 To retrieve all loaded column values as a hash, use L</get_columns>.
@@ -671,7 +672,7 @@ sub get_column {
 
 =head2 has_column_loaded
 
-  if ( $row->has_column_loaded($col) ) {
+  if ( $result->has_column_loaded($col) ) {
      print "$col has been loaded from db";
   }
 
@@ -695,9 +696,33 @@ sub has_column_loaded {
   return exists $self->{_column_data}{$column};
 }
 
+=head2 has_relationship_loaded
+
+=over 4
+
+=item Arguments: $relationship_name
+
+=item Return Value: true, if the relationship has been loaded.
+
+=back
+
+=cut
+
+sub has_relationship_loaded {
+  my ($self, $rel) = @_;
+
+  $self->throw_exception( "has_relationship_loaded needs a relationship name" )
+    unless defined $rel;
+
+  # _relationship_data gets populated from related_resultsets when the rel
+  # accessor is called
+  return exists $self->{_relationship_data}->{$rel}
+      || exists $self->{related_resultsets}->{$rel};
+}
+
 =head2 get_columns
 
-  my %data = $row->get_columns;
+  my %data = $result->get_columns;
 
 =over
 
@@ -718,8 +743,22 @@ sub get_columns {
   my $self = shift;
   if (exists $self->{_inflated_column}) {
     foreach my $col (keys %{$self->{_inflated_column}}) {
-      $self->store_column($col, $self->_deflated_column($col, $self->{_inflated_column}{$col}))
-        unless exists $self->{_column_data}{$col};
+      unless (exists $self->{_column_data}{$col}) {
+
+        # if cached related_resultset is present assume this was a prefetch
+        carp_unique(
+          "Returning primary keys of prefetched 'filter' rels as part of get_columns() is deprecated and will "
+        . 'eventually be removed entirely (set DBIC_COLUMNS_INCLUDE_FILTER_RELS to disable this warning)'
+        ) if (
+          ! $ENV{DBIC_COLUMNS_INCLUDE_FILTER_RELS}
+            and
+          defined $self->{related_resultsets}{$col}
+            and
+          defined $self->{related_resultsets}{$col}->get_cache
+        );
+
+        $self->store_column($col, $self->_deflated_column($col, $self->{_inflated_column}{$col}));
+      }
     }
   }
   return %{$self->{_column_data}};
@@ -727,7 +766,7 @@ sub get_columns {
 
 =head2 get_dirty_columns
 
-  my %data = $row->get_dirty_columns;
+  my %data = $result->get_dirty_columns;
 
 =over
 
@@ -752,7 +791,7 @@ sub get_dirty_columns {
 
 =head2 make_column_dirty
 
-  $row->make_column_dirty($col)
+  $result->make_column_dirty($col)
 
 =over
 
@@ -819,19 +858,43 @@ sub get_inflated_columns {
     grep { $self->has_column_loaded($_) } $self->columns
   ]);
 
-  my %inflated;
-  for my $col (keys %$loaded_colinfo) {
-    if (exists $loaded_colinfo->{$col}{accessor}) {
-      my $acc = $loaded_colinfo->{$col}{accessor};
-      $inflated{$col} = $self->$acc if defined $acc;
-    }
-    else {
-      $inflated{$col} = $self->$col;
+  my %cols_to_return = ( %{$self->{_column_data}}, %$loaded_colinfo );
+
+  unless ($ENV{DBIC_COLUMNS_INCLUDE_FILTER_RELS}) {
+    for (keys %$loaded_colinfo) {
+      # if cached related_resultset is present assume this was a prefetch
+      if (
+        $loaded_colinfo->{$_}{_inflate_info}
+          and
+        defined $self->{related_resultsets}{$_}
+          and
+        defined $self->{related_resultsets}{$_}->get_cache
+      ) {
+        carp_unique(
+          "Returning prefetched 'filter' rels as part of get_inflated_columns() is deprecated and will "
+        . 'eventually be removed entirely (set DBIC_COLUMNS_INCLUDE_FILTER_RELS to disable this warning)'
+        );
+        last;
+      }
     }
   }
 
-  # return all loaded columns with the inflations overlayed on top
-  return %{ { $self->get_columns, %inflated } };
+  map { $_ => (
+  (
+    ! exists $loaded_colinfo->{$_}
+      or
+    (
+      exists $loaded_colinfo->{$_}{accessor}
+        and
+      ! defined $loaded_colinfo->{$_}{accessor}
+    )
+  ) ? $self->get_column($_)
+    : $self->${ \(
+      defined $loaded_colinfo->{$_}{accessor}
+        ? $loaded_colinfo->{$_}{accessor}
+        : $_
+      )}
+  )} keys %cols_to_return;
 }
 
 sub _is_column_numeric {
@@ -856,7 +919,7 @@ sub _is_column_numeric {
 
 =head2 set_column
 
-  $row->set_column($col => $val);
+  $result->set_column($col => $val);
 
 =over
 
@@ -966,7 +1029,7 @@ sub _track_storage_value {
 
 =head2 set_columns
 
-  $row->set_columns({ $col => $val, ... });
+  $result->set_columns({ $col => $val, ... });
 
 =over
 
@@ -990,7 +1053,7 @@ sub set_columns {
 
 =head2 set_inflated_columns
 
-  $row->set_inflated_columns({ $col => $val, $relname => $obj, ... });
+  $result->set_inflated_columns({ $col => $val, $relname => $obj, ... });
 
 =over
 
@@ -1105,7 +1168,7 @@ sub copy {
     );
 
     my $copied = $relnames_copied->{ $rel_info->{source} } ||= {};
-    foreach my $related ($self->search_related($relname)) {
+    foreach my $related ($self->search_related($relname)->all) {
       my $id_str = join("\0", $related->id);
       next if $copied->{$id_str};
       $copied->{$id_str} = 1;
@@ -1118,7 +1181,7 @@ sub copy {
 
 =head2 store_column
 
-  $row->store_column($col => $val);
+  $result->store_column($col => $val);
 
 =over
 
@@ -1245,7 +1308,7 @@ sub inflate_result {
 
 =head2 update_or_insert
 
-  $row->update_or_insert
+  $result->update_or_insert
 
 =over
 
@@ -1275,8 +1338,8 @@ sub update_or_insert {
 
 =head2 is_changed
 
-  my @changed_col_names = $row->is_changed();
-  if ($row->is_changed()) { ... }
+  my @changed_col_names = $result->is_changed();
+  if ($result->is_changed()) { ... }
 
 =over
 
@@ -1298,7 +1361,7 @@ sub is_changed {
 
 =head2 is_column_changed
 
-  if ($row->is_column_changed('col')) { ... }
+  if ($result->is_column_changed('col')) { ... }
 
 =over
 
@@ -1319,7 +1382,7 @@ sub is_column_changed {
 
 =head2 result_source
 
-  my $resultsource = $row->result_source;
+  my $resultsource = $result->result_source;
 
 =over
 
@@ -1386,7 +1449,7 @@ sub register_column {
 
 =head2 get_from_storage
 
-  my $copy = $row->get_from_storage($attrs)
+  my $copy = $result->get_from_storage($attrs)
 
 =over
 
@@ -1430,7 +1493,7 @@ sub get_from_storage {
 
 =head2 discard_changes
 
-  $row->discard_changes
+  $result->discard_changes
 
 =over
 
@@ -1504,7 +1567,7 @@ sub throw_exception {
 
 =head2 id
 
-  my @pk = $row->id;
+  my @pk = $result->id;
 
 =over