1 package DBIx::Class::Row;
6 use base qw/DBIx::Class/;
7 use Carp::Clan qw/^DBIx::Class/;
9 __PACKAGE__->mk_group_accessors('simple' => qw/_source_handle/);
13 DBIx::Class::Row - Basic row methods
19 This class is responsible for defining and doing basic operations on rows
20 derived from L<DBIx::Class::ResultSource> objects.
26 my $obj = My::Class->new($attrs);
28 Creates a new row object from column => value mappings passed as a hash ref
33 my ($class, $attrs) = @_;
34 $class = ref $class if ref $class;
36 my $new = { _column_data => {} };
39 if (my $handle = delete $attrs->{-source_handle}) {
40 $new->_source_handle($handle);
42 if (my $source = delete $attrs->{-result_source}) {
43 $new->result_source($source);
47 $new->throw_exception("attrs must be a hashref")
48 unless ref($attrs) eq 'HASH';
50 my ($related,$inflated);
51 foreach my $key (keys %$attrs) {
52 if (ref $attrs->{$key}) {
53 my $info = $class->relationship_info($key);
54 if ($info && $info->{attrs}{accessor}
55 && $info->{attrs}{accessor} eq 'single')
57 $new->set_from_related($key, $attrs->{$key});
58 $related->{$key} = $attrs->{$key};
61 elsif ($class->has_column($key)
62 && exists $class->column_info($key)->{_inflate_info})
64 $inflated->{$key} = $attrs->{$key};
68 $new->throw_exception("No such column $key on $class")
69 unless $class->has_column($key);
70 $new->store_column($key => $attrs->{$key});
73 $new->{_relationship_data} = $related if $related;
74 $new->{_inflated_column} = $inflated if $inflated;
84 Inserts an object into the database if it isn't already in
85 there. Returns the object itself. Requires the object's result source to
86 be set, or the class to have a result_source_instance method. To insert
87 an entirely new object into the database, use C<create> (see
88 L<DBIx::Class::ResultSet/create>).
94 return $self if $self->in_storage;
95 my $source = $self->result_source;
96 $source ||= $self->result_source($self->result_source_instance)
97 if $self->can('result_source_instance');
98 $self->throw_exception("No result_source set on this object; can't insert")
101 $source->storage->insert($source, { $self->get_columns });
102 $self->in_storage(1);
103 $self->{_dirty_columns} = {};
104 $self->{related_resultsets} = {};
105 undef $self->{_orig_ident};
111 $obj->in_storage; # Get value
112 $obj->in_storage(1); # Set value
114 Indicated whether the object exists as a row in the database or not
119 my ($self, $val) = @_;
120 $self->{_in_storage} = $val if @_ > 1;
121 return $self->{_in_storage};
126 $obj->update \%columns?;
128 Must be run on an object that is already in the database; issues an SQL
129 UPDATE query to commit any changes to the object to the database if
132 Also takes an options hashref of C<< column_name => value> pairs >> to update
133 first. But be aware that this hashref might be edited in place, so dont rely on
134 it being the same after a call to C<update>.
139 my ($self, $upd) = @_;
140 $self->throw_exception( "Not in database" ) unless $self->in_storage;
141 my $ident_cond = $self->ident_condition;
142 $self->throw_exception("Cannot safely update a row in a PK-less table")
143 if ! keys %$ident_cond;
146 foreach my $key (keys %$upd) {
147 if (ref $upd->{$key}) {
148 my $info = $self->relationship_info($key);
149 if ($info && $info->{attrs}{accessor}
150 && $info->{attrs}{accessor} eq 'single')
152 my $rel = delete $upd->{$key};
153 $self->set_from_related($key => $rel);
154 $self->{_relationship_data}{$key} = $rel;
156 elsif ($self->has_column($key)
157 && exists $self->column_info($key)->{_inflate_info})
159 $self->set_inflated_column($key, delete $upd->{$key});
163 $self->set_columns($upd);
165 my %to_update = $self->get_dirty_columns;
166 return $self unless keys %to_update;
167 my $rows = $self->result_source->storage->update(
168 $self->result_source, \%to_update,
169 $self->{_orig_ident} || $ident_cond
172 $self->throw_exception( "Can't update ${self}: row not found" );
173 } elsif ($rows > 1) {
174 $self->throw_exception("Can't update ${self}: updated more than one row");
176 $self->{_dirty_columns} = {};
177 $self->{related_resultsets} = {};
178 undef $self->{_orig_ident};
186 Deletes the object from the database. The object is still perfectly
187 usable, but C<< ->in_storage() >> will now return 0 and the object must
188 reinserted using C<< ->insert() >> before C<< ->update() >> can be used
189 on it. If you delete an object in a class with a C<has_many>
190 relationship, all the related objects will be deleted as well. To turn
191 this behavior off, pass C<cascade_delete => 0> in the C<$attr>
192 hashref. Any database-level cascade or restrict will take precedence
193 over a DBIx-Class-based cascading delete. See also L<DBIx::Class::ResultSet/delete>.
200 $self->throw_exception( "Not in database" ) unless $self->in_storage;
201 my $ident_cond = $self->ident_condition;
202 $self->throw_exception("Cannot safely delete a row in a PK-less table")
203 if ! keys %$ident_cond;
204 foreach my $column (keys %$ident_cond) {
205 $self->throw_exception("Can't delete the object unless it has loaded the primary keys")
206 unless exists $self->{_column_data}{$column};
208 $self->result_source->storage->delete(
209 $self->result_source, $ident_cond);
210 $self->in_storage(undef);
212 $self->throw_exception("Can't do class delete without a ResultSource instance")
213 unless $self->can('result_source_instance');
214 my $attrs = @_ > 1 && ref $_[$#_] eq 'HASH' ? { %{pop(@_)} } : {};
215 my $query = ref $_[0] eq 'HASH' ? $_[0] : {@_};
216 $self->result_source_instance->resultset->search(@_)->delete;
223 my $val = $obj->get_column($col);
225 Gets a column value from a row object. Does not do any queries; the column
226 must have already been fetched from the database and stored in the object. If
227 there is an inflated value stored that has not yet been deflated, it is deflated
228 when the method is invoked.
233 my ($self, $column) = @_;
234 $self->throw_exception( "Can't fetch data as class method" ) unless ref $self;
235 return $self->{_column_data}{$column} if exists $self->{_column_data}{$column};
236 if (exists $self->{_inflated_column}{$column}) {
237 return $self->store_column($column,
238 $self->_deflated_column($column, $self->{_inflated_column}{$column}));
240 $self->throw_exception( "No such column '${column}'" ) unless $self->has_column($column);
244 =head2 has_column_loaded
246 if ( $obj->has_column_loaded($col) ) {
247 print "$col has been loaded from db";
250 Returns a true value if the column value has been loaded from the
251 database (or set locally).
255 sub has_column_loaded {
256 my ($self, $column) = @_;
257 $self->throw_exception( "Can't call has_column data as class method" ) unless ref $self;
258 return 1 if exists $self->{_inflated_column}{$column};
259 return exists $self->{_column_data}{$column};
264 my %data = $obj->get_columns;
266 Does C<get_column>, for all column values at once.
272 if (exists $self->{_inflated_column}) {
273 foreach my $col (keys %{$self->{_inflated_column}}) {
274 $self->store_column($col, $self->_deflated_column($col, $self->{_inflated_column}{$col}))
275 unless exists $self->{_column_data}{$col};
278 return %{$self->{_column_data}};
281 =head2 get_dirty_columns
283 my %data = $obj->get_dirty_columns;
285 Identical to get_columns but only returns those that have been changed.
289 sub get_dirty_columns {
291 return map { $_ => $self->{_column_data}{$_} }
292 keys %{$self->{_dirty_columns}};
295 =head2 get_inflated_columns
297 my $inflated_data = $obj->get_inflated_columns;
299 Similar to get_columns but objects are returned for inflated columns instead of their raw non-inflated values.
303 sub get_inflated_columns {
306 my $accessor = $self->column_info($_)->{'accessor'} || $_;
307 ($_ => $self->$accessor);
313 $obj->set_column($col => $val);
315 Sets a column value. If the new value is different from the old one,
316 the column is marked as dirty for when you next call $obj->update.
323 $self->{_orig_ident} ||= $self->ident_condition;
324 my $old = $self->get_column($column);
325 my $ret = $self->store_column(@_);
326 $self->{_dirty_columns}{$column} = 1
327 if (defined $old ^ defined $ret) || (defined $old && $old ne $ret);
333 my $copy = $orig->set_columns({ $col => $val, ... });
335 Sets more than one column value at once.
340 my ($self,$data) = @_;
341 foreach my $col (keys %$data) {
342 $self->set_column($col,$data->{$col});
349 my $copy = $orig->copy({ change => $to, ... });
351 Inserts a new row with the specified changes.
356 my ($self, $changes) = @_;
358 my $col_data = { %{$self->{_column_data}} };
359 foreach my $col (keys %$col_data) {
360 delete $col_data->{$col}
361 if $self->result_source->column_info($col)->{is_auto_increment};
364 my $new = { _column_data => $col_data };
365 bless $new, ref $self;
367 $new->result_source($self->result_source);
368 $new->set_columns($changes);
370 foreach my $rel ($self->result_source->relationships) {
371 my $rel_info = $self->result_source->relationship_info($rel);
372 if ($rel_info->{attrs}{cascade_copy}) {
373 my $resolved = $self->result_source->resolve_condition(
374 $rel_info->{cond}, $rel, $new);
375 foreach my $related ($self->search_related($rel)) {
376 $related->copy($resolved);
385 $obj->store_column($col => $val);
387 Sets a column value without marking it as dirty.
392 my ($self, $column, $value) = @_;
393 $self->throw_exception( "No such column '${column}'" )
394 unless exists $self->{_column_data}{$column} || $self->has_column($column);
395 $self->throw_exception( "set_column called for ${column} without value" )
397 return $self->{_column_data}{$column} = $value;
400 =head2 inflate_result
402 Class->inflate_result($result_source, \%me, \%prefetch?)
404 Called by ResultSet to inflate a result from storage
409 my ($class, $source, $me, $prefetch) = @_;
411 my ($source_handle) = $source;
413 if ($source->isa('DBIx::Class::ResultSourceHandle')) {
414 $source = $source_handle->resolve
416 $source_handle = $source->handle
420 _source_handle => $source_handle,
424 bless $new, (ref $class || $class);
427 foreach my $pre (keys %{$prefetch||{}}) {
428 my $pre_val = $prefetch->{$pre};
429 my $pre_source = $source->related_source($pre);
430 $class->throw_exception("Can't prefetch non-existent relationship ${pre}")
432 if (ref($pre_val->[0]) eq 'ARRAY') { # multi
434 foreach my $pre_rec (@$pre_val) {
435 unless ($pre_source->primary_columns == grep { exists $pre_rec->[0]{$_}
436 and defined $pre_rec->[0]{$_} } $pre_source->primary_columns) {
439 push(@pre_objects, $pre_source->result_class->inflate_result(
440 $pre_source, @{$pre_rec}));
442 $new->related_resultset($pre)->set_cache(\@pre_objects);
443 } elsif (defined $pre_val->[0]) {
445 unless ($pre_source->primary_columns == grep { exists $pre_val->[0]{$_}
446 and !defined $pre_val->[0]{$_} } $pre_source->primary_columns)
448 $fetched = $pre_source->result_class->inflate_result(
449 $pre_source, @{$pre_val});
451 my $accessor = $source->relationship_info($pre)->{attrs}{accessor};
452 $class->throw_exception("No accessor for prefetched $pre")
453 unless defined $accessor;
454 if ($accessor eq 'single') {
455 $new->{_relationship_data}{$pre} = $fetched;
456 } elsif ($accessor eq 'filter') {
457 $new->{_inflated_column}{$pre} = $fetched;
459 $class->throw_exception("Prefetch not supported with accessor '$accessor'");
466 =head2 update_or_insert
468 $obj->update_or_insert
470 Updates the object if it's already in the db, else inserts it.
472 =head2 insert_or_update
474 $obj->insert_or_update
476 Alias for L</update_or_insert>
480 *insert_or_update = \&update_or_insert;
481 sub update_or_insert {
483 return ($self->in_storage ? $self->update : $self->insert);
488 my @changed_col_names = $obj->is_changed();
489 if ($obj->is_changed()) { ... }
491 In array context returns a list of columns with uncommited changes, or
492 in scalar context returns a true value if there are uncommitted
498 return keys %{shift->{_dirty_columns} || {}};
501 =head2 is_column_changed
503 if ($obj->is_column_changed('col')) { ... }
505 Returns a true value if the column has uncommitted changes.
509 sub is_column_changed {
510 my( $self, $col ) = @_;
511 return exists $self->{_dirty_columns}->{$col};
516 my $resultsource = $object->result_source;
518 Accessor to the ResultSource this object was created from
526 $self->_source_handle($_[0]->handle);
528 $self->_source_handle->resolve;
532 =head2 register_column
534 $column_info = { .... };
535 $class->register_column($column_name, $column_info);
537 Registers a column on the class. If the column_info has an 'accessor'
538 key, creates an accessor named after the value if defined; if there is
539 no such key, creates an accessor with the same name as the column
541 The column_info attributes are described in
542 L<DBIx::Class::ResultSource/add_columns>
546 sub register_column {
547 my ($class, $col, $info) = @_;
549 if (exists $info->{accessor}) {
550 return unless defined $info->{accessor};
551 $acc = [ $info->{accessor}, $col ];
553 $class->mk_group_accessors('column' => $acc);
557 =head2 throw_exception
559 See Schema's throw_exception.
563 sub throw_exception {
565 if (ref $self && ref $self->result_source) {
566 $self->result_source->schema->throw_exception(@_);
576 Matt S. Trout <mst@shadowcatsystems.co.uk>
580 You may distribute this code under the same terms as Perl itself.