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