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