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