This class is responsible for defining and doing basic operations on rows
derived from L<DBIx::Class::ResultSource> objects.
+Row objects are returned from L<DBIx::Class::ResultSet>s using the
+L<create|DBIx::Class::ResultSet/create>, L<find|DBIx::Class::ResultSet/find>,
+L<next|DBIx::Class::ResultSet/next> and L<all|DBIx::Class::ResultSet/all> methods,
+as well as invocations of 'single' (
+L<belongs_to|DBIx::Class::Relationship/belongs_to>,
+L<has_one|DBIx::Class::Relationship/has_one> or
+L<might_have|DBIx::Class::Relationship/might_have>)
+relationship accessors of L<DBIx::Class::Row> objects.
+
=head1 METHODS
=head2 new
- my $obj = My::Class->new($attrs);
+ my $row = My::Class->new(\%attrs);
+
+ my $row = $schema->resultset('MySource')->new(\%colsandvalues);
+
+=over
+
+=item Arguments: \%attrs or \%colsandvalues
+
+=item Returns: A Row object
+
+=back
+
+While you can create a new row object by calling C<new> directly on
+this class, you are better off calling it on a
+L<DBIx::Class::ResultSet> object.
-Creates a new row object from column => value mappings passed as a hash ref
+When calling it directly, you will not get a complete, usable row
+object until you pass or set the C<source_handle> attribute, to a
+L<DBIx::Class::ResultSource> instance that is attached to a
+L<DBIx::Class::Schema> with a valid connection.
+
+C<$attrs> is a hashref of column name, value data. It can also contain
+some other attributes such as the C<source_handle>.
Passing an object, or an arrayref of objects as a value will call
L<DBIx::Class::Relationship::Base/set_from_related> for you. When
## 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)) {
+# print STDERR "PK needs us\n";
+# print STDERR "Data: ", Data::Dumper::Dumper($data);
+ return $self->result_source
+ ->related_source($relname)
+ ->resultset
+ ->new_result($data);
+ }
+ if ($self->result_source->pk_depends_on($relname, $data)) {
+# print STDERR "PK depends on\n";
+ return $self->result_source
+ ->related_source($relname)
+ ->resultset
+ ->find_or_create($data);
+ }
+# print STDERR "Neither, find_or_new\n";
+ 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);
+# print STDERR "Found reverse rel info: ", Data::Dumper::Dumper($reverse);
+ my $rel_source = $source->related_source($relname);
+ my $us = { $self->get_columns };
+# print STDERR "Test on self cols: ", Data::Dumper::Dumper($us);
+ 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
+ my $dep = $rel_source->pk_depends_on($key, $us);
+ if($dep) {
+# print STDERR "Assigning $self to $key\n";
+ $data->{$key} = $self;
+ return 1;
+ }
+# return 1 if $rel_source->pk_depends_on($key, $us);
+ }
+ return 0;
+}
+
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}) {
$new->_source_handle($handle);
}
- if (my $source = delete $attrs->{-result_source}) {
+
+ my $source;
+ if ($source = delete $attrs->{-result_source}) {
$new->result_source($source);
}
+# print "Source ", $source->source_name, " is $new\n";
if ($attrs) {
$new->throw_exception("attrs must be a hashref")
unless ref($attrs) eq 'HASH';
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')
{
+# print STDERR "Single $key ", Data::Dumper::Dumper($attrs);
+# print STDERR "from $class to: $info->{class}\n";
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;
+# print STDERR "Related :", join(", ", keys %$related), "\n";
next;
} elsif ($info && $info->{attrs}{accessor}
&& $info->{attrs}{accessor} eq 'multi'
&& ref $attrs->{$key} eq 'ARRAY') {
+# print STDERR "Multi $key ", Data::Dumper::Dumper($attrs);
+# print STDERR "from $class to: $info->{class}\n";
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;
+# print STDERR "Related :", join(", ", keys %$related), "\n";
next;
} elsif ($info && $info->{attrs}{accessor}
&& $info->{attrs}{accessor} eq 'filter')
## '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)
$inflated->{$key} = $attrs->{$key};
next;
}
+# print STDERR "Done :", join(", ", keys %$related), "\n";
}
$new->throw_exception("No such column $key on $class")
unless $class->has_column($key);
=head2 insert
- $obj->insert;
+ $row->insert;
+
+=over
+
+=item Arguments: none
+
+=item Returns: The Row object
+
+=back
-Inserts an object into the database if it isn't already in
-there. Returns the object itself. Requires the object's result source to
-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>).
+Inserts an object previously created by L</new> into the database if
+it isn't already in there. Returns the object itself. Requires the
+object's result source to be set, or the class to have a
+result_source_instance method. To insert an entirely new row 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.
%{$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..
my @pri = $self->primary_columns;
REL: foreach my $relname (keys %related_stuff) {
-
+# print STDERR "Looking at: $relname\n";
my $rel_obj = $related_stuff{$relname};
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;
- }
- }
- }
-
+# print STDERR "Check pk: from ", $source->source_name, " to $relname\n";
+# print STDERR "With ", Data::Dumper::Dumper({ $rel_obj->get_columns });
+ next REL unless $source->pk_depends_on(
+ $relname, { $rel_obj->get_columns }
+ );
+# print STDERR "$rel_obj\n";
+# print STDERR "in_storage: ", $rel_obj->in_storage, "\n";
+# print STDERR "Inserting $relname\n";
$rel_obj->insert();
$self->set_from_related($relname, $rel_obj);
delete $related_stuff{$relname};
}
}
- $source->storage->insert($source, { $self->get_columns });
+# print STDERR "self $self\n";
+# print STDERR "self in_storage ", $self->in_storage, "\n";
+# print STDERR "Ran out of rels, insert ", $source->source_name, "\n";
+ my $updated_cols = $source->storage->insert($source, { $self->get_columns });
+ $self->set_columns($updated_cols);
+ $self->in_storage(1);
+# print STDERR "$self\n";
## PK::Auto
my @auto_pri = grep {
$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->{_relationship_data} || {} }, $obj->get_inflated_columns };
+ my $them = { $obj->get_inflated_columns };
+# print STDERR "Does $relname need our PK?\n";
+ if ($self->__their_pk_needs_us($relname, $them)) {
+# print STDERR "Yes\n";
+ # $obj = $self->find_or_create_related($relname, $them);
+ $obj->insert();
+ } else {
+# print STDERR "No\n";
+ $obj->insert();
+ }
}
}
}
- $source->storage->txn_commit;
- $rollback_guard->dismiss;
+ $rollback_guard->commit;
}
- $self->in_storage(1);
- $self->{_dirty_columns} = {};
- $self->{related_resultsets} = {};
+# $self->in_storage(1);
undef $self->{_orig_ident};
return $self;
}
=head2 in_storage
- $obj->in_storage; # Get value
- $obj->in_storage(1); # Set value
+ $row->in_storage; # Get value
+ $row->in_storage(1); # Set value
+
+=over
+
+=item Arguments: none or 1|0
+
+=item Returns: 1|0
+
+=back
+
+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.
-Indicates whether the object exists as a row in the database or not
+Creating a row object using L<DBIx::Class::ResultSet/new>, or calling
+L</delete> on one, sets it to false.
=cut
=head2 update
- $obj->update \%columns?;
+ $row->update(\%columns?)
-Must be run on an object that is already in the database; issues an SQL
-UPDATE query to commit any changes to the object to the database if
-required.
+=over
-Also takes an options hashref of C<< column_name => value> pairs >> to update
-first. But be awawre 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 } } )
+=item Arguments: none or a hashref
+
+=item Returns: The Row object
+
+=back
+
+Throws an exception if the row object is not yet in the database,
+according to L</in_storage>.
+
+This method issues an SQL UPDATE query to commit any changes to the
+object to the database if required.
+
+Also takes an optional hashref of C<< column_name => value> >> pairs
+to update on the object first. Be aware that the hashref will be
+passed to C<set_inflated_columns>, which might edit it in place, so
+don't 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:
+
+ $row->last_modified(\'NOW()');
+ # OR
+ $row->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.
+
+ $row->update()->discard_changes();
+
+To determine before calling this method, which column values have
+changed and will be updated, call L</get_dirty_columns>.
+
+To check if any columns will be updated, call L</is_changed>.
+
+To force a column to be updated, call L</make_column_dirty> before
+this method.
=cut
=head2 delete
- $obj->delete
+ $row->delete
+
+=over
-Deletes the object from the database. The object is still perfectly
-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>
-hashref. Any database-level cascade or restrict will take precedence
-over a DBIx-Class-based cascading delete. See also L<DBIx::Class::ResultSet/delete>.
+=item Arguments: none
+
+=item Returns: The Row object
+
+=back
+
+Throws an exception if the object is not in the database according to
+L</in_storage>. Runs an SQL DELETE statement using the primary key
+values to locate the row.
+
+The object is still perfectly usable, but L</in_storage> will
+now return 0 and the object must be reinserted using L</insert>
+before it can be used to L</update> the row again.
+
+If you delete an object in a class with a C<has_many> relationship, an
+attempt is made to delete all the related objects as well. To turn
+this behaviour off, pass C<< cascade_delete => 0 >> in the C<$attr>
+hashref of the relationship, see L<DBIx::Class::Relationship>. Any
+database-level cascade or restrict will take precedence over a
+DBIx-Class-based cascading delete.
+
+See also L<DBIx::Class::ResultSet/delete>.
=cut
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) {
=head2 get_column
- my $val = $obj->get_column($col);
+ my $val = $row->get_column($col);
+
+=over
+
+=item Arguments: $columnname
+
+=item Returns: The value of the column
+
+=back
+
+Throws an exception if the column name given doesn't exist according
+to L</has_column>.
+
+Returns a raw column value from the row object, if it has already
+been fetched from the database or set by an accessor.
-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.
+If an L<inflated value|DBIx::Class::InflateColumn> has been set, it
+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,
+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>.
=cut
=head2 has_column_loaded
- if ( $obj->has_column_loaded($col) ) {
+ if ( $row->has_column_loaded($col) ) {
print "$col has been loaded from db";
}
+=over
+
+=item Arguments: $columnname
+
+=item Returns: 0|1
+
+=back
+
Returns a true value if the column value has been loaded from the
database (or set locally).
=head2 get_columns
- my %data = $obj->get_columns;
+ my %data = $row->get_columns;
+
+=over
-Does C<get_column>, for all column values at once.
+=item Arguments: none
+
+=item Returns: A hash of columnname, value pairs.
+
+=back
+
+Returns all loaded column data as a hash, containing raw values. To
+get just one value for a particular column, use L</get_column>.
=cut
=head2 get_dirty_columns
- my %data = $obj->get_dirty_columns;
+ my %data = $row->get_dirty_columns;
+
+=over
+
+=item Arguments: none
+
+=item Returns: A hash of column, value pairs
-Identical to get_columns but only returns those that have been changed.
+=back
+
+Only returns the column, value pairs for those columns that have been
+changed on this object since the last L</update> or L</insert> call.
+
+See L</get_columns> to fetch all column/value pairs.
=cut
keys %{$self->{_dirty_columns}};
}
+=head2 make_column_dirty
+
+ $row->make_column_dirty($col)
+
+=over
+
+=item Arguments: $columnname
+
+=item Returns: undefined
+
+=back
+
+Throws an exception if the column does not exist.
+
+Marks a column as having been changed regardless of whether it has
+really changed.
+
+=cut
+sub make_column_dirty {
+ my ($self, $column) = @_;
+
+ $self->throw_exception( "No such column '${column}'" )
+ unless exists $self->{_column_data}{$column} || $self->has_column($column);
+ $self->{_dirty_columns}{$column} = 1;
+}
+
=head2 get_inflated_columns
- my $inflated_data = $obj->get_inflated_columns;
+ my %inflated_data = $obj->get_inflated_columns;
+
+=over
+
+=item Arguments: none
-Similar to get_columns but objects are returned for inflated columns instead of their raw non-inflated values.
+=item Returns: A hash of column, object|value pairs
+
+=back
+
+Returns a hash of all column keys and associated values. Values for any
+columns set to use inflation will be inflated and returns as objects.
+
+See L</get_columns> to get the uninflated values.
+
+See L<DBIx::Class::InflateColumn> for how to setup inflation.
=cut
=head2 set_column
- $obj->set_column($col => $val);
+ $row->set_column($col => $val);
+
+=over
+
+=item Arguments: $columnname, $value
-Sets a 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.
+=item Returns: $value
+
+=back
+
+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 L</update>.
+
+If passed an object or reference as a value, this method will happily
+attempt to store it, and a later L</insert> or L</update> will try and
+stringify/numify as appropriate. To set an object to be deflated
+instead, see L</set_inflated_columns>.
=cut
sub set_column {
- my $self = shift;
- my ($column) = @_;
+ my ($self, $column, $new_value) = @_;
+
$self->{_orig_ident} ||= $self->ident_condition;
- my $old = $self->get_column($column);
- my $ret = $self->store_column(@_);
+ my $old_value = $self->get_column($column);
+
+ $self->store_column($column, $new_value);
$self->{_dirty_columns}{$column} = 1
- if (defined $old ^ defined $ret) || (defined $old && $old ne $ret);
- return $ret;
+ if (defined $old_value xor defined $new_value) || (defined $old_value && $old_value ne $new_value);
+
+ # XXX clear out the relation cache for this column
+ delete $self->{related_resultsets}{$column};
+
+ return $new_value;
}
=head2 set_columns
- my $copy = $orig->set_columns({ $col => $val, ... });
+ $row->set_columns({ $col => $val, ... });
+
+=over
+
+=item Arguments: \%columndata
+
+=item Returns: The Row object
+
+=back
+
+Sets multiple column, raw value pairs at once.
-Sets more than one column value at once.
+Works as L</set_column>.
=cut
=head2 set_inflated_columns
- my $copy = $orig->set_inflated_columns({ $col => $val, $rel => $obj, ... });
+ $row->set_inflated_columns({ $col => $val, $relname => $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 } } )
+=over
+
+=item Arguments: \%columndata
+
+=item Returns: The Row object
+
+=back
+
+Sets more than one column value at once. Any inflated values are
+deflated and the raw values stored.
+
+Any related values passed as Row objects, using the relation name as a
+key, are reduced to the appropriate foreign key values and stored. If
+instead of related row objects, a hashref of column, value data is
+passed, will create the related object first then store.
+
+Will even accept arrayrefs of data as a value to a
+L<DBIx::Class::Relationship/has_many> key, and create the related
+objects if necessary.
+
+Be aware that the input 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 } } )
+
+See also L<DBIx::Class::Relationship::Base/set_from_related>.
=cut
{
my $rel = delete $upd->{$key};
$self->set_from_related($key => $rel);
- $self->{_relationship_data}{$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;
+ && $info->{attrs}{accessor} eq 'multi') {
+ $self->throw_exception(
+ "Recursive update is not supported over relationships of type multi ($key)"
+ );
}
elsif ($self->has_column($key)
&& exists $self->column_info($key)->{_inflate_info})
{
- $self->set_inflated_column($key, delete $upd->{$key});
+ $self->set_inflated_column($key, delete $upd->{$key});
}
}
}
my $copy = $orig->copy({ change => $to, ... });
-Inserts a new row with the specified changes.
+=over
+
+=item Arguments: \%replacementdata
+
+=item Returns: The Row object copy
+
+=back
+
+Inserts a new row into the database, as a copy of the original
+object. If a hashref of replacement data is supplied, these will take
+precedence over data in the original.
+
+If the row has related objects in a
+L<DBIx::Class::Relationship/has_many> then those objects may be copied
+too depending on the L<cascade_copy|DBIx::Class::Relationship>
+relationship attribute.
=cut
=head2 store_column
- $obj->store_column($col => $val);
+ $row->store_column($col => $val);
+
+=over
+
+=item Arguments: $columnname, $value
+
+=item Returns: The value sent to storage
-Sets a column value without marking it as dirty.
+=back
+
+Set a raw value for a column without marking it as changed. This
+method is used internally by L</set_column> which you should probably
+be using.
+
+This is the lowest level at which data is set on a row object,
+extend this method to catch all data setting methods.
=cut
Class->inflate_result($result_source, \%me, \%prefetch?)
-Called by ResultSet to inflate a result from storage
+=over
+
+=item Arguments: $result_source, \%columndata, \%prefetcheddata
+
+=item Returns: A Row object
+
+=back
+
+All L<DBIx::Class::ResultSet> methods that retrieve data from the
+database and turn it into row objects call this method.
+
+Extend this method in your Result classes to hook into this process,
+for example to rebless the result into a different class.
+
+Reblessing can also be done more easily by setting C<result_class> in
+your Result class. See L<DBIx::Class::ResultSource/result_class>.
=cut
=head2 update_or_insert
- $obj->update_or_insert
+ $row->update_or_insert
+
+=over
+
+=item Arguments: none
-Updates the object if it's already in the db, else inserts it.
+=item Returns: Result of update or insert operation
+
+=back
+
+L</Update>s the object if it's already in the database, according to
+L</in_storage>, else L</insert>s it.
=head2 insert_or_update
=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);
=head2 is_changed
- my @changed_col_names = $obj->is_changed();
- if ($obj->is_changed()) { ... }
+ my @changed_col_names = $row->is_changed();
+ if ($row->is_changed()) { ... }
-In array context returns a list of columns with uncommited changes, or
+=over
+
+=item Arguments: none
+
+=item Returns: 0|1 or @columnnames
+
+=back
+
+In list context returns a list of columns with uncommited changes, or
in scalar context returns a true value if there are uncommitted
changes.
=head2 is_column_changed
- if ($obj->is_column_changed('col')) { ... }
+ if ($row->is_column_changed('col')) { ... }
+
+=over
+
+=item Arguments: $columname
+
+=item Returns: 0|1
+
+=back
Returns a true value if the column has uncommitted changes.
=head2 result_source
- my $resultsource = $object->result_source;
+ my $resultsource = $row->result_source;
+
+=over
+
+=item Arguments: none
+
+=item Returns: a ResultSource instance
+
+=back
-Accessor to the ResultSource this object was created from
+Accessor to the L<DBIx::Class::ResultSource> this object was created from.
=cut
$column_info = { .... };
$class->register_column($column_name, $column_info);
+=over
+
+=item Arguments: $columnname, \%columninfo
+
+=item Returns: undefined
+
+=back
+
Registers a column on the class. If the column_info has an 'accessor'
key, creates an accessor named after the value if defined; if there is
no such key, creates an accessor with the same name as the column
$class->mk_group_accessors('column' => $acc);
}
+=head2 get_from_storage
+
+ my $copy = $row->get_from_storage($attrs)
+
+=over
+
+=item Arguments: \%attrs
+
+=item Returns: A Row object
+
+=back
+
+Fetches a fresh copy of the Row object from the database and returns it.
+
+If passed the \%attrs argument, will first apply these attributes to
+the resultset used to find the row.
+
+This copy can then be used to compare to an existing row object, to
+determine if any changes have been made in the database since it was
+created.
+
+To just update your Row object with any latest changes from the
+database, use L</discard_changes> instead.
+
+The \%attrs argument should be compatible with
+L<DBIx::Class::ResultSet/ATTRIBUTES>.
+
+=cut
+
+sub get_from_storage {
+ my $self = shift @_;
+ 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
-See Schema's throw_exception.
+See L<DBIx::Class::Schema/throw_exception>.
=cut
=head2 id
+ my @pk = $row->id;
+
+=over
+
+=item Arguments: none
+
+=item Returns: A list of primary key values
+
+=back
+
Returns the primary key(s) for a row. Can't be called as a class method.
-Actually implemented in L<DBIx::Class::Pk>
+Actually implemented in L<DBIx::Class::PK>
=head2 discard_changes
-Re-selects the row from the database, losing any changes that had
-been made.
+ $row->discard_changes
+
+=over
+
+=item Arguments: none
+
+=item Returns: nothing (updates object in-place)
+
+=back
+
+Retrieves and sets the row object data from the database, losing any
+local changes 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>
+implemented in L<DBIx::Class::PK>
=cut