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