DBIx-Class

 view release on metacpan or  search on metacpan

lib/DBIx/Class/Row.pm  view on Meta::CPAN

  $result->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

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</in_storage>. Also throws an exception if a proper WHERE clause
uniquely identifying the database row can not be constructed (see
L<significance of primary keys|DBIx::Class::Manual::Intro/The Significance and Importance of Primary Keys>
for more details).

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, since DBIx-Class B<deletes the
main row first> and only then attempts to delete any remaining related
rows.

If you delete an object within a txn_do() (see L<DBIx::Class::Storage/txn_do>)
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<DBIx::Class::ResultSet/delete>.

=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<has_column|DBIx::Class::ResultSource/has_column>.

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<inflated value|DBIx::Class::InflateColumn> has been set, it
will be deflated and returned.

lib/DBIx/Class/Row.pm  view on Meta::CPAN

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

sub set_inflated_columns {
  my ( $self, $upd ) = @_;
  my $rsrc;
  foreach my $key (keys %$upd) {
    if (ref $upd->{$key}) {
      $rsrc ||= $self->result_source;
      my $info = $rsrc->relationship_info($key);
      my $acc_type = $info->{attrs}{accessor} || '';

      if ($acc_type eq 'single') {
        my $rel_obj = delete $upd->{$key};
        $self->set_from_related($key => $rel_obj);
        $self->{_relationship_data}{$key} = $rel_obj;
      }
      elsif ($acc_type eq 'multi') {
        $self->throw_exception(
          "Recursive update is not supported over relationships of type '$acc_type' ($key)"
        );
      }
      elsif (
        $rsrc->has_column($key)
          and
        exists $rsrc->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, ... });

=over

=item Arguments: \%replacementdata

=item Return Value: L<$result|DBIx::Class::Manual::ResultClass> 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. Also any columns which have
the L<column info attribute|DBIx::Class::ResultSource/add_columns>
C<< is_auto_increment => 1 >> are explicitly removed before the copy,
so that the database can insert its own autoincremented values into
the new object.

Relationships will be followed by the copy procedure B<only> if the
relationship specifies a true value for its
L<cascade_copy|DBIx::Class::Relationship::Base> attribute. C<cascade_copy>
is set by default on C<has_many> relationships and unset on all others.

=cut

sub copy {
  my ($self, $changes) = @_;
  $changes ||= {};
  my $col_data = { $self->get_columns };

  my $rsrc = $self->result_source;

  my $colinfo = $rsrc->columns_info;
  foreach my $col (keys %$col_data) {
    delete $col_data->{$col}
      if ( ! $colinfo->{$col} or $colinfo->{$col}{is_auto_increment} );
  }

  my $new = { _column_data => $col_data };
  bless $new, ref $self;

  $new->result_source($rsrc);
  $new->set_inflated_columns($changes);
  $new->insert;

  # Its possible we'll have 2 relations to the same Source. We need to make
  # sure we don't try to insert the same row twice else we'll violate unique
  # constraints
  my $rel_names_copied = {};

  foreach my $rel_name ($rsrc->relationships) {
    my $rel_info = $rsrc->relationship_info($rel_name);

    next unless $rel_info->{attrs}{cascade_copy};

    my $resolved = $rsrc->_resolve_condition(
      $rel_info->{cond}, $rel_name, $new, $rel_name
    );

    my $copied = $rel_names_copied->{ $rel_info->{source} } ||= {};
    foreach my $related ($self->search_related($rel_name)->all) {
      $related->copy($resolved)
        unless $copied->{$related->ID}++;
    }

  }
  return $new;
}

=head2 store_column

  $result->store_column($col => $val);

=over

=item Arguments: $columnname, $value

=item Return Value: The value sent to storage

=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 result object,
extend this method to catch all data setting methods.

=cut

sub store_column {
  my ($self, $column, $value) = @_;
  $self->throw_exception( "No such column '${column}' on " . ref $self )
    unless exists $self->{_column_data}{$column} || $self->result_source->has_column($column);
  $self->throw_exception( "set_column called for ${column} without value" )
    if @_ < 3;
  return $self->{_column_data}{$column} = $value;
}

=head2 inflate_result

  Class->inflate_result($result_source, \%me, \%prefetch?)

=over

=item Arguments: L<$result_source|DBIx::Class::ResultSource>, \%columndata, \%prefetcheddata

=item Return Value: L<$result|DBIx::Class::Manual::ResultClass>

=back

All L<DBIx::Class::ResultSet> methods that retrieve data from the
database and turn it into result objects call this method.



( run in 0.873 second using v1.01-cache-2.11-cpan-d8267643d1d )