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