1 package DBIx::Class::Row;
6 use base qw/DBIx::Class/;
7 use Carp::Clan qw/^DBIx::Class/;
10 __PACKAGE__->mk_group_accessors('simple' => qw/_source_handle/);
14 DBIx::Class::Row - Basic row methods
20 This class is responsible for defining and doing basic operations on rows
21 derived from L<DBIx::Class::ResultSource> objects.
27 my $obj = My::Class->new($attrs);
29 Creates a new row object from column => value mappings passed as a hash ref
31 Passing an object, or an arrayref of objects as a value will call
32 L<DBIx::Class::Relationship::Base/set_from_related> for you. When
33 passed a hashref or an arrayref of hashrefs as the value, these will
34 be turned into objects via new_related, and treated as if you had
39 ## NB (JER) - this assumes set_from_related can cope with multi-rels
40 ## 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().
41 ## This only works because DBIC doesnt yet care to check whether the new_related objects have been passed all their mandatory columns
42 ## When doing the later insert, we need to make sure the PKs are set.
43 ## using _relationship_data in new and funky ways..
44 ## check Relationship::CascadeActions and Relationship::Accessor for compat
48 my ($class, $attrs, $source) = @_;
49 $class = ref $class if ref $class;
51 my $new = { _column_data => {} };
54 $new->_source_handle($source) if $source;
57 $new->throw_exception("attrs must be a hashref")
58 unless ref($attrs) eq 'HASH';
60 my ($related,$inflated);
61 foreach my $key (keys %$attrs) {
62 if (ref $attrs->{$key}) {
63 my $info = $class->relationship_info($key);
64 if ($info && $info->{attrs}{accessor}
65 && $info->{attrs}{accessor} eq 'single')
67 my $rel_obj = $attrs->{$key};
68 $new->{_rel_in_storage} = 1;
69 if(!Scalar::Util::blessed($rel_obj)) {
70 $rel_obj = $new->new_related($key, $rel_obj);
71 $new->{_rel_in_storage} = 0;
73 $new->set_from_related($key, $attrs->{$key});
74 $related->{$key} = $attrs->{$key};
76 } elsif ($info && $info->{attrs}{accessor}
77 && $info->{attrs}{accessor} eq 'multi'
78 && ref $attrs->{$key} eq 'ARRAY') {
79 my $others = $attrs->{$key};
80 $new->{_rel_in_storage} = 1;
81 foreach my $rel_obj (@$others) {
82 if(!Scalar::Util::blessed($rel_obj)) {
83 $rel_obj = $new->new_related($key, $rel_obj);
84 $new->{_rel_in_storage} = 0;
87 $new->set_from_related($key, $others);
88 $related->{$key} = $attrs->{$key};
89 } elsif ($class->has_column($key)
90 && exists $class->column_info($key)->{_inflate_info})
92 ## 'filter' should disappear and get merged in with 'single' above!
93 my $rel_obj = $attrs->{$key};
94 $new->{_rel_in_storage} = 1;
95 if(!Scalar::Util::blessed($rel_obj)) {
96 $rel_obj = $new->new_related($key, $rel_obj);
97 $new->{_rel_in_storage} = 0;
99 $inflated->{$key} = $rel_obj;
103 $new->throw_exception("No such column $key on $class")
104 unless $class->has_column($key);
105 $new->store_column($key => $attrs->{$key});
107 if (my $source = delete $attrs->{-result_source}) {
108 $new->result_source($source);
111 $new->{_relationship_data} = $related if $related;
112 $new->{_inflated_column} = $inflated if $inflated;
122 Inserts an object into the database if it isn't already in
123 there. Returns the object itself. Requires the object's result source to
124 be set, or the class to have a result_source_instance method. To insert
125 an entirely new object into the database, use C<create> (see
126 L<DBIx::Class::ResultSet/create>).
132 return $self if $self->in_storage;
133 my $source = $self->result_source;
134 $source ||= $self->result_source($self->result_source_instance)
135 if $self->can('result_source_instance');
136 $self->throw_exception("No result_source set on this object; can't insert")
139 # Check if we stored uninserted relobjs here in new()
140 $source->storage->txn_begin if(!$self->{_rel_in_storage});
142 my %related_stuff = (%{$self->{_relationship_data} || {}},
143 %{$self->{_inflated_column} || {}});
144 ## Should all be in relationship_data, but we need to get rid of the
145 ## 'filter' reltype..
146 foreach my $relname (keys %related_stuff) {
147 my $relobj = $related_stuff{$relname};
148 if(ref $relobj ne 'ARRAY') {
149 $relobj->insert() if(!$relobj->in_storage);
150 $self->set_from_related($relname, $relobj);
154 $source->storage->insert($source->from, { $self->get_columns });
156 foreach my $relname (keys %related_stuff) {
157 my $relobj = $related_stuff{$relname};
158 if(ref $relobj eq 'ARRAY') {
159 foreach my $obj (@$relobj) {
160 my $info = $self->relationship_info($relname);
161 ## What about multi-col FKs ?
162 my $key = $1 if($info && (keys %{$info->{cond}})[0] =~ /^foreign\.(\w+)/);
163 $obj->set_from_related($key, $self);
164 $obj->insert() if(!$obj->in_storage);
168 $source->storage->txn_commit if(!$self->{_rel_in_storage});
170 $self->in_storage(1);
171 $self->{_dirty_columns} = {};
172 $self->{related_resultsets} = {};
173 undef $self->{_orig_ident};
179 $obj->in_storage; # Get value
180 $obj->in_storage(1); # Set value
182 Indicated whether the object exists as a row in the database or not
187 my ($self, $val) = @_;
188 $self->{_in_storage} = $val if @_ > 1;
189 return $self->{_in_storage};
196 Must be run on an object that is already in the database; issues an SQL
197 UPDATE query to commit any changes to the object to the database if
203 my ($self, $upd) = @_;
204 $self->throw_exception( "Not in database" ) unless $self->in_storage;
205 my $ident_cond = $self->ident_condition;
206 $self->throw_exception("Cannot safely update a row in a PK-less table")
207 if ! keys %$ident_cond;
210 foreach my $key (keys %$upd) {
211 if (ref $upd->{$key}) {
212 my $info = $self->relationship_info($key);
213 if ($info && $info->{attrs}{accessor}
214 && $info->{attrs}{accessor} eq 'single')
216 my $rel = delete $upd->{$key};
217 $self->set_from_related($key => $rel);
218 $self->{_relationship_data}{$key} = $rel;
220 elsif ($self->has_column($key)
221 && exists $self->column_info($key)->{_inflate_info})
223 $self->set_inflated_column($key, delete $upd->{$key});
227 $self->set_columns($upd);
229 my %to_update = $self->get_dirty_columns;
230 return $self unless keys %to_update;
231 my $rows = $self->result_source->storage->update(
232 $self->result_source, \%to_update,
233 $self->{_orig_ident} || $ident_cond
236 $self->throw_exception( "Can't update ${self}: row not found" );
237 } elsif ($rows > 1) {
238 $self->throw_exception("Can't update ${self}: updated more than one row");
240 $self->{_dirty_columns} = {};
241 $self->{related_resultsets} = {};
242 undef $self->{_orig_ident};
250 Deletes the object from the database. The object is still perfectly
251 usable, but C<< ->in_storage() >> will now return 0 and the object must
252 reinserted using C<< ->insert() >> before C<< ->update() >> can be used
253 on it. If you delete an object in a class with a C<has_many>
254 relationship, all the related objects will be deleted as well. To turn
255 this behavior off, pass C<cascade_delete => 0> in the C<$attr>
256 hashref. Any database-level cascade or restrict will take precedence
257 over a DBIx-Class-based cascading delete. See also L<DBIx::Class::ResultSet/delete>.
264 $self->throw_exception( "Not in database" ) unless $self->in_storage;
265 my $ident_cond = $self->ident_condition;
266 $self->throw_exception("Cannot safely delete a row in a PK-less table")
267 if ! keys %$ident_cond;
268 foreach my $column (keys %$ident_cond) {
269 $self->throw_exception("Can't delete the object unless it has loaded the primary keys")
270 unless exists $self->{_column_data}{$column};
272 $self->result_source->storage->delete(
273 $self->result_source, $ident_cond);
274 $self->in_storage(undef);
276 $self->throw_exception("Can't do class delete without a ResultSource instance")
277 unless $self->can('result_source_instance');
278 my $attrs = @_ > 1 && ref $_[$#_] eq 'HASH' ? { %{pop(@_)} } : {};
279 my $query = ref $_[0] eq 'HASH' ? $_[0] : {@_};
280 $self->result_source_instance->resultset->search(@_)->delete;
287 my $val = $obj->get_column($col);
289 Gets a column value from a row object. Does not do any queries; the column
290 must have already been fetched from the database and stored in the object. If
291 there is an inflated value stored that has not yet been deflated, it is deflated
292 when the method is invoked.
297 my ($self, $column) = @_;
298 $self->throw_exception( "Can't fetch data as class method" ) unless ref $self;
299 return $self->{_column_data}{$column} if exists $self->{_column_data}{$column};
300 if (exists $self->{_inflated_column}{$column}) {
301 return $self->store_column($column,
302 $self->_deflated_column($column, $self->{_inflated_column}{$column}));
304 $self->throw_exception( "No such column '${column}'" ) unless $self->has_column($column);
308 =head2 has_column_loaded
310 if ( $obj->has_column_loaded($col) ) {
311 print "$col has been loaded from db";
314 Returns a true value if the column value has been loaded from the
315 database (or set locally).
319 sub has_column_loaded {
320 my ($self, $column) = @_;
321 $self->throw_exception( "Can't call has_column data as class method" ) unless ref $self;
322 return 1 if exists $self->{_inflated_column}{$column};
323 return exists $self->{_column_data}{$column};
328 my %data = $obj->get_columns;
330 Does C<get_column>, for all column values at once.
336 if (exists $self->{_inflated_column}) {
337 foreach my $col (keys %{$self->{_inflated_column}}) {
338 $self->store_column($col, $self->_deflated_column($col, $self->{_inflated_column}{$col}))
339 unless exists $self->{_column_data}{$col};
342 return %{$self->{_column_data}};
345 =head2 get_dirty_columns
347 my %data = $obj->get_dirty_columns;
349 Identical to get_columns but only returns those that have been changed.
353 sub get_dirty_columns {
355 return map { $_ => $self->{_column_data}{$_} }
356 keys %{$self->{_dirty_columns}};
361 $obj->set_column($col => $val);
363 Sets a column value. If the new value is different from the old one,
364 the column is marked as dirty for when you next call $obj->update.
371 $self->{_orig_ident} ||= $self->ident_condition;
372 my $old = $self->get_column($column);
373 my $ret = $self->store_column(@_);
374 $self->{_dirty_columns}{$column} = 1
375 if (defined $old ^ defined $ret) || (defined $old && $old ne $ret);
381 my $copy = $orig->set_columns({ $col => $val, ... });
383 Sets more than one column value at once.
388 my ($self,$data) = @_;
389 foreach my $col (keys %$data) {
390 $self->set_column($col,$data->{$col});
397 my $copy = $orig->copy({ change => $to, ... });
399 Inserts a new row with the specified changes.
404 my ($self, $changes) = @_;
406 my $col_data = { %{$self->{_column_data}} };
407 foreach my $col (keys %$col_data) {
408 delete $col_data->{$col}
409 if $self->result_source->column_info($col)->{is_auto_increment};
412 my $new = { _column_data => $col_data };
413 bless $new, ref $self;
415 $new->result_source($self->result_source);
416 $new->set_columns($changes);
418 foreach my $rel ($self->result_source->relationships) {
419 my $rel_info = $self->result_source->relationship_info($rel);
420 if ($rel_info->{attrs}{cascade_copy}) {
421 my $resolved = $self->result_source->resolve_condition(
422 $rel_info->{cond}, $rel, $new);
423 foreach my $related ($self->search_related($rel)) {
424 $related->copy($resolved);
433 $obj->store_column($col => $val);
435 Sets a column value without marking it as dirty.
440 my ($self, $column, $value) = @_;
441 $self->throw_exception( "No such column '${column}'" )
442 unless exists $self->{_column_data}{$column} || $self->has_column($column);
443 $self->throw_exception( "set_column called for ${column} without value" )
445 return $self->{_column_data}{$column} = $value;
448 =head2 inflate_result
450 Class->inflate_result($result_source, \%me, \%prefetch?)
452 Called by ResultSet to inflate a result from storage
457 my ($class, $source, $me, $prefetch) = @_;
459 my ($source_handle) = $source;
461 if ($source->isa('DBIx::Class::ResultSourceHandle')) {
462 $source = $source_handle->resolve
464 $source_handle = $source->handle
468 _source_handle => $source_handle,
472 bless $new, (ref $class || $class);
475 foreach my $pre (keys %{$prefetch||{}}) {
476 my $pre_val = $prefetch->{$pre};
477 my $pre_source = $source->related_source($pre);
478 $class->throw_exception("Can't prefetch non-existent relationship ${pre}")
480 if (ref($pre_val->[0]) eq 'ARRAY') { # multi
482 foreach my $pre_rec (@$pre_val) {
483 unless ($pre_source->primary_columns == grep { exists $pre_rec->[0]{$_}
484 and defined $pre_rec->[0]{$_} } $pre_source->primary_columns) {
487 push(@pre_objects, $pre_source->result_class->inflate_result(
488 $pre_source, @{$pre_rec}));
490 $new->related_resultset($pre)->set_cache(\@pre_objects);
491 } elsif (defined $pre_val->[0]) {
493 unless ($pre_source->primary_columns == grep { exists $pre_val->[0]{$_}
494 and !defined $pre_val->[0]{$_} } $pre_source->primary_columns)
496 $fetched = $pre_source->result_class->inflate_result(
497 $pre_source, @{$pre_val});
499 my $accessor = $source->relationship_info($pre)->{attrs}{accessor};
500 $class->throw_exception("No accessor for prefetched $pre")
501 unless defined $accessor;
502 if ($accessor eq 'single') {
503 $new->{_relationship_data}{$pre} = $fetched;
504 } elsif ($accessor eq 'filter') {
505 $new->{_inflated_column}{$pre} = $fetched;
507 $class->throw_exception("Prefetch not supported with accessor '$accessor'");
514 =head2 update_or_insert
516 $obj->update_or_insert
518 Updates the object if it's already in the db, else inserts it.
520 =head2 insert_or_update
522 $obj->insert_or_update
524 Alias for L</update_or_insert>
528 *insert_or_update = \&update_or_insert;
529 sub update_or_insert {
531 return ($self->in_storage ? $self->update : $self->insert);
536 my @changed_col_names = $obj->is_changed();
537 if ($obj->is_changed()) { ... }
539 In array context returns a list of columns with uncommited changes, or
540 in scalar context returns a true value if there are uncommitted
546 return keys %{shift->{_dirty_columns} || {}};
549 =head2 is_column_changed
551 if ($obj->is_column_changed('col')) { ... }
553 Returns a true value if the column has uncommitted changes.
557 sub is_column_changed {
558 my( $self, $col ) = @_;
559 return exists $self->{_dirty_columns}->{$col};
564 my $resultsource = $object->result_source;
566 Accessor to the ResultSource this object was created from
574 $self->_source_handle($_[0]->handle);
576 $self->_source_handle->resolve;
580 =head2 register_column
582 $column_info = { .... };
583 $class->register_column($column_name, $column_info);
585 Registers a column on the class. If the column_info has an 'accessor'
586 key, creates an accessor named after the value if defined; if there is
587 no such key, creates an accessor with the same name as the column
589 The column_info attributes are described in
590 L<DBIx::Class::ResultSource/add_columns>
594 sub register_column {
595 my ($class, $col, $info) = @_;
597 if (exists $info->{accessor}) {
598 return unless defined $info->{accessor};
599 $acc = [ $info->{accessor}, $col ];
601 $class->mk_group_accessors('column' => $acc);
605 =head2 throw_exception
607 See Schema's throw_exception.
611 sub throw_exception {
613 if (ref $self && ref $self->result_source) {
614 $self->result_source->schema->throw_exception(@_);
624 Matt S. Trout <mst@shadowcatsystems.co.uk>
628 You may distribute this code under the same terms as Perl itself.