package DBIx::Class::Row; use strict; use warnings; use base qw/DBIx::Class/; use Scalar::Util 'blessed'; use Try::Tiny; use DBIx::Class::Carp; use SQL::Abstract::Util 'is_literal_value'; ### ### Internal method ### Do not use ### BEGIN { *MULTICREATE_DEBUG = $ENV{DBIC_MULTICREATE_DEBUG} ? sub () { 1 } : sub () { 0 }; } use namespace::clean; __PACKAGE__->mk_group_accessors ( simple => [ in_storage => '_in_storage' ] ); =head1 NAME DBIx::Class::Row - Basic row methods =head1 SYNOPSIS =head1 DESCRIPTION This class is responsible for defining and doing basic operations on rows derived from L objects. Result objects are returned from Ls using the L, L, L and L methods, as well as invocations of 'single' ( L, L or L) relationship accessors of L objects. =head1 NOTE All "Row objects" derived from a Schema-attached L object (such as a typical C<< L-> L >> call) are actually Result instances, based on your application's L. L implements most of the row-based communication with the underlying storage, but a Result class B. Usually, Result classes inherit from L, which in turn combines the methods from several classes, one of them being L. Therefore, while many of the methods available to a L-derived Result class are described in the following documentation, it does not detail all of the methods available to Result objects. Refer to L for more info. =head1 METHODS =head2 new my $result = My::Class->new(\%attrs); my $result = $schema->resultset('MySource')->new(\%colsandvalues); =over =item Arguments: \%attrs or \%colsandvalues =item Return Value: L<$result|DBIx::Class::Manual::ResultClass> =back While you can create a new result object by calling C directly on this class, you are better off calling it on a L object. When calling it directly, you will not get a complete, usable row object until you pass or set the C attribute, to a L instance that is attached to a L 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. Passing an object, or an arrayref of objects as a value will call L for you. When passed a hashref or an arrayref of hashrefs as the value, these will be turned into objects via new_related, and treated as if you had passed objects. For a more involved explanation, see L. Please note that if a value is not passed to new, no value will be sent in the SQL INSERT call, and the column will therefore assume whatever default value was specified in your database. While DBIC will retrieve the value of autoincrement columns, it will never make an explicit database trip to retrieve default values assigned by the RDBMS. You can explicitly request that all values be fetched back from the database by calling L, or you can supply an explicit C to columns with NULL as the default, and save yourself a SELECT. CAVEAT: The behavior described above will backfire if you use a foreign key column with a database-defined default. If you call the relationship accessor on an object that doesn't have a set value for the FK column, DBIC will throw an exception, as it has no way of knowing the PK of the related object (if there is one). =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 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 ## tests! sub __new_related_find_or_new_helper { my ($self, $rel_name, $values) = @_; my $rsrc = $self->result_source; # create a mock-object so all new/set_column component overrides will run: my $rel_rs = $rsrc->related_source($rel_name)->resultset; my $new_rel_obj = $rel_rs->new_result($values); my $proc_data = { $new_rel_obj->get_columns }; if ($self->__their_pk_needs_us($rel_name)) { MULTICREATE_DEBUG and print STDERR "MC $self constructing $rel_name via new_result\n"; return $new_rel_obj; } elsif ($rsrc->_pk_depends_on($rel_name, $proc_data )) { if (! keys %$proc_data) { # there is nothing to search for - blind create MULTICREATE_DEBUG and print STDERR "MC $self constructing default-insert $rel_name\n"; } else { MULTICREATE_DEBUG and print STDERR "MC $self constructing $rel_name via find_or_new\n"; # this is not *really* find or new, as we don't want to double-new the # data (thus potentially double encoding or whatever) my $exists = $rel_rs->find ($proc_data); return $exists if $exists; } return $new_rel_obj; } else { my $us = $rsrc->source_name; $self->throw_exception ( "Unable to determine relationship '$rel_name' direction from '$us', " . "possibly due to a missing reverse-relationship on '$rel_name' to '$us'." ); } } sub __their_pk_needs_us { # this should maybe be in resultsource. my ($self, $rel_name) = @_; my $rsrc = $self->result_source; my $reverse = $rsrc->reverse_relationship_info($rel_name); my $rel_source = $rsrc->related_source($rel_name); 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; my $new = bless { _column_data => {}, _in_storage => 0 }, $class; if ($attrs) { $new->throw_exception("attrs must be a hashref") unless ref($attrs) eq 'HASH'; my $rsrc = delete $attrs->{-result_source}; if ( my $h = delete $attrs->{-source_handle} ) { $rsrc ||= $h->resolve; } $new->result_source($rsrc) if $rsrc; if (my $col_from_rel = delete $attrs->{-cols_from_relations}) { @{$new->{_ignore_at_insert}={}}{@$col_from_rel} = (); } my ($related,$inflated); foreach my $key (keys %$attrs) { if (ref $attrs->{$key} and ! is_literal_value($attrs->{$key}) ) { ## Can we extract this lot to use with update(_or .. ) ? $new->throw_exception("Can't do multi-create without result source") unless $rsrc; my $info = $rsrc->relationship_info($key); my $acc_type = $info->{attrs}{accessor} || ''; if ($acc_type eq 'single') { my $rel_obj = delete $attrs->{$key}; if(!blessed $rel_obj) { $rel_obj = $new->__new_related_find_or_new_helper($key, $rel_obj); } if ($rel_obj->in_storage) { $new->{_rel_in_storage}{$key} = 1; $new->set_from_related($key, $rel_obj); } else { MULTICREATE_DEBUG and print STDERR "MC $new uninserted $key $rel_obj\n"; } $related->{$key} = $rel_obj; next; } elsif ($acc_type eq 'multi' && ref $attrs->{$key} eq 'ARRAY' ) { my $others = delete $attrs->{$key}; my $total = @$others; my @objects; foreach my $idx (0 .. $#$others) { my $rel_obj = $others->[$idx]; if(!blessed $rel_obj) { $rel_obj = $new->__new_related_find_or_new_helper($key, $rel_obj); } if ($rel_obj->in_storage) { $rel_obj->throw_exception ('A multi relationship can not be pre-existing when doing multicreate. Something went wrong'); } else { MULTICREATE_DEBUG and print STDERR "MC $new uninserted $key $rel_obj (${\($idx+1)} of $total)\n"; } push(@objects, $rel_obj); } $related->{$key} = \@objects; next; } elsif ($acc_type eq 'filter') { ## 'filter' should disappear and get merged in with 'single' above! my $rel_obj = delete $attrs->{$key}; if(!blessed $rel_obj) { $rel_obj = $new->__new_related_find_or_new_helper($key, $rel_obj); } if ($rel_obj->in_storage) { $new->{_rel_in_storage}{$key} = 1; } else { MULTICREATE_DEBUG and print STDERR "MC $new uninserted $key $rel_obj\n"; } $inflated->{$key} = $rel_obj; next; } elsif ( $rsrc->has_column($key) and $rsrc->column_info($key)->{_inflate_info} ) { $inflated->{$key} = $attrs->{$key}; next; } } $new->store_column($key => $attrs->{$key}); } $new->{_relationship_data} = $related if $related; $new->{_inflated_column} = $inflated if $inflated; } return $new; } =head2 $column_accessor # Each pair does the same thing # (un-inflated, regular column) my $val = $result->get_column('first_name'); my $val = $result->first_name; $result->set_column('first_name' => $val); $result->first_name($val); # (inflated column via DBIx::Class::InflateColumn::DateTime) my $val = $result->get_inflated_column('last_modified'); my $val = $result->last_modified; $result->set_inflated_column('last_modified' => $val); $result->last_modified($val); =over =item Arguments: $value? =item Return Value: $value =back A column accessor method is created for each column, which is used for getting/setting the value for that column. The actual method name is based on the L name given during the L L. Like L, this will not store the data in the database until L or L is called on the row. =head2 insert $result->insert; =over =item Arguments: none =item Return Value: L<$result|DBIx::Class::Manual::ResultClass> =back Inserts an object previously created by L into the database if it isn't already in there. Returns the object itself. To insert an entirely new row into the database, use L. To fetch an uninserted result object, call L on a resultset. This will also insert any uninserted, related objects held inside this one, see L for more details. =cut sub insert { my ($self) = @_; return $self if $self->in_storage; my $rsrc = $self->result_source; $self->throw_exception("No result_source set on this object; can't insert") unless $rsrc; my $storage = $rsrc->storage; my $rollback_guard; # Check if we stored uninserted relobjs here in new() my %related_stuff = (%{$self->{_relationship_data} || {}}, %{$self->{_inflated_column} || {}}); # insert what needs to be inserted before us my %pre_insert; for my $rel_name (keys %related_stuff) { my $rel_obj = $related_stuff{$rel_name}; if (! $self->{_rel_in_storage}{$rel_name}) { next unless (blessed $rel_obj && $rel_obj->isa('DBIx::Class::Row')); next unless $rsrc->_pk_depends_on( $rel_name, { $rel_obj->get_columns } ); # The guard will save us if we blow out of this scope via die $rollback_guard ||= $storage->txn_scope_guard; MULTICREATE_DEBUG and print STDERR "MC $self pre-reconstructing $rel_name $rel_obj\n"; my $them = { %{$rel_obj->{_relationship_data} || {} }, $rel_obj->get_columns }; my $existing; # if there are no keys - nothing to search for if (keys %$them and $existing = $self->result_source ->related_source($rel_name) ->resultset ->find($them) ) { %{$rel_obj} = %{$existing}; } else { $rel_obj->insert; } $self->{_rel_in_storage}{$rel_name} = 1; } $self->set_from_related($rel_name, $rel_obj); delete $related_stuff{$rel_name}; } # start a transaction here if not started yet and there is more stuff # to insert after us if (keys %related_stuff) { $rollback_guard ||= $storage->txn_scope_guard } MULTICREATE_DEBUG and do { no warnings 'uninitialized'; print STDERR "MC $self inserting (".join(', ', $self->get_columns).")\n"; }; # perform the insert - the storage will return everything it is asked to # (autoinc primary columns and any retrieve_on_insert columns) my %current_rowdata = $self->get_columns; my $returned_cols = $storage->insert( $rsrc, { %current_rowdata }, # what to insert, copy because the storage *will* change it ); for (keys %$returned_cols) { $self->store_column($_, $returned_cols->{$_}) # this ensures we fire store_column only once # (some asshats like overriding it) if ( (!exists $current_rowdata{$_}) or (defined $current_rowdata{$_} xor defined $returned_cols->{$_}) or (defined $current_rowdata{$_} and $current_rowdata{$_} ne $returned_cols->{$_}) ); } delete $self->{_column_data_in_storage}; $self->in_storage(1); $self->{_dirty_columns} = {}; $self->{related_resultsets} = {}; foreach my $rel_name (keys %related_stuff) { next unless $rsrc->has_relationship ($rel_name); my @cands = ref $related_stuff{$rel_name} eq 'ARRAY' ? @{$related_stuff{$rel_name}} : $related_stuff{$rel_name} ; if (@cands && blessed $cands[0] && $cands[0]->isa('DBIx::Class::Row') ) { my $reverse = $rsrc->reverse_relationship_info($rel_name); foreach my $obj (@cands) { $obj->set_from_related($_, $self) for keys %$reverse; if ($self->__their_pk_needs_us($rel_name)) { if (exists $self->{_ignore_at_insert}{$rel_name}) { MULTICREATE_DEBUG and print STDERR "MC $self skipping post-insert on $rel_name\n"; } else { MULTICREATE_DEBUG and print STDERR "MC $self inserting $rel_name $obj\n"; $obj->insert; } } else { MULTICREATE_DEBUG and print STDERR "MC $self post-inserting $obj\n"; $obj->insert(); } } } } delete $self->{_ignore_at_insert}; $rollback_guard->commit if $rollback_guard; return $self; } =head2 in_storage $result->in_storage; # Get value $result->in_storage(1); # Set value =over =item Arguments: none or 1|0 =item Return Value: 1|0 =back Indicates whether the object exists as a row in the database or not. This is set to true when L, L or L are invoked. Creating a result object using L, or calling L on one, sets it to false. =head2 update $result->update(\%columns?) =over =item Arguments: none or a hashref =item Return Value: L<$result|DBIx::Class::Manual::ResultClass> =back Throws an exception if the result object is not yet in the database, according to L. 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). It throws an exception if a proper WHERE clause uniquely identifying the database row can not be constructed (see L for more details). 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, which might edit it in place, so don't rely on it being the same after a call to C. If you need to preserve the hashref, it is sufficient to pass a shallow copy to C, e.g. ( { %{ $href } } ) If the values passed or any of the column values set on the object contain scalar references, e.g.: $result->last_modified(\'NOW()')->update(); # OR $result->update({ last_modified => \'NOW()' }); The update will pass the values verbatim into SQL. (See L docs). The values in your Result 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 after the update. $result->update()->discard_changes(); To determine before calling this method, which column values have changed and will be updated, call L. To check if any columns will be updated, call L. To force a column to be updated, call L before this method. =cut sub update { my ($self, $upd) = @_; $self->set_inflated_columns($upd) if $upd; my %to_update = $self->get_dirty_columns or return $self; $self->throw_exception( "Not in database" ) unless $self->in_storage; my $rows = $self->result_source->storage->update( $self->result_source, \%to_update, $self->_storage_ident_condition ); if ($rows == 0) { $self->throw_exception( "Can't update ${self}: row not found" ); } elsif ($rows > 1) { $self->throw_exception("Can't update ${self}: updated more than one row"); } $self->{_dirty_columns} = {}; $self->{related_resultsets} = {}; delete $self->{_column_data_in_storage}; return $self; } =head2 delete $result->delete =over =item Arguments: none =item Return Value: L<$result|DBIx::Class::Manual::ResultClass> =back Throws an exception if the object is not in the database according to L. Also throws an exception if a proper WHERE clause uniquely identifying the database row can not be constructed (see L for more details). The object is still perfectly usable, but L will now return 0 and the object must be reinserted using L before it can be used to L the row again. If you delete an object in a class with a C 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. Any database-level cascade or restrict will take precedence over a DBIx-Class-based cascading delete, since DBIx-Class B and only then attempts to delete any remaining related rows. If you delete an object within a txn_do() (see L) and the transaction subsequently fails, the result object will remain marked as not being in storage. If you know for a fact that the object is still in storage (i.e. by inspecting the cause of the transaction's failure), you can use C<< $obj->in_storage(1) >> to restore consistency between the object and the database. This would allow a subsequent C<< $obj->delete >> to work as expected. See also L. =cut sub delete { my $self = shift; if (ref $self) { $self->throw_exception( "Not in database" ) unless $self->in_storage; $self->result_source->storage->delete( $self->result_source, $self->_storage_ident_condition ); delete $self->{_column_data_in_storage}; $self->in_storage(0); } else { my $rsrc = try { $self->result_source_instance } or $self->throw_exception("Can't do class delete without a ResultSource instance"); my $attrs = @_ > 1 && ref $_[$#_] eq 'HASH' ? { %{pop(@_)} } : {}; my $query = ref $_[0] eq 'HASH' ? $_[0] : {@_}; $rsrc->resultset->search(@_)->delete; } return $self; } =head2 get_column my $val = $result->get_column($col); =over =item Arguments: $columnname =item Return Value: The value of the column =back Throws an exception if the column name given doesn't exist according to L. Returns a raw column value from the result object, if it has already been fetched from the database or set by an accessor. If an L has been set, it will be deflated and returned. Note that if you used the C or the C