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