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