Fix updates with multi-create syntax
[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
211 $obj->update;
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
217=cut
218
219sub update {
220 my ($self, $upd) = @_;
701da8c4 221 $self->throw_exception( "Not in database" ) unless $self->in_storage;
4b12b3c2 222 my $ident_cond = $self->ident_condition;
223 $self->throw_exception("Cannot safely update a row in a PK-less table")
224 if ! keys %$ident_cond;
6e399b4f 225
61a622ee 226 if ($upd) {
227 foreach my $key (keys %$upd) {
228 if (ref $upd->{$key}) {
229 my $info = $self->relationship_info($key);
230 if ($info && $info->{attrs}{accessor}
231 && $info->{attrs}{accessor} eq 'single')
232 {
233 my $rel = delete $upd->{$key};
234 $self->set_from_related($key => $rel);
235 $self->{_relationship_data}{$key} = $rel;
af2d42c0 236 } elsif ($info && $info->{attrs}{accessor}
237 && $info->{attrs}{accessor} eq 'multi'
238 && ref $upd->{$key} eq 'ARRAY') {
239 my $others = delete $upd->{$key};
240 foreach my $rel_obj (@$others) {
241 if(!Scalar::Util::blessed($rel_obj)) {
242 $rel_obj = $self->create_related($key, $rel_obj);
243 }
244 }
245 $self->{_relationship_data}{$key} = $others;
246# $related->{$key} = $others;
247 next;
248 }
61a622ee 249 elsif ($self->has_column($key)
250 && exists $self->column_info($key)->{_inflate_info})
251 {
252 $self->set_inflated_column($key, delete $upd->{$key});
253 }
254 }
255 }
256 $self->set_columns($upd);
257 }
5a9e0e60 258 my %to_update = $self->get_dirty_columns;
259 return $self unless keys %to_update;
88cb6a1d 260 my $rows = $self->result_source->storage->update(
f4afcd5d 261 $self->result_source, \%to_update,
262 $self->{_orig_ident} || $ident_cond
263 );
7624b19f 264 if ($rows == 0) {
701da8c4 265 $self->throw_exception( "Can't update ${self}: row not found" );
7624b19f 266 } elsif ($rows > 1) {
701da8c4 267 $self->throw_exception("Can't update ${self}: updated more than one row");
7624b19f 268 }
269 $self->{_dirty_columns} = {};
64acc2bc 270 $self->{related_resultsets} = {};
729b29ae 271 undef $self->{_orig_ident};
7624b19f 272 return $self;
273}
274
8091aa91 275=head2 delete
7624b19f 276
277 $obj->delete
278
b8810cc5 279Deletes the object from the database. The object is still perfectly
61a622ee 280usable, but C<< ->in_storage() >> will now return 0 and the object must
281reinserted using C<< ->insert() >> before C<< ->update() >> can be used
b8810cc5 282on it. If you delete an object in a class with a C<has_many>
283relationship, all the related objects will be deleted as well. To turn
284this behavior off, pass C<cascade_delete => 0> in the C<$attr>
285hashref. Any database-level cascade or restrict will take precedence
286over a DBIx-Class-based cascading delete. See also L<DBIx::Class::ResultSet/delete>.
7624b19f 287
288=cut
289
290sub delete {
291 my $self = shift;
292 if (ref $self) {
701da8c4 293 $self->throw_exception( "Not in database" ) unless $self->in_storage;
4b12b3c2 294 my $ident_cond = $self->ident_condition;
295 $self->throw_exception("Cannot safely delete a row in a PK-less table")
296 if ! keys %$ident_cond;
e0f56292 297 foreach my $column (keys %$ident_cond) {
75d07914 298 $self->throw_exception("Can't delete the object unless it has loaded the primary keys")
299 unless exists $self->{_column_data}{$column};
e0f56292 300 }
88cb6a1d 301 $self->result_source->storage->delete(
7af8b477 302 $self->result_source, $ident_cond);
7624b19f 303 $self->in_storage(undef);
7624b19f 304 } else {
701da8c4 305 $self->throw_exception("Can't do class delete without a ResultSource instance")
097d3227 306 unless $self->can('result_source_instance');
aeb1bf75 307 my $attrs = @_ > 1 && ref $_[$#_] eq 'HASH' ? { %{pop(@_)} } : {};
308 my $query = ref $_[0] eq 'HASH' ? $_[0] : {@_};
097d3227 309 $self->result_source_instance->resultset->search(@_)->delete;
7624b19f 310 }
311 return $self;
312}
313
8091aa91 314=head2 get_column
7624b19f 315
316 my $val = $obj->get_column($col);
317
61a622ee 318Gets a column value from a row object. Does not do any queries; the column
319must have already been fetched from the database and stored in the object. If
320there is an inflated value stored that has not yet been deflated, it is deflated
321when the method is invoked.
7624b19f 322
323=cut
324
325sub get_column {
326 my ($self, $column) = @_;
701da8c4 327 $self->throw_exception( "Can't fetch data as class method" ) unless ref $self;
aeb1bf75 328 return $self->{_column_data}{$column} if exists $self->{_column_data}{$column};
61a622ee 329 if (exists $self->{_inflated_column}{$column}) {
330 return $self->store_column($column,
331 $self->_deflated_column($column, $self->{_inflated_column}{$column}));
332 }
701da8c4 333 $self->throw_exception( "No such column '${column}'" ) unless $self->has_column($column);
7624b19f 334 return undef;
335}
336
9b83fccd 337=head2 has_column_loaded
338
339 if ( $obj->has_column_loaded($col) ) {
340 print "$col has been loaded from db";
341 }
342
343Returns a true value if the column value has been loaded from the
c4a30d56 344database (or set locally).
9b83fccd 345
346=cut
347
def81720 348sub has_column_loaded {
349 my ($self, $column) = @_;
350 $self->throw_exception( "Can't call has_column data as class method" ) unless ref $self;
61a622ee 351 return 1 if exists $self->{_inflated_column}{$column};
aeb1bf75 352 return exists $self->{_column_data}{$column};
def81720 353}
354
8091aa91 355=head2 get_columns
076a6864 356
357 my %data = $obj->get_columns;
358
8091aa91 359Does C<get_column>, for all column values at once.
076a6864 360
361=cut
362
363sub get_columns {
364 my $self = shift;
61a622ee 365 if (exists $self->{_inflated_column}) {
366 foreach my $col (keys %{$self->{_inflated_column}}) {
367 $self->store_column($col, $self->_deflated_column($col, $self->{_inflated_column}{$col}))
c4a30d56 368 unless exists $self->{_column_data}{$col};
61a622ee 369 }
370 }
cb5f2eea 371 return %{$self->{_column_data}};
d7156e50 372}
373
374=head2 get_dirty_columns
375
376 my %data = $obj->get_dirty_columns;
377
378Identical to get_columns but only returns those that have been changed.
379
380=cut
381
382sub get_dirty_columns {
383 my $self = shift;
384 return map { $_ => $self->{_column_data}{$_} }
385 keys %{$self->{_dirty_columns}};
076a6864 386}
387
8091aa91 388=head2 set_column
7624b19f 389
390 $obj->set_column($col => $val);
391
8091aa91 392Sets a column value. If the new value is different from the old one,
393the column is marked as dirty for when you next call $obj->update.
7624b19f 394
395=cut
396
397sub set_column {
398 my $self = shift;
399 my ($column) = @_;
729b29ae 400 $self->{_orig_ident} ||= $self->ident_condition;
7624b19f 401 my $old = $self->get_column($column);
402 my $ret = $self->store_column(@_);
87772e46 403 $self->{_dirty_columns}{$column} = 1
404 if (defined $old ^ defined $ret) || (defined $old && $old ne $ret);
7624b19f 405 return $ret;
406}
407
8091aa91 408=head2 set_columns
076a6864 409
dc818523 410 my $copy = $orig->set_columns({ $col => $val, ... });
076a6864 411
8091aa91 412Sets more than one column value at once.
076a6864 413
414=cut
415
416sub set_columns {
417 my ($self,$data) = @_;
a2ca474b 418 foreach my $col (keys %$data) {
419 $self->set_column($col,$data->{$col});
076a6864 420 }
c01ab172 421 return $self;
076a6864 422}
423
8091aa91 424=head2 copy
076a6864 425
426 my $copy = $orig->copy({ change => $to, ... });
427
8091aa91 428Inserts a new row with the specified changes.
076a6864 429
430=cut
431
c01ab172 432sub copy {
433 my ($self, $changes) = @_;
333cce60 434 $changes ||= {};
fde6e28e 435 my $col_data = { %{$self->{_column_data}} };
436 foreach my $col (keys %$col_data) {
437 delete $col_data->{$col}
438 if $self->result_source->column_info($col)->{is_auto_increment};
439 }
04786a4c 440
441 my $new = { _column_data => $col_data };
442 bless $new, ref $self;
443
83419ec6 444 $new->result_source($self->result_source);
ecd1f408 445 $new->set_columns($changes);
333cce60 446 $new->insert;
447 foreach my $rel ($self->result_source->relationships) {
448 my $rel_info = $self->result_source->relationship_info($rel);
449 if ($rel_info->{attrs}{cascade_copy}) {
450 my $resolved = $self->result_source->resolve_condition(
451 $rel_info->{cond}, $rel, $new);
452 foreach my $related ($self->search_related($rel)) {
453 $related->copy($resolved);
454 }
455 }
456 }
2c4c67b6 457 return $new;
c01ab172 458}
459
8091aa91 460=head2 store_column
7624b19f 461
462 $obj->store_column($col => $val);
463
8091aa91 464Sets a column value without marking it as dirty.
7624b19f 465
466=cut
467
468sub store_column {
469 my ($self, $column, $value) = @_;
75d07914 470 $self->throw_exception( "No such column '${column}'" )
d7156e50 471 unless exists $self->{_column_data}{$column} || $self->has_column($column);
75d07914 472 $self->throw_exception( "set_column called for ${column} without value" )
7624b19f 473 if @_ < 3;
474 return $self->{_column_data}{$column} = $value;
475}
476
b52e9bf8 477=head2 inflate_result
478
c01ab172 479 Class->inflate_result($result_source, \%me, \%prefetch?)
b52e9bf8 480
481Called by ResultSet to inflate a result from storage
482
483=cut
484
485sub inflate_result {
c01ab172 486 my ($class, $source, $me, $prefetch) = @_;
aec3eff1 487
488 my ($source_handle) = $source;
489
490 if ($source->isa('DBIx::Class::ResultSourceHandle')) {
491 $source = $source_handle->resolve
492 } else {
493 $source_handle = $source->handle
494 }
495
04786a4c 496 my $new = {
aec3eff1 497 _source_handle => $source_handle,
04786a4c 498 _column_data => $me,
499 _in_storage => 1
500 };
501 bless $new, (ref $class || $class);
502
7fb16f1a 503 my $schema;
64acc2bc 504 foreach my $pre (keys %{$prefetch||{}}) {
505 my $pre_val = $prefetch->{$pre};
f9cc31dd 506 my $pre_source = $source->related_source($pre);
a86b1efe 507 $class->throw_exception("Can't prefetch non-existent relationship ${pre}")
508 unless $pre_source;
0f66a01b 509 if (ref($pre_val->[0]) eq 'ARRAY') { # multi
a86b1efe 510 my @pre_objects;
511 foreach my $pre_rec (@$pre_val) {
75d07914 512 unless ($pre_source->primary_columns == grep { exists $pre_rec->[0]{$_}
5a5bec6c 513 and defined $pre_rec->[0]{$_} } $pre_source->primary_columns) {
a86b1efe 514 next;
515 }
516 push(@pre_objects, $pre_source->result_class->inflate_result(
517 $pre_source, @{$pre_rec}));
518 }
519 $new->related_resultset($pre)->set_cache(\@pre_objects);
62e87ea8 520 } elsif (defined $pre_val->[0]) {
a86b1efe 521 my $fetched;
75d07914 522 unless ($pre_source->primary_columns == grep { exists $pre_val->[0]{$_}
a86b1efe 523 and !defined $pre_val->[0]{$_} } $pre_source->primary_columns)
524 {
525 $fetched = $pre_source->result_class->inflate_result(
75d07914 526 $pre_source, @{$pre_val});
a86b1efe 527 }
528 my $accessor = $source->relationship_info($pre)->{attrs}{accessor};
529 $class->throw_exception("No accessor for prefetched $pre")
530 unless defined $accessor;
531 if ($accessor eq 'single') {
532 $new->{_relationship_data}{$pre} = $fetched;
533 } elsif ($accessor eq 'filter') {
534 $new->{_inflated_column}{$pre} = $fetched;
535 } else {
536 $class->throw_exception("Prefetch not supported with accessor '$accessor'");
537 }
b52e9bf8 538 }
539 }
7624b19f 540 return $new;
541}
542
9b465d00 543=head2 update_or_insert
7624b19f 544
9b465d00 545 $obj->update_or_insert
7624b19f 546
8091aa91 547Updates the object if it's already in the db, else inserts it.
7624b19f 548
9b83fccd 549=head2 insert_or_update
550
551 $obj->insert_or_update
552
553Alias for L</update_or_insert>
554
7624b19f 555=cut
556
9b465d00 557*insert_or_update = \&update_or_insert;
558sub update_or_insert {
7624b19f 559 my $self = shift;
560 return ($self->in_storage ? $self->update : $self->insert);
561}
562
8091aa91 563=head2 is_changed
7624b19f 564
228dbcb4 565 my @changed_col_names = $obj->is_changed();
566 if ($obj->is_changed()) { ... }
7624b19f 567
9b83fccd 568In array context returns a list of columns with uncommited changes, or
569in scalar context returns a true value if there are uncommitted
570changes.
571
7624b19f 572=cut
573
574sub is_changed {
575 return keys %{shift->{_dirty_columns} || {}};
576}
228dbcb4 577
578=head2 is_column_changed
579
580 if ($obj->is_column_changed('col')) { ... }
581
9b83fccd 582Returns a true value if the column has uncommitted changes.
583
228dbcb4 584=cut
585
586sub is_column_changed {
587 my( $self, $col ) = @_;
588 return exists $self->{_dirty_columns}->{$col};
589}
7624b19f 590
097d3227 591=head2 result_source
592
9b83fccd 593 my $resultsource = $object->result_source;
097d3227 594
9b83fccd 595Accessor to the ResultSource this object was created from
87c4e602 596
aec3eff1 597=cut
598
599sub result_source {
600 my $self = shift;
601
602 if (@_) {
603 $self->_source_handle($_[0]->handle);
604 } else {
605 $self->_source_handle->resolve;
606 }
607}
608
9b83fccd 609=head2 register_column
27f01d1f 610
9b83fccd 611 $column_info = { .... };
612 $class->register_column($column_name, $column_info);
27f01d1f 613
9b83fccd 614Registers a column on the class. If the column_info has an 'accessor'
615key, creates an accessor named after the value if defined; if there is
616no such key, creates an accessor with the same name as the column
1f23a877 617
9b83fccd 618The column_info attributes are described in
619L<DBIx::Class::ResultSource/add_columns>
1f23a877 620
097d3227 621=cut
622
1f23a877 623sub register_column {
624 my ($class, $col, $info) = @_;
91b0fbd7 625 my $acc = $col;
626 if (exists $info->{accessor}) {
627 return unless defined $info->{accessor};
628 $acc = [ $info->{accessor}, $col ];
629 }
630 $class->mk_group_accessors('column' => $acc);
1f23a877 631}
632
701da8c4 633
5160b401 634=head2 throw_exception
701da8c4 635
636See Schema's throw_exception.
637
638=cut
639
640sub throw_exception {
641 my $self=shift;
642 if (ref $self && ref $self->result_source) {
643 $self->result_source->schema->throw_exception(@_);
644 } else {
645 croak(@_);
646 }
647}
648
7624b19f 6491;
650
7624b19f 651=head1 AUTHORS
652
daec44b8 653Matt S. Trout <mst@shadowcatsystems.co.uk>
7624b19f 654
655=head1 LICENSE
656
657You may distribute this code under the same terms as Perl itself.
658
659=cut