Merge 'DBIx-Class-current' into 'bulk_create'
[dbsrgits/DBIx-Class.git] / lib / DBIx / Class / Row.pm
CommitLineData
7624b19f 1package DBIx::Class::Row;
2
3use strict;
4use warnings;
5
1edd1722 6use base qw/DBIx::Class/;
701da8c4 7use Carp::Clan qw/^DBIx::Class/;
33dd4e80 8use Scalar::Util ();
1edd1722 9
aec3eff1 10__PACKAGE__->mk_group_accessors('simple' => qw/_source_handle/);
8c49f629 11
75d07914 12=head1 NAME
7624b19f 13
14DBIx::Class::Row - Basic row methods
15
16=head1 SYNOPSIS
17
18=head1 DESCRIPTION
19
20This class is responsible for defining and doing basic operations on rows
1ea77c14 21derived from L<DBIx::Class::ResultSource> objects.
7624b19f 22
23=head1 METHODS
24
8091aa91 25=head2 new
7624b19f 26
27 my $obj = My::Class->new($attrs);
28
29Creates a new row object from column => value mappings passed as a hash ref
30
33dd4e80 31Passing an object, or an arrayref of objects as a value will call
32L<DBIx::Class::Relationship::Base/set_from_related> for you. When
33passed a hashref or an arrayref of hashrefs as the value, these will
34be turned into objects via new_related, and treated as if you had
35passed objects.
36
7624b19f 37=cut
38
33dd4e80 39## 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().
40## This only works because DBIC doesnt yet care to check whether the new_related objects have been passed all their mandatory columns
41## When doing the later insert, we need to make sure the PKs are set.
42## using _relationship_data in new and funky ways..
43## check Relationship::CascadeActions and Relationship::Accessor for compat
44## tests!
45
7624b19f 46sub new {
61354236 47 my ($class, $attrs, $source) = @_;
7624b19f 48 $class = ref $class if ref $class;
04786a4c 49
50 my $new = { _column_data => {} };
51 bless $new, $class;
52
a6a280b9 53 $new->_source_handle($source) if $source;
54
7624b19f 55 if ($attrs) {
27f01d1f 56 $new->throw_exception("attrs must be a hashref")
57 unless ref($attrs) eq 'HASH';
61a622ee 58
59 my ($related,$inflated);
60 foreach my $key (keys %$attrs) {
61 if (ref $attrs->{$key}) {
af2d42c0 62 ## Can we extract this lot to use with update(_or .. ) ?
61a622ee 63 my $info = $class->relationship_info($key);
64 if ($info && $info->{attrs}{accessor}
c4a30d56 65 && $info->{attrs}{accessor} eq 'single')
61a622ee 66 {
33dd4e80 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;
72 }
61a622ee 73 $new->set_from_related($key, $attrs->{$key});
74 $related->{$key} = $attrs->{$key};
75 next;
33dd4e80 76 } elsif ($info && $info->{attrs}{accessor}
77 && $info->{attrs}{accessor} eq 'multi'
78 && ref $attrs->{$key} eq 'ARRAY') {
ac8e89d7 79 my $others = delete $attrs->{$key};
33dd4e80 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;
85 }
86 }
ac8e89d7 87 $related->{$key} = $others;
88 next;
33dd4e80 89 } elsif ($class->has_column($key)
61a622ee 90 && exists $class->column_info($key)->{_inflate_info})
91 {
33dd4e80 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;
98 }
99 $inflated->{$key} = $rel_obj;
61a622ee 100 next;
101 }
102 }
103 $new->throw_exception("No such column $key on $class")
104 unless $class->has_column($key);
105 $new->store_column($key => $attrs->{$key});
7624b19f 106 }
f90375dd 107 if (my $source = delete $attrs->{-result_source}) {
108 $new->result_source($source);
7624b19f 109 }
f90375dd 110
61a622ee 111 $new->{_relationship_data} = $related if $related;
112 $new->{_inflated_column} = $inflated if $inflated;
7624b19f 113 }
04786a4c 114
7624b19f 115 return $new;
116}
117
8091aa91 118=head2 insert
7624b19f 119
120 $obj->insert;
121
b8810cc5 122Inserts an object into the database if it isn't already in
123there. Returns the object itself. Requires the object's result source to
124be set, or the class to have a result_source_instance method. To insert
125an entirely new object into the database, use C<create> (see
126L<DBIx::Class::ResultSet/create>).
7624b19f 127
128=cut
129
130sub insert {
131 my ($self) = @_;
132 return $self if $self->in_storage;
6aba697f 133 my $source = $self->result_source;
134 $source ||= $self->result_source($self->result_source_instance)
097d3227 135 if $self->can('result_source_instance');
aeb1bf75 136 $self->throw_exception("No result_source set on this object; can't insert")
137 unless $source;
6e399b4f 138
33dd4e80 139 # Check if we stored uninserted relobjs here in new()
140 $source->storage->txn_begin if(!$self->{_rel_in_storage});
141
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..
af2d42c0 146 ## These are the FK rels, need their IDs for the insert.
33dd4e80 147 foreach my $relname (keys %related_stuff) {
148 my $relobj = $related_stuff{$relname};
149 if(ref $relobj ne 'ARRAY') {
150 $relobj->insert() if(!$relobj->in_storage);
151 $self->set_from_related($relname, $relobj);
152 }
153 }
154
ac8e89d7 155 $source->storage->insert($source, { $self->get_columns });
156
157 ## PK::Auto
158 my ($pri, $too_many) = grep { !defined $self->get_column($_) ||
159 ref($self->get_column($_)) eq 'SCALAR'} $self->primary_columns;
160 if(defined $pri) {
161 $self->throw_exception( "More than one possible key found for auto-inc on ".ref $self )
162 if defined $too_many;
163
164 my $storage = $self->result_source->storage;
165 $self->throw_exception( "Missing primary key but Storage doesn't support last_insert_id" )
166 unless $storage->can('last_insert_id');
167 my $id = $storage->last_insert_id($self->result_source,$pri);
168 $self->throw_exception( "Can't get last insert id" ) unless $id;
169 $self->store_column($pri => $id);
170 }
33dd4e80 171
af2d42c0 172 ## Now do the has_many rels, that need $selfs ID.
33dd4e80 173 foreach my $relname (keys %related_stuff) {
174 my $relobj = $related_stuff{$relname};
175 if(ref $relobj eq 'ARRAY') {
176 foreach my $obj (@$relobj) {
177 my $info = $self->relationship_info($relname);
178 ## What about multi-col FKs ?
179 my $key = $1 if($info && (keys %{$info->{cond}})[0] =~ /^foreign\.(\w+)/);
180 $obj->set_from_related($key, $self);
181 $obj->insert() if(!$obj->in_storage);
182 }
183 }
184 }
185 $source->storage->txn_commit if(!$self->{_rel_in_storage});
186
7624b19f 187 $self->in_storage(1);
188 $self->{_dirty_columns} = {};
64acc2bc 189 $self->{related_resultsets} = {};
729b29ae 190 undef $self->{_orig_ident};
7624b19f 191 return $self;
192}
193
8091aa91 194=head2 in_storage
7624b19f 195
196 $obj->in_storage; # Get value
197 $obj->in_storage(1); # Set value
198
199Indicated whether the object exists as a row in the database or not
200
201=cut
202
203sub in_storage {
204 my ($self, $val) = @_;
205 $self->{_in_storage} = $val if @_ > 1;
206 return $self->{_in_storage};
207}
208
8091aa91 209=head2 update
7624b19f 210
d5d833d9 211 $obj->update \%columns?;
7624b19f 212
213Must be run on an object that is already in the database; issues an SQL
d3b0e369 214UPDATE query to commit any changes to the object to the database if
215required.
7624b19f 216
d5d833d9 217Also takes an options hashref of C<< column_name => value> pairs >> to update
218first. But be aware that this hashref might be edited in place, so dont rely on
219it being the same after a call to C<update>.
220
7624b19f 221=cut
222
223sub update {
224 my ($self, $upd) = @_;
701da8c4 225 $self->throw_exception( "Not in database" ) unless $self->in_storage;
4b12b3c2 226 my $ident_cond = $self->ident_condition;
227 $self->throw_exception("Cannot safely update a row in a PK-less table")
228 if ! keys %$ident_cond;
6e399b4f 229
61a622ee 230 if ($upd) {
231 foreach my $key (keys %$upd) {
232 if (ref $upd->{$key}) {
233 my $info = $self->relationship_info($key);
234 if ($info && $info->{attrs}{accessor}
235 && $info->{attrs}{accessor} eq 'single')
236 {
237 my $rel = delete $upd->{$key};
238 $self->set_from_related($key => $rel);
239 $self->{_relationship_data}{$key} = $rel;
af2d42c0 240 } elsif ($info && $info->{attrs}{accessor}
241 && $info->{attrs}{accessor} eq 'multi'
242 && ref $upd->{$key} eq 'ARRAY') {
243 my $others = delete $upd->{$key};
244 foreach my $rel_obj (@$others) {
245 if(!Scalar::Util::blessed($rel_obj)) {
246 $rel_obj = $self->create_related($key, $rel_obj);
247 }
248 }
249 $self->{_relationship_data}{$key} = $others;
250# $related->{$key} = $others;
251 next;
252 }
61a622ee 253 elsif ($self->has_column($key)
254 && exists $self->column_info($key)->{_inflate_info})
255 {
256 $self->set_inflated_column($key, delete $upd->{$key});
257 }
258 }
259 }
260 $self->set_columns($upd);
261 }
5a9e0e60 262 my %to_update = $self->get_dirty_columns;
263 return $self unless keys %to_update;
88cb6a1d 264 my $rows = $self->result_source->storage->update(
f4afcd5d 265 $self->result_source, \%to_update,
266 $self->{_orig_ident} || $ident_cond
267 );
7624b19f 268 if ($rows == 0) {
701da8c4 269 $self->throw_exception( "Can't update ${self}: row not found" );
7624b19f 270 } elsif ($rows > 1) {
701da8c4 271 $self->throw_exception("Can't update ${self}: updated more than one row");
7624b19f 272 }
273 $self->{_dirty_columns} = {};
64acc2bc 274 $self->{related_resultsets} = {};
729b29ae 275 undef $self->{_orig_ident};
7624b19f 276 return $self;
277}
278
8091aa91 279=head2 delete
7624b19f 280
281 $obj->delete
282
b8810cc5 283Deletes the object from the database. The object is still perfectly
61a622ee 284usable, but C<< ->in_storage() >> will now return 0 and the object must
285reinserted using C<< ->insert() >> before C<< ->update() >> can be used
b8810cc5 286on it. If you delete an object in a class with a C<has_many>
287relationship, all the related objects will be deleted as well. To turn
288this behavior off, pass C<cascade_delete => 0> in the C<$attr>
289hashref. Any database-level cascade or restrict will take precedence
290over a DBIx-Class-based cascading delete. See also L<DBIx::Class::ResultSet/delete>.
7624b19f 291
292=cut
293
294sub delete {
295 my $self = shift;
296 if (ref $self) {
701da8c4 297 $self->throw_exception( "Not in database" ) unless $self->in_storage;
4b12b3c2 298 my $ident_cond = $self->ident_condition;
299 $self->throw_exception("Cannot safely delete a row in a PK-less table")
300 if ! keys %$ident_cond;
e0f56292 301 foreach my $column (keys %$ident_cond) {
75d07914 302 $self->throw_exception("Can't delete the object unless it has loaded the primary keys")
303 unless exists $self->{_column_data}{$column};
e0f56292 304 }
88cb6a1d 305 $self->result_source->storage->delete(
7af8b477 306 $self->result_source, $ident_cond);
7624b19f 307 $self->in_storage(undef);
7624b19f 308 } else {
701da8c4 309 $self->throw_exception("Can't do class delete without a ResultSource instance")
097d3227 310 unless $self->can('result_source_instance');
aeb1bf75 311 my $attrs = @_ > 1 && ref $_[$#_] eq 'HASH' ? { %{pop(@_)} } : {};
312 my $query = ref $_[0] eq 'HASH' ? $_[0] : {@_};
097d3227 313 $self->result_source_instance->resultset->search(@_)->delete;
7624b19f 314 }
315 return $self;
316}
317
8091aa91 318=head2 get_column
7624b19f 319
320 my $val = $obj->get_column($col);
321
61a622ee 322Gets a column value from a row object. Does not do any queries; the column
323must have already been fetched from the database and stored in the object. If
324there is an inflated value stored that has not yet been deflated, it is deflated
325when the method is invoked.
7624b19f 326
327=cut
328
329sub get_column {
330 my ($self, $column) = @_;
701da8c4 331 $self->throw_exception( "Can't fetch data as class method" ) unless ref $self;
aeb1bf75 332 return $self->{_column_data}{$column} if exists $self->{_column_data}{$column};
61a622ee 333 if (exists $self->{_inflated_column}{$column}) {
334 return $self->store_column($column,
335 $self->_deflated_column($column, $self->{_inflated_column}{$column}));
336 }
701da8c4 337 $self->throw_exception( "No such column '${column}'" ) unless $self->has_column($column);
7624b19f 338 return undef;
339}
340
9b83fccd 341=head2 has_column_loaded
342
343 if ( $obj->has_column_loaded($col) ) {
344 print "$col has been loaded from db";
345 }
346
347Returns a true value if the column value has been loaded from the
c4a30d56 348database (or set locally).
9b83fccd 349
350=cut
351
def81720 352sub has_column_loaded {
353 my ($self, $column) = @_;
354 $self->throw_exception( "Can't call has_column data as class method" ) unless ref $self;
61a622ee 355 return 1 if exists $self->{_inflated_column}{$column};
aeb1bf75 356 return exists $self->{_column_data}{$column};
def81720 357}
358
8091aa91 359=head2 get_columns
076a6864 360
361 my %data = $obj->get_columns;
362
8091aa91 363Does C<get_column>, for all column values at once.
076a6864 364
365=cut
366
367sub get_columns {
368 my $self = shift;
61a622ee 369 if (exists $self->{_inflated_column}) {
370 foreach my $col (keys %{$self->{_inflated_column}}) {
371 $self->store_column($col, $self->_deflated_column($col, $self->{_inflated_column}{$col}))
c4a30d56 372 unless exists $self->{_column_data}{$col};
61a622ee 373 }
374 }
cb5f2eea 375 return %{$self->{_column_data}};
d7156e50 376}
377
378=head2 get_dirty_columns
379
380 my %data = $obj->get_dirty_columns;
381
382Identical to get_columns but only returns those that have been changed.
383
384=cut
385
386sub get_dirty_columns {
387 my $self = shift;
388 return map { $_ => $self->{_column_data}{$_} }
389 keys %{$self->{_dirty_columns}};
076a6864 390}
391
8091aa91 392=head2 set_column
7624b19f 393
394 $obj->set_column($col => $val);
395
8091aa91 396Sets a column value. If the new value is different from the old one,
397the column is marked as dirty for when you next call $obj->update.
7624b19f 398
399=cut
400
401sub set_column {
402 my $self = shift;
403 my ($column) = @_;
729b29ae 404 $self->{_orig_ident} ||= $self->ident_condition;
7624b19f 405 my $old = $self->get_column($column);
406 my $ret = $self->store_column(@_);
87772e46 407 $self->{_dirty_columns}{$column} = 1
408 if (defined $old ^ defined $ret) || (defined $old && $old ne $ret);
7624b19f 409 return $ret;
410}
411
8091aa91 412=head2 set_columns
076a6864 413
dc818523 414 my $copy = $orig->set_columns({ $col => $val, ... });
076a6864 415
8091aa91 416Sets more than one column value at once.
076a6864 417
418=cut
419
420sub set_columns {
421 my ($self,$data) = @_;
a2ca474b 422 foreach my $col (keys %$data) {
423 $self->set_column($col,$data->{$col});
076a6864 424 }
c01ab172 425 return $self;
076a6864 426}
427
8091aa91 428=head2 copy
076a6864 429
430 my $copy = $orig->copy({ change => $to, ... });
431
8091aa91 432Inserts a new row with the specified changes.
076a6864 433
434=cut
435
c01ab172 436sub copy {
437 my ($self, $changes) = @_;
333cce60 438 $changes ||= {};
fde6e28e 439 my $col_data = { %{$self->{_column_data}} };
440 foreach my $col (keys %$col_data) {
441 delete $col_data->{$col}
442 if $self->result_source->column_info($col)->{is_auto_increment};
443 }
04786a4c 444
445 my $new = { _column_data => $col_data };
446 bless $new, ref $self;
447
83419ec6 448 $new->result_source($self->result_source);
ecd1f408 449 $new->set_columns($changes);
333cce60 450 $new->insert;
451 foreach my $rel ($self->result_source->relationships) {
452 my $rel_info = $self->result_source->relationship_info($rel);
453 if ($rel_info->{attrs}{cascade_copy}) {
454 my $resolved = $self->result_source->resolve_condition(
455 $rel_info->{cond}, $rel, $new);
456 foreach my $related ($self->search_related($rel)) {
457 $related->copy($resolved);
458 }
459 }
460 }
2c4c67b6 461 return $new;
c01ab172 462}
463
8091aa91 464=head2 store_column
7624b19f 465
466 $obj->store_column($col => $val);
467
8091aa91 468Sets a column value without marking it as dirty.
7624b19f 469
470=cut
471
472sub store_column {
473 my ($self, $column, $value) = @_;
75d07914 474 $self->throw_exception( "No such column '${column}'" )
d7156e50 475 unless exists $self->{_column_data}{$column} || $self->has_column($column);
75d07914 476 $self->throw_exception( "set_column called for ${column} without value" )
7624b19f 477 if @_ < 3;
478 return $self->{_column_data}{$column} = $value;
479}
480
b52e9bf8 481=head2 inflate_result
482
c01ab172 483 Class->inflate_result($result_source, \%me, \%prefetch?)
b52e9bf8 484
485Called by ResultSet to inflate a result from storage
486
487=cut
488
489sub inflate_result {
c01ab172 490 my ($class, $source, $me, $prefetch) = @_;
aec3eff1 491
492 my ($source_handle) = $source;
493
494 if ($source->isa('DBIx::Class::ResultSourceHandle')) {
495 $source = $source_handle->resolve
496 } else {
497 $source_handle = $source->handle
498 }
499
04786a4c 500 my $new = {
aec3eff1 501 _source_handle => $source_handle,
04786a4c 502 _column_data => $me,
503 _in_storage => 1
504 };
505 bless $new, (ref $class || $class);
506
7fb16f1a 507 my $schema;
64acc2bc 508 foreach my $pre (keys %{$prefetch||{}}) {
509 my $pre_val = $prefetch->{$pre};
f9cc31dd 510 my $pre_source = $source->related_source($pre);
a86b1efe 511 $class->throw_exception("Can't prefetch non-existent relationship ${pre}")
512 unless $pre_source;
0f66a01b 513 if (ref($pre_val->[0]) eq 'ARRAY') { # multi
a86b1efe 514 my @pre_objects;
515 foreach my $pre_rec (@$pre_val) {
75d07914 516 unless ($pre_source->primary_columns == grep { exists $pre_rec->[0]{$_}
5a5bec6c 517 and defined $pre_rec->[0]{$_} } $pre_source->primary_columns) {
a86b1efe 518 next;
519 }
520 push(@pre_objects, $pre_source->result_class->inflate_result(
521 $pre_source, @{$pre_rec}));
522 }
523 $new->related_resultset($pre)->set_cache(\@pre_objects);
62e87ea8 524 } elsif (defined $pre_val->[0]) {
a86b1efe 525 my $fetched;
75d07914 526 unless ($pre_source->primary_columns == grep { exists $pre_val->[0]{$_}
a86b1efe 527 and !defined $pre_val->[0]{$_} } $pre_source->primary_columns)
528 {
529 $fetched = $pre_source->result_class->inflate_result(
75d07914 530 $pre_source, @{$pre_val});
a86b1efe 531 }
532 my $accessor = $source->relationship_info($pre)->{attrs}{accessor};
533 $class->throw_exception("No accessor for prefetched $pre")
534 unless defined $accessor;
535 if ($accessor eq 'single') {
536 $new->{_relationship_data}{$pre} = $fetched;
537 } elsif ($accessor eq 'filter') {
538 $new->{_inflated_column}{$pre} = $fetched;
539 } else {
540 $class->throw_exception("Prefetch not supported with accessor '$accessor'");
541 }
b52e9bf8 542 }
543 }
7624b19f 544 return $new;
545}
546
9b465d00 547=head2 update_or_insert
7624b19f 548
9b465d00 549 $obj->update_or_insert
7624b19f 550
8091aa91 551Updates the object if it's already in the db, else inserts it.
7624b19f 552
9b83fccd 553=head2 insert_or_update
554
555 $obj->insert_or_update
556
557Alias for L</update_or_insert>
558
7624b19f 559=cut
560
9b465d00 561*insert_or_update = \&update_or_insert;
562sub update_or_insert {
7624b19f 563 my $self = shift;
564 return ($self->in_storage ? $self->update : $self->insert);
565}
566
8091aa91 567=head2 is_changed
7624b19f 568
228dbcb4 569 my @changed_col_names = $obj->is_changed();
570 if ($obj->is_changed()) { ... }
7624b19f 571
9b83fccd 572In array context returns a list of columns with uncommited changes, or
573in scalar context returns a true value if there are uncommitted
574changes.
575
7624b19f 576=cut
577
578sub is_changed {
579 return keys %{shift->{_dirty_columns} || {}};
580}
228dbcb4 581
582=head2 is_column_changed
583
584 if ($obj->is_column_changed('col')) { ... }
585
9b83fccd 586Returns a true value if the column has uncommitted changes.
587
228dbcb4 588=cut
589
590sub is_column_changed {
591 my( $self, $col ) = @_;
592 return exists $self->{_dirty_columns}->{$col};
593}
7624b19f 594
097d3227 595=head2 result_source
596
9b83fccd 597 my $resultsource = $object->result_source;
097d3227 598
9b83fccd 599Accessor to the ResultSource this object was created from
87c4e602 600
aec3eff1 601=cut
602
603sub result_source {
604 my $self = shift;
605
606 if (@_) {
607 $self->_source_handle($_[0]->handle);
608 } else {
609 $self->_source_handle->resolve;
610 }
611}
612
9b83fccd 613=head2 register_column
27f01d1f 614
9b83fccd 615 $column_info = { .... };
616 $class->register_column($column_name, $column_info);
27f01d1f 617
9b83fccd 618Registers a column on the class. If the column_info has an 'accessor'
619key, creates an accessor named after the value if defined; if there is
620no such key, creates an accessor with the same name as the column
1f23a877 621
9b83fccd 622The column_info attributes are described in
623L<DBIx::Class::ResultSource/add_columns>
1f23a877 624
097d3227 625=cut
626
1f23a877 627sub register_column {
628 my ($class, $col, $info) = @_;
91b0fbd7 629 my $acc = $col;
630 if (exists $info->{accessor}) {
631 return unless defined $info->{accessor};
632 $acc = [ $info->{accessor}, $col ];
633 }
634 $class->mk_group_accessors('column' => $acc);
1f23a877 635}
636
701da8c4 637
5160b401 638=head2 throw_exception
701da8c4 639
640See Schema's throw_exception.
641
642=cut
643
644sub throw_exception {
645 my $self=shift;
646 if (ref $self && ref $self->result_source) {
647 $self->result_source->schema->throw_exception(@_);
648 } else {
649 croak(@_);
650 }
651}
652
7624b19f 6531;
654
7624b19f 655=head1 AUTHORS
656
daec44b8 657Matt S. Trout <mst@shadowcatsystems.co.uk>
7624b19f 658
659=head1 LICENSE
660
661You may distribute this code under the same terms as Perl itself.
662
663=cut