Merge 'trunk' into 'cdbicompat_integration'
[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 ();
9c6d6d93 9use Scope::Guard;
1edd1722 10
aec3eff1 11__PACKAGE__->mk_group_accessors('simple' => qw/_source_handle/);
8c49f629 12
75d07914 13=head1 NAME
7624b19f 14
15DBIx::Class::Row - Basic row methods
16
17=head1 SYNOPSIS
18
19=head1 DESCRIPTION
20
21This class is responsible for defining and doing basic operations on rows
1ea77c14 22derived from L<DBIx::Class::ResultSource> objects.
7624b19f 23
24=head1 METHODS
25
8091aa91 26=head2 new
7624b19f 27
28 my $obj = My::Class->new($attrs);
29
30Creates a new row object from column => value mappings passed as a hash ref
31
33dd4e80 32Passing an object, or an arrayref of objects as a value will call
33L<DBIx::Class::Relationship::Base/set_from_related> for you. When
34passed a hashref or an arrayref of hashrefs as the value, these will
35be turned into objects via new_related, and treated as if you had
36passed objects.
37
264f1571 38For a more involved explanation, see L<DBIx::Class::ResultSet/create>.
39
7624b19f 40=cut
41
33dd4e80 42## 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().
43## This only works because DBIC doesnt yet care to check whether the new_related objects have been passed all their mandatory columns
44## When doing the later insert, we need to make sure the PKs are set.
45## using _relationship_data in new and funky ways..
46## check Relationship::CascadeActions and Relationship::Accessor for compat
47## tests!
48
7624b19f 49sub new {
448f820f 50 my ($class, $attrs) = @_;
7624b19f 51 $class = ref $class if ref $class;
04786a4c 52
e60dc79f 53 my $new = {
54 _column_data => {},
55 };
04786a4c 56 bless $new, $class;
57
448f820f 58 if (my $handle = delete $attrs->{-source_handle}) {
59 $new->_source_handle($handle);
60 }
e9fe476b 61 if (my $source = delete $attrs->{-result_source}) {
62 $new->result_source($source);
63 }
a6a280b9 64
7624b19f 65 if ($attrs) {
27f01d1f 66 $new->throw_exception("attrs must be a hashref")
67 unless ref($attrs) eq 'HASH';
61a622ee 68
69 my ($related,$inflated);
de7c7c53 70 ## Pretend all the rels are actual objects, unset below if not, for insert() to fix
71 $new->{_rel_in_storage} = 1;
8222f722 72
61a622ee 73 foreach my $key (keys %$attrs) {
74 if (ref $attrs->{$key}) {
af2d42c0 75 ## Can we extract this lot to use with update(_or .. ) ?
61a622ee 76 my $info = $class->relationship_info($key);
77 if ($info && $info->{attrs}{accessor}
c4a30d56 78 && $info->{attrs}{accessor} eq 'single')
61a622ee 79 {
de7c7c53 80 my $rel_obj = delete $attrs->{$key};
33dd4e80 81 if(!Scalar::Util::blessed($rel_obj)) {
2ec8e594 82 $rel_obj = $new->find_or_new_related($key, $rel_obj);
33dd4e80 83 }
2bc3c81e 84
85 $new->{_rel_in_storage} = 0 unless ($rel_obj->in_storage);
86
de7c7c53 87 $new->set_from_related($key, $rel_obj);
88 $related->{$key} = $rel_obj;
61a622ee 89 next;
33dd4e80 90 } elsif ($info && $info->{attrs}{accessor}
91 && $info->{attrs}{accessor} eq 'multi'
92 && ref $attrs->{$key} eq 'ARRAY') {
2ec8e594 93 my $others = delete $attrs->{$key};
94 foreach my $rel_obj (@$others) {
95 if(!Scalar::Util::blessed($rel_obj)) {
96 $rel_obj = $new->new_related($key, $rel_obj);
97 $new->{_rel_in_storage} = 0;
33dd4e80 98 }
2bc3c81e 99
100 $new->{_rel_in_storage} = 0 unless ($rel_obj->in_storage);
2ec8e594 101 }
102 $related->{$key} = $others;
103 next;
104 } elsif ($info && $info->{attrs}{accessor}
105 && $info->{attrs}{accessor} eq 'filter')
61a622ee 106 {
33dd4e80 107 ## 'filter' should disappear and get merged in with 'single' above!
2ec8e594 108 my $rel_obj = delete $attrs->{$key};
33dd4e80 109 if(!Scalar::Util::blessed($rel_obj)) {
df78aeb1 110 $rel_obj = $new->find_or_new_related($key, $rel_obj);
111 $new->{_rel_in_storage} = 0 unless ($rel_obj->in_storage);
33dd4e80 112 }
113 $inflated->{$key} = $rel_obj;
61a622ee 114 next;
2ec8e594 115 } elsif ($class->has_column($key)
116 && $class->column_info($key)->{_inflate_info}) {
61a622ee 117 $inflated->{$key} = $attrs->{$key};
118 next;
119 }
120 }
121 $new->throw_exception("No such column $key on $class")
122 unless $class->has_column($key);
123 $new->store_column($key => $attrs->{$key});
7624b19f 124 }
f90375dd 125
61a622ee 126 $new->{_relationship_data} = $related if $related;
127 $new->{_inflated_column} = $inflated if $inflated;
7624b19f 128 }
04786a4c 129
7624b19f 130 return $new;
131}
132
8091aa91 133=head2 insert
7624b19f 134
135 $obj->insert;
136
b8810cc5 137Inserts an object into the database if it isn't already in
138there. Returns the object itself. Requires the object's result source to
139be set, or the class to have a result_source_instance method. To insert
140an entirely new object into the database, use C<create> (see
141L<DBIx::Class::ResultSet/create>).
7624b19f 142
264f1571 143This will also insert any uninserted, related objects held inside this
144one, see L<DBIx::Class::ResultSet/create> for more details.
145
7624b19f 146=cut
147
148sub insert {
149 my ($self) = @_;
150 return $self if $self->in_storage;
6aba697f 151 my $source = $self->result_source;
152 $source ||= $self->result_source($self->result_source_instance)
097d3227 153 if $self->can('result_source_instance');
aeb1bf75 154 $self->throw_exception("No result_source set on this object; can't insert")
155 unless $source;
6e399b4f 156
9c6d6d93 157 my $rollback_guard;
158
33dd4e80 159 # Check if we stored uninserted relobjs here in new()
33dd4e80 160 my %related_stuff = (%{$self->{_relationship_data} || {}},
161 %{$self->{_inflated_column} || {}});
9c6d6d93 162
ae66ef47 163 if(!$self->{_rel_in_storage}) {
8222f722 164
9c6d6d93 165 # The guard will save us if we blow out of this scope via die
1bc193ac 166 $rollback_guard = $source->storage->txn_scope_guard;
9c6d6d93 167
8222f722 168 ## Should all be in relationship_data, but we need to get rid of the
169 ## 'filter' reltype..
170 ## These are the FK rels, need their IDs for the insert.
9c6d6d93 171
172 my @pri = $self->primary_columns;
173
174 REL: foreach my $relname (keys %related_stuff) {
a8c98174 175
176 my $rel_obj = $related_stuff{$relname};
177
178 next REL unless (Scalar::Util::blessed($rel_obj)
179 && $rel_obj->isa('DBIx::Class::Row'));
180
181 my $cond = $source->relationship_info($relname)->{cond};
182
183 next REL unless ref($cond) eq 'HASH';
184
185 # map { foreign.foo => 'self.bar' } to { bar => 'foo' }
186
187 my $keyhash = { map { my $x = $_; $x =~ s/.*\.//; $x; } reverse %$cond };
9c6d6d93 188
189 # assume anything that references our PK probably is dependent on us
a8c98174 190 # rather than vice versa, unless the far side is (a) defined or (b)
191 # auto-increment
9c6d6d93 192
193 foreach my $p (@pri) {
a8c98174 194 if (exists $keyhash->{$p}) {
a8c98174 195 unless (defined($rel_obj->get_column($keyhash->{$p}))
196 || $rel_obj->column_info($keyhash->{$p})
197 ->{is_auto_increment}) {
198 next REL;
199 }
200 }
9c6d6d93 201 }
202
a8c98174 203 $rel_obj->insert();
204 $self->set_from_related($relname, $rel_obj);
205 delete $related_stuff{$relname};
33dd4e80 206 }
207 }
6e399b4f 208
7af8b477 209 $source->storage->insert($source, { $self->get_columns });
ac8e89d7 210
211 ## PK::Auto
3fda409f 212 my @auto_pri = grep {
213 !defined $self->get_column($_) ||
214 ref($self->get_column($_)) eq 'SCALAR'
215 } $self->primary_columns;
216
217 if (@auto_pri) {
218 #$self->throw_exception( "More than one possible key found for auto-inc on ".ref $self )
219 # if defined $too_many;
ac8e89d7 220
221 my $storage = $self->result_source->storage;
222 $self->throw_exception( "Missing primary key but Storage doesn't support last_insert_id" )
223 unless $storage->can('last_insert_id');
3fda409f 224 my @ids = $storage->last_insert_id($self->result_source,@auto_pri);
225 $self->throw_exception( "Can't get last insert id" )
226 unless (@ids == @auto_pri);
227 $self->store_column($auto_pri[$_] => $ids[$_]) for 0 .. $#ids;
ac8e89d7 228 }
33dd4e80 229
ae66ef47 230 if(!$self->{_rel_in_storage}) {
8222f722 231 ## Now do the has_many rels, that need $selfs ID.
232 foreach my $relname (keys %related_stuff) {
9c6d6d93 233 my $rel_obj = $related_stuff{$relname};
234 my @cands;
235 if (Scalar::Util::blessed($rel_obj)
236 && $rel_obj->isa('DBIx::Class::Row')) {
237 @cands = ($rel_obj);
238 } elsif (ref $rel_obj eq 'ARRAY') {
239 @cands = @$rel_obj;
240 }
241 if (@cands) {
242 my $reverse = $source->reverse_relationship_info($relname);
243 foreach my $obj (@cands) {
244 $obj->set_from_related($_, $self) for keys %$reverse;
c193d1d2 245 $obj->insert() unless ($obj->in_storage || $obj->result_source->resultset->search({$obj->get_columns})->count);
8222f722 246 }
33dd4e80 247 }
248 }
1bc193ac 249 $rollback_guard->commit;
33dd4e80 250 }
33dd4e80 251
7624b19f 252 $self->in_storage(1);
253 $self->{_dirty_columns} = {};
64acc2bc 254 $self->{related_resultsets} = {};
729b29ae 255 undef $self->{_orig_ident};
7624b19f 256 return $self;
257}
258
8091aa91 259=head2 in_storage
7624b19f 260
261 $obj->in_storage; # Get value
262 $obj->in_storage(1); # Set value
263
264f1571 264Indicates whether the object exists as a row in the database or not
7624b19f 265
266=cut
267
268sub in_storage {
269 my ($self, $val) = @_;
270 $self->{_in_storage} = $val if @_ > 1;
271 return $self->{_in_storage};
272}
273
8091aa91 274=head2 update
7624b19f 275
d5d833d9 276 $obj->update \%columns?;
7624b19f 277
278Must be run on an object that is already in the database; issues an SQL
d3b0e369 279UPDATE query to commit any changes to the object to the database if
280required.
7624b19f 281
d5d833d9 282Also takes an options hashref of C<< column_name => value> pairs >> to update
f7043881 283first. But be aware that the hashref will be passed to
bacf6f12 284C<set_inflated_columns>, which might edit it in place, so dont rely on it being
285the same after a call to C<update>. If you need to preserve the hashref, it is
286sufficient to pass a shallow copy to C<update>, e.g. ( { %{ $href } } )
d5d833d9 287
7624b19f 288=cut
289
290sub update {
291 my ($self, $upd) = @_;
701da8c4 292 $self->throw_exception( "Not in database" ) unless $self->in_storage;
4b12b3c2 293 my $ident_cond = $self->ident_condition;
294 $self->throw_exception("Cannot safely update a row in a PK-less table")
295 if ! keys %$ident_cond;
6e399b4f 296
bacf6f12 297 $self->set_inflated_columns($upd) if $upd;
5a9e0e60 298 my %to_update = $self->get_dirty_columns;
299 return $self unless keys %to_update;
88cb6a1d 300 my $rows = $self->result_source->storage->update(
f4afcd5d 301 $self->result_source, \%to_update,
302 $self->{_orig_ident} || $ident_cond
303 );
7624b19f 304 if ($rows == 0) {
701da8c4 305 $self->throw_exception( "Can't update ${self}: row not found" );
7624b19f 306 } elsif ($rows > 1) {
701da8c4 307 $self->throw_exception("Can't update ${self}: updated more than one row");
7624b19f 308 }
309 $self->{_dirty_columns} = {};
64acc2bc 310 $self->{related_resultsets} = {};
729b29ae 311 undef $self->{_orig_ident};
7624b19f 312 return $self;
313}
314
8091aa91 315=head2 delete
7624b19f 316
317 $obj->delete
318
b8810cc5 319Deletes the object from the database. The object is still perfectly
61a622ee 320usable, but C<< ->in_storage() >> will now return 0 and the object must
321reinserted using C<< ->insert() >> before C<< ->update() >> can be used
b8810cc5 322on it. If you delete an object in a class with a C<has_many>
323relationship, all the related objects will be deleted as well. To turn
977e7403 324this behavior off, pass C<< cascade_delete => 0 >> in the C<$attr>
b8810cc5 325hashref. Any database-level cascade or restrict will take precedence
326over a DBIx-Class-based cascading delete. See also L<DBIx::Class::ResultSet/delete>.
7624b19f 327
328=cut
329
330sub delete {
331 my $self = shift;
332 if (ref $self) {
701da8c4 333 $self->throw_exception( "Not in database" ) unless $self->in_storage;
4b12b3c2 334 my $ident_cond = $self->ident_condition;
335 $self->throw_exception("Cannot safely delete a row in a PK-less table")
336 if ! keys %$ident_cond;
e0f56292 337 foreach my $column (keys %$ident_cond) {
75d07914 338 $self->throw_exception("Can't delete the object unless it has loaded the primary keys")
339 unless exists $self->{_column_data}{$column};
e0f56292 340 }
88cb6a1d 341 $self->result_source->storage->delete(
7af8b477 342 $self->result_source, $ident_cond);
7624b19f 343 $self->in_storage(undef);
7624b19f 344 } else {
701da8c4 345 $self->throw_exception("Can't do class delete without a ResultSource instance")
097d3227 346 unless $self->can('result_source_instance');
aeb1bf75 347 my $attrs = @_ > 1 && ref $_[$#_] eq 'HASH' ? { %{pop(@_)} } : {};
348 my $query = ref $_[0] eq 'HASH' ? $_[0] : {@_};
097d3227 349 $self->result_source_instance->resultset->search(@_)->delete;
7624b19f 350 }
351 return $self;
352}
353
8091aa91 354=head2 get_column
7624b19f 355
356 my $val = $obj->get_column($col);
357
61a622ee 358Gets a column value from a row object. Does not do any queries; the column
359must have already been fetched from the database and stored in the object. If
360there is an inflated value stored that has not yet been deflated, it is deflated
361when the method is invoked.
7624b19f 362
363=cut
364
365sub get_column {
366 my ($self, $column) = @_;
701da8c4 367 $self->throw_exception( "Can't fetch data as class method" ) unless ref $self;
aeb1bf75 368 return $self->{_column_data}{$column} if exists $self->{_column_data}{$column};
61a622ee 369 if (exists $self->{_inflated_column}{$column}) {
370 return $self->store_column($column,
371 $self->_deflated_column($column, $self->{_inflated_column}{$column}));
372 }
701da8c4 373 $self->throw_exception( "No such column '${column}'" ) unless $self->has_column($column);
7624b19f 374 return undef;
375}
376
9b83fccd 377=head2 has_column_loaded
378
379 if ( $obj->has_column_loaded($col) ) {
380 print "$col has been loaded from db";
381 }
382
383Returns a true value if the column value has been loaded from the
384database (or set locally).
385
386=cut
387
def81720 388sub has_column_loaded {
389 my ($self, $column) = @_;
390 $self->throw_exception( "Can't call has_column data as class method" ) unless ref $self;
61a622ee 391 return 1 if exists $self->{_inflated_column}{$column};
aeb1bf75 392 return exists $self->{_column_data}{$column};
def81720 393}
394
8091aa91 395=head2 get_columns
076a6864 396
397 my %data = $obj->get_columns;
398
8091aa91 399Does C<get_column>, for all column values at once.
076a6864 400
401=cut
402
403sub get_columns {
404 my $self = shift;
61a622ee 405 if (exists $self->{_inflated_column}) {
406 foreach my $col (keys %{$self->{_inflated_column}}) {
407 $self->store_column($col, $self->_deflated_column($col, $self->{_inflated_column}{$col}))
c4a30d56 408 unless exists $self->{_column_data}{$col};
61a622ee 409 }
410 }
cb5f2eea 411 return %{$self->{_column_data}};
d7156e50 412}
413
414=head2 get_dirty_columns
415
416 my %data = $obj->get_dirty_columns;
417
418Identical to get_columns but only returns those that have been changed.
419
420=cut
421
422sub get_dirty_columns {
423 my $self = shift;
424 return map { $_ => $self->{_column_data}{$_} }
425 keys %{$self->{_dirty_columns}};
076a6864 426}
427
ba4a6453 428=head2 get_inflated_columns
429
430 my $inflated_data = $obj->get_inflated_columns;
431
432Similar to get_columns but objects are returned for inflated columns instead of their raw non-inflated values.
433
434=cut
435
436sub get_inflated_columns {
437 my $self = shift;
438 return map {
439 my $accessor = $self->column_info($_)->{'accessor'} || $_;
440 ($_ => $self->$accessor);
441 } $self->columns;
442}
443
8091aa91 444=head2 set_column
7624b19f 445
446 $obj->set_column($col => $val);
447
8091aa91 448Sets a column value. If the new value is different from the old one,
449the column is marked as dirty for when you next call $obj->update.
7624b19f 450
451=cut
452
453sub set_column {
454 my $self = shift;
455 my ($column) = @_;
729b29ae 456 $self->{_orig_ident} ||= $self->ident_condition;
7624b19f 457 my $old = $self->get_column($column);
458 my $ret = $self->store_column(@_);
87772e46 459 $self->{_dirty_columns}{$column} = 1
4c9f72c1 460 if (defined $old xor defined $ret) || (defined $old && $old ne $ret);
e60dc79f 461
462 # XXX clear out the relation cache for this column
463 delete $self->{related_resultsets}{$column};
464
7624b19f 465 return $ret;
466}
467
8091aa91 468=head2 set_columns
076a6864 469
dc818523 470 my $copy = $orig->set_columns({ $col => $val, ... });
076a6864 471
8091aa91 472Sets more than one column value at once.
076a6864 473
474=cut
475
476sub set_columns {
477 my ($self,$data) = @_;
a2ca474b 478 foreach my $col (keys %$data) {
479 $self->set_column($col,$data->{$col});
076a6864 480 }
c01ab172 481 return $self;
076a6864 482}
483
bacf6f12 484=head2 set_inflated_columns
485
486 my $copy = $orig->set_inflated_columns({ $col => $val, $rel => $obj, ... });
487
488Sets more than one column value at once, taking care to respect inflations and
489relationships if relevant. Be aware that this hashref might be edited in place,
490so dont rely on it being the same after a call to C<set_inflated_columns>. If
491you need to preserve the hashref, it is sufficient to pass a shallow copy to
492C<set_inflated_columns>, e.g. ( { %{ $href } } )
493
494=cut
495
496sub set_inflated_columns {
497 my ( $self, $upd ) = @_;
498 foreach my $key (keys %$upd) {
499 if (ref $upd->{$key}) {
500 my $info = $self->relationship_info($key);
501 if ($info && $info->{attrs}{accessor}
502 && $info->{attrs}{accessor} eq 'single')
503 {
504 my $rel = delete $upd->{$key};
505 $self->set_from_related($key => $rel);
506 $self->{_relationship_data}{$key} = $rel;
507 } elsif ($info && $info->{attrs}{accessor}
508 && $info->{attrs}{accessor} eq 'multi'
509 && ref $upd->{$key} eq 'ARRAY') {
510 my $others = delete $upd->{$key};
511 foreach my $rel_obj (@$others) {
512 if(!Scalar::Util::blessed($rel_obj)) {
513 $rel_obj = $self->create_related($key, $rel_obj);
514 }
515 }
516 $self->{_relationship_data}{$key} = $others;
517# $related->{$key} = $others;
518 next;
519 }
520 elsif ($self->has_column($key)
521 && exists $self->column_info($key)->{_inflate_info})
522 {
523 $self->set_inflated_column($key, delete $upd->{$key});
524 }
525 }
526 }
527 $self->set_columns($upd);
528}
529
8091aa91 530=head2 copy
076a6864 531
532 my $copy = $orig->copy({ change => $to, ... });
533
8091aa91 534Inserts a new row with the specified changes.
076a6864 535
536=cut
537
c01ab172 538sub copy {
539 my ($self, $changes) = @_;
333cce60 540 $changes ||= {};
fde6e28e 541 my $col_data = { %{$self->{_column_data}} };
542 foreach my $col (keys %$col_data) {
543 delete $col_data->{$col}
544 if $self->result_source->column_info($col)->{is_auto_increment};
545 }
04786a4c 546
547 my $new = { _column_data => $col_data };
548 bless $new, ref $self;
549
83419ec6 550 $new->result_source($self->result_source);
bacf6f12 551 $new->set_inflated_columns($changes);
333cce60 552 $new->insert;
35688220 553
554 # Its possible we'll have 2 relations to the same Source. We need to make
555 # sure we don't try to insert the same row twice esle we'll violate unique
556 # constraints
557 my $rels_copied = {};
558
333cce60 559 foreach my $rel ($self->result_source->relationships) {
560 my $rel_info = $self->result_source->relationship_info($rel);
35688220 561
562 next unless $rel_info->{attrs}{cascade_copy};
563
564 my $resolved = $self->result_source->resolve_condition(
565 $rel_info->{cond}, $rel, $new
566 );
567
568 my $copied = $rels_copied->{ $rel_info->{source} } ||= {};
569 foreach my $related ($self->search_related($rel)) {
570 my $id_str = join("\0", $related->id);
571 next if $copied->{$id_str};
572 $copied->{$id_str} = 1;
573 my $rel_copy = $related->copy($resolved);
333cce60 574 }
35688220 575
333cce60 576 }
2c4c67b6 577 return $new;
c01ab172 578}
579
8091aa91 580=head2 store_column
7624b19f 581
582 $obj->store_column($col => $val);
583
8091aa91 584Sets a column value without marking it as dirty.
7624b19f 585
586=cut
587
588sub store_column {
589 my ($self, $column, $value) = @_;
75d07914 590 $self->throw_exception( "No such column '${column}'" )
d7156e50 591 unless exists $self->{_column_data}{$column} || $self->has_column($column);
75d07914 592 $self->throw_exception( "set_column called for ${column} without value" )
7624b19f 593 if @_ < 3;
594 return $self->{_column_data}{$column} = $value;
595}
596
b52e9bf8 597=head2 inflate_result
598
c01ab172 599 Class->inflate_result($result_source, \%me, \%prefetch?)
b52e9bf8 600
601Called by ResultSet to inflate a result from storage
602
603=cut
604
605sub inflate_result {
c01ab172 606 my ($class, $source, $me, $prefetch) = @_;
aec3eff1 607
608 my ($source_handle) = $source;
609
610 if ($source->isa('DBIx::Class::ResultSourceHandle')) {
611 $source = $source_handle->resolve
612 } else {
613 $source_handle = $source->handle
614 }
615
04786a4c 616 my $new = {
aec3eff1 617 _source_handle => $source_handle,
04786a4c 618 _column_data => $me,
619 _in_storage => 1
620 };
621 bless $new, (ref $class || $class);
622
7fb16f1a 623 my $schema;
64acc2bc 624 foreach my $pre (keys %{$prefetch||{}}) {
625 my $pre_val = $prefetch->{$pre};
f9cc31dd 626 my $pre_source = $source->related_source($pre);
a86b1efe 627 $class->throw_exception("Can't prefetch non-existent relationship ${pre}")
628 unless $pre_source;
0f66a01b 629 if (ref($pre_val->[0]) eq 'ARRAY') { # multi
a86b1efe 630 my @pre_objects;
631 foreach my $pre_rec (@$pre_val) {
75d07914 632 unless ($pre_source->primary_columns == grep { exists $pre_rec->[0]{$_}
5a5bec6c 633 and defined $pre_rec->[0]{$_} } $pre_source->primary_columns) {
a86b1efe 634 next;
635 }
636 push(@pre_objects, $pre_source->result_class->inflate_result(
637 $pre_source, @{$pre_rec}));
638 }
639 $new->related_resultset($pre)->set_cache(\@pre_objects);
62e87ea8 640 } elsif (defined $pre_val->[0]) {
a86b1efe 641 my $fetched;
75d07914 642 unless ($pre_source->primary_columns == grep { exists $pre_val->[0]{$_}
a86b1efe 643 and !defined $pre_val->[0]{$_} } $pre_source->primary_columns)
644 {
645 $fetched = $pre_source->result_class->inflate_result(
75d07914 646 $pre_source, @{$pre_val});
a86b1efe 647 }
9809a6df 648 $new->related_resultset($pre)->set_cache([ $fetched ]);
a86b1efe 649 my $accessor = $source->relationship_info($pre)->{attrs}{accessor};
650 $class->throw_exception("No accessor for prefetched $pre")
651 unless defined $accessor;
652 if ($accessor eq 'single') {
653 $new->{_relationship_data}{$pre} = $fetched;
654 } elsif ($accessor eq 'filter') {
655 $new->{_inflated_column}{$pre} = $fetched;
656 } else {
657 $class->throw_exception("Prefetch not supported with accessor '$accessor'");
658 }
b52e9bf8 659 }
660 }
7624b19f 661 return $new;
662}
663
9b465d00 664=head2 update_or_insert
7624b19f 665
9b465d00 666 $obj->update_or_insert
7624b19f 667
8091aa91 668Updates the object if it's already in the db, else inserts it.
7624b19f 669
9b83fccd 670=head2 insert_or_update
671
672 $obj->insert_or_update
673
674Alias for L</update_or_insert>
675
7624b19f 676=cut
677
9b465d00 678*insert_or_update = \&update_or_insert;
679sub update_or_insert {
7624b19f 680 my $self = shift;
681 return ($self->in_storage ? $self->update : $self->insert);
682}
683
8091aa91 684=head2 is_changed
7624b19f 685
228dbcb4 686 my @changed_col_names = $obj->is_changed();
687 if ($obj->is_changed()) { ... }
7624b19f 688
9b83fccd 689In array context returns a list of columns with uncommited changes, or
690in scalar context returns a true value if there are uncommitted
691changes.
692
7624b19f 693=cut
694
695sub is_changed {
696 return keys %{shift->{_dirty_columns} || {}};
697}
228dbcb4 698
699=head2 is_column_changed
700
701 if ($obj->is_column_changed('col')) { ... }
702
9b83fccd 703Returns a true value if the column has uncommitted changes.
704
228dbcb4 705=cut
706
707sub is_column_changed {
708 my( $self, $col ) = @_;
709 return exists $self->{_dirty_columns}->{$col};
710}
7624b19f 711
097d3227 712=head2 result_source
713
9b83fccd 714 my $resultsource = $object->result_source;
097d3227 715
9b83fccd 716Accessor to the ResultSource this object was created from
87c4e602 717
aec3eff1 718=cut
719
720sub result_source {
721 my $self = shift;
722
723 if (@_) {
724 $self->_source_handle($_[0]->handle);
725 } else {
726 $self->_source_handle->resolve;
727 }
728}
729
9b83fccd 730=head2 register_column
27f01d1f 731
9b83fccd 732 $column_info = { .... };
733 $class->register_column($column_name, $column_info);
27f01d1f 734
9b83fccd 735Registers a column on the class. If the column_info has an 'accessor'
736key, creates an accessor named after the value if defined; if there is
737no such key, creates an accessor with the same name as the column
1f23a877 738
9b83fccd 739The column_info attributes are described in
740L<DBIx::Class::ResultSource/add_columns>
1f23a877 741
097d3227 742=cut
743
1f23a877 744sub register_column {
745 my ($class, $col, $info) = @_;
91b0fbd7 746 my $acc = $col;
747 if (exists $info->{accessor}) {
748 return unless defined $info->{accessor};
749 $acc = [ $info->{accessor}, $col ];
750 }
751 $class->mk_group_accessors('column' => $acc);
1f23a877 752}
753
701da8c4 754
5160b401 755=head2 throw_exception
701da8c4 756
757See Schema's throw_exception.
758
759=cut
760
761sub throw_exception {
762 my $self=shift;
66cab05c 763 if (ref $self && ref $self->result_source && $self->result_source->schema) {
701da8c4 764 $self->result_source->schema->throw_exception(@_);
765 } else {
766 croak(@_);
767 }
768}
769
33cf6616 770=head2 id
771
772Returns the primary key(s) for a row. Can't be called as a class method.
f7043881 773Actually implemented in L<DBIx::Class::PK>
33cf6616 774
775=head2 discard_changes
776
777Re-selects the row from the database, losing any changes that had
778been made.
779
780This method can also be used to refresh from storage, retrieving any
781changes made since the row was last read from storage. Actually
f7043881 782implemented in L<DBIx::Class::PK>
33cf6616 783
784=cut
785
7624b19f 7861;
787
7624b19f 788=head1 AUTHORS
789
daec44b8 790Matt S. Trout <mst@shadowcatsystems.co.uk>
7624b19f 791
792=head1 LICENSE
793
794You may distribute this code under the same terms as Perl itself.
795
796=cut