removed ->reload_row from storage, changed this to a method based on the actual row...
[dbsrgits/DBIx-Class.git] / lib / DBIx / Class / Row.pm
index 00bd0a2..eab11d9 100644 (file)
@@ -50,7 +50,9 @@ sub new {
   my ($class, $attrs) = @_;
   $class = ref $class if ref $class;
 
-  my $new = { _column_data => {} };
+  my $new = {
+      _column_data          => {},
+  };
   bless $new, $class;
 
   if (my $handle = delete $attrs->{-source_handle}) {
@@ -138,6 +140,9 @@ be set, or the class to have a result_source_instance method. To insert
 an entirely new object into the database, use C<create> (see
 L<DBIx::Class::ResultSet/create>).
 
+To fetch an uninserted row object, call
+L<new|DBIx::Class::ResultSet/new> on a resultset.
+
 This will also insert any uninserted, related objects held inside this
 one, see L<DBIx::Class::ResultSet/create> for more details.
 
@@ -159,11 +164,9 @@ sub insert {
                        %{$self->{_inflated_column} || {}});
 
   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 });
+    $rollback_guard = $source->storage->txn_scope_guard;
 
     ## Should all be in relationship_data, but we need to get rid of the
     ## 'filter' reltype..
@@ -206,7 +209,8 @@ sub insert {
     }
   }
 
-  $source->storage->insert($source, { $self->get_columns });
+  my $updated_cols = $source->storage->insert($source, { $self->get_columns });
+  $self->set_columns($updated_cols);
 
   ## PK::Auto
   my @auto_pri = grep {
@@ -246,8 +250,7 @@ sub insert {
         }
       }
     }
-    $source->storage->txn_commit;
-    $rollback_guard->dismiss;
+    $rollback_guard->commit;
   }
 
   $self->in_storage(1);
@@ -262,7 +265,13 @@ sub insert {
   $obj->in_storage; # Get value
   $obj->in_storage(1); # Set value
 
-Indicates whether the object exists as a row in the database or not
+Indicates whether the object exists as a row in the database or
+not. This is set to true when L<DBIx::Class::ResultSet/find>,
+L<DBIx::Class::ResultSet/create> or L<DBIx::Class::ResultSet/insert>
+are used. 
+
+Creating a row object using L<DBIx::Class::ResultSet/new>, or calling
+L</delete> on one, sets it to false.
 
 =cut
 
@@ -281,9 +290,25 @@ 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<update>. If you need to preserve the hashref,
-it is sufficient to pass a shallow copy to C<update>, e.g. ( { %{ $href } } )
+first. But be aware that the hashref will be passed to
+C<set_inflated_columns>, which might edit it in place, so dont rely on 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 } } )
+
+If the values passed or any of the column values set on the object
+contain scalar references, eg:
+
+  $obj->last_modified(\'NOW()');
+  # OR
+  $obj->update({ last_modified => \'NOW()' });
+
+The update will pass the values verbatim into SQL. (See
+L<SQL::Abstract> docs).  The values in your Row 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</discard_changes>
+after the update.
+
+  $obj->update()->discard_changes();
 
 =cut
 
@@ -294,38 +319,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(
@@ -352,7 +346,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<has_many>
 relationship, all the related objects will be deleted as well. To turn
-this behavior off, pass C<cascade_delete => 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<DBIx::Class::ResultSet/delete>.
 
@@ -386,10 +380,11 @@ sub delete {
 
   my $val = $obj->get_column($col);
 
-Gets a column value from a row object. Does not do any queries; the column 
-must have already been fetched from the database and stored in the object. If 
-there is an inflated value stored that has not yet been deflated, it is deflated
-when the method is invoked.
+Returns a raw column value from the row object, if it has already
+been fetched from the database or set by an accessor.
+
+If an L<inflated value|DBIx::Class::InflateColumn> has been set, it
+will be deflated and returned.
 
 =cut
 
@@ -427,7 +422,7 @@ sub has_column_loaded {
 
   my %data = $obj->get_columns;
 
-Does C<get_column>, for all column values at once.
+Does C<get_column>, for all loaded column values at once.
 
 =cut
 
@@ -458,9 +453,10 @@ sub get_dirty_columns {
 
 =head2 get_inflated_columns
 
-  my $inflated_data = $obj->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.
+Similar to get_columns but objects are returned for inflated columns
+instead of their raw non-inflated values.
 
 =cut
 
@@ -476,9 +472,13 @@ sub get_inflated_columns {
 
   $obj->set_column($col => $val);
 
-Sets a column value. If the new value is different from the old one,
+Sets a raw column value. If the new value is different from the old one,
 the column is marked as dirty for when you next call $obj->update.
 
+If passed an object or reference, this will happily attempt store the
+value, and a later insert/update will try and stringify/numify as
+appropriate.
+
 =cut
 
 sub set_column {
@@ -488,7 +488,11 @@ sub set_column {
   my $old = $self->get_column($column);
   my $ret = $self->store_column(@_);
   $self->{_dirty_columns}{$column} = 1
-    if (defined $old ^ defined $ret) || (defined $old && $old ne $ret);
+    if (defined $old xor defined $ret) || (defined $old && $old ne $ret);
+
+  # XXX clear out the relation cache for this column
+  delete $self->{related_resultsets}{$column};
+
   return $ret;
 }
 
@@ -508,11 +512,59 @@ 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<set_inflated_columns>. If
+you need to preserve the hashref, it is sufficient to pass a shallow copy to
+C<set_inflated_columns>, 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, ... });
 
-Inserts a new row with the specified changes.
+Inserts a new row with the specified changes. If the row has related
+objects in a C<has_many> then those objects may be copied too depending
+on the C<cascade_copy> relationship attribute.
 
 =cut
 
@@ -529,7 +581,7 @@ 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 
@@ -646,7 +698,8 @@ sub inflate_result {
 
   $obj->update_or_insert
 
-Updates the object if it's already in the db, else inserts it.
+Updates the object if it's already in the database, according to
+L</in_storage>, else inserts it.
 
 =head2 insert_or_update
 
@@ -732,6 +785,21 @@ sub register_column {
   $class->mk_group_accessors('column' => $acc);
 }
 
+=head2 get_current_storage
+
+Returns a new Row which is whatever the Storage has for the currently created
+Row object.  You ca use this to see if the storage has become inconsistent with
+whatever your Row object is.
+
+=cut
+
+sub get_current_storage {
+    my $self = shift @_;
+    my @primary_columns = map { $self->$_ } $self->primary_columns;
+    return $self->result_source->schema->txn_do(sub {
+      return $self->result_source->resultset->find(@primary_columns);  
+    });
+}
 
 =head2 throw_exception
 
@@ -748,6 +816,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<DBIx::Class::PK>
+
+=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<DBIx::Class::PK>
+
+=cut
+
 1;
 
 =head1 AUTHORS