Improve error message on UD operation failures due to an incomplete identity
[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/;
1a58752c 7
8use DBIx::Class::Exception;
6298a324 9use Scalar::Util 'blessed';
9780718f 10use Try::Tiny;
fd323bf1 11use namespace::clean;
1edd1722 12
0d5d1f12 13###
14### Internal method
15### Do not use
16###
e0cdf2cb 17BEGIN {
18 *MULTICREATE_DEBUG =
19 $ENV{DBIC_MULTICREATE_DEBUG}
20 ? sub () { 1 }
21 : sub () { 0 };
22}
23
aec3eff1 24__PACKAGE__->mk_group_accessors('simple' => qw/_source_handle/);
8c49f629 25
75d07914 26=head1 NAME
7624b19f 27
28DBIx::Class::Row - Basic row methods
29
30=head1 SYNOPSIS
31
32=head1 DESCRIPTION
33
34This class is responsible for defining and doing basic operations on rows
1ea77c14 35derived from L<DBIx::Class::ResultSource> objects.
7624b19f 36
a2531bf2 37Row objects are returned from L<DBIx::Class::ResultSet>s using the
ea36f4e4 38L<create|DBIx::Class::ResultSet/create>, L<find|DBIx::Class::ResultSet/find>,
39L<next|DBIx::Class::ResultSet/next> and L<all|DBIx::Class::ResultSet/all> methods,
40as well as invocations of 'single' (
41L<belongs_to|DBIx::Class::Relationship/belongs_to>,
42L<has_one|DBIx::Class::Relationship/has_one> or
43L<might_have|DBIx::Class::Relationship/might_have>)
44relationship accessors of L<DBIx::Class::Row> objects.
a2531bf2 45
7624b19f 46=head1 METHODS
47
8091aa91 48=head2 new
7624b19f 49
a2531bf2 50 my $row = My::Class->new(\%attrs);
51
52 my $row = $schema->resultset('MySource')->new(\%colsandvalues);
53
54=over
55
56=item Arguments: \%attrs or \%colsandvalues
57
58=item Returns: A Row object
7624b19f 59
a2531bf2 60=back
61
62While you can create a new row object by calling C<new> directly on
63this class, you are better off calling it on a
64L<DBIx::Class::ResultSet> object.
65
66When calling it directly, you will not get a complete, usable row
67object until you pass or set the C<source_handle> attribute, to a
68L<DBIx::Class::ResultSource> instance that is attached to a
69L<DBIx::Class::Schema> with a valid connection.
70
71C<$attrs> is a hashref of column name, value data. It can also contain
72some other attributes such as the C<source_handle>.
7624b19f 73
33dd4e80 74Passing an object, or an arrayref of objects as a value will call
75L<DBIx::Class::Relationship::Base/set_from_related> for you. When
76passed a hashref or an arrayref of hashrefs as the value, these will
77be turned into objects via new_related, and treated as if you had
78passed objects.
79
264f1571 80For a more involved explanation, see L<DBIx::Class::ResultSet/create>.
81
dc5f0ad3 82Please note that if a value is not passed to new, no value will be sent
83in the SQL INSERT call, and the column will therefore assume whatever
84default value was specified in your database. While DBIC will retrieve the
85value of autoincrement columns, it will never make an explicit database
86trip to retrieve default values assigned by the RDBMS. You can explicitly
87request that all values be fetched back from the database by calling
88L</discard_changes>, or you can supply an explicit C<undef> to columns
89with NULL as the default, and save yourself a SELECT.
90
91 CAVEAT:
92
93 The behavior described above will backfire if you use a foreign key column
94 with a database-defined default. If you call the relationship accessor on
95 an object that doesn't have a set value for the FK column, DBIC will throw
96 an exception, as it has no way of knowing the PK of the related object (if
97 there is one).
98
7624b19f 99=cut
100
33dd4e80 101## 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().
102## This only works because DBIC doesnt yet care to check whether the new_related objects have been passed all their mandatory columns
103## When doing the later insert, we need to make sure the PKs are set.
104## using _relationship_data in new and funky ways..
105## check Relationship::CascadeActions and Relationship::Accessor for compat
106## tests!
107
370f2ba2 108sub __new_related_find_or_new_helper {
109 my ($self, $relname, $data) = @_;
68888c09 110
b7ded743 111 my $rsrc = $self->result_source;
112
68888c09 113 # create a mock-object so all new/set_column component overrides will run:
b7ded743 114 my $rel_rs = $rsrc->related_source($relname)->resultset;
68888c09 115 my $new_rel_obj = $rel_rs->new_result($data);
116 my $proc_data = { $new_rel_obj->get_columns };
117
118 if ($self->__their_pk_needs_us($relname)) {
de404241 119 MULTICREATE_DEBUG and warn "MC $self constructing $relname via new_result";
68888c09 120 return $new_rel_obj;
121 }
b7ded743 122 elsif ($rsrc->_pk_depends_on($relname, $proc_data )) {
68888c09 123 if (! keys %$proc_data) {
124 # there is nothing to search for - blind create
125 MULTICREATE_DEBUG and warn "MC $self constructing default-insert $relname";
126 }
127 else {
128 MULTICREATE_DEBUG and warn "MC $self constructing $relname via find_or_new";
129 # this is not *really* find or new, as we don't want to double-new the
130 # data (thus potentially double encoding or whatever)
131 my $exists = $rel_rs->find ($proc_data);
132 return $exists if $exists;
133 }
134 return $new_rel_obj;
370f2ba2 135 }
68888c09 136 else {
b7ded743 137 my $us = $rsrc->source_name;
68888c09 138 $self->throw_exception ("'$us' neither depends nor is depended on by '$relname', something is wrong...");
370f2ba2 139 }
370f2ba2 140}
141
142sub __their_pk_needs_us { # this should maybe be in resultsource.
68888c09 143 my ($self, $relname) = @_;
370f2ba2 144 my $source = $self->result_source;
145 my $reverse = $source->reverse_relationship_info($relname);
146 my $rel_source = $source->related_source($relname);
147 my $us = { $self->get_columns };
148 foreach my $key (keys %$reverse) {
149 # if their primary key depends on us, then we have to
150 # just create a result and we'll fill it out afterwards
6d0ee587 151 return 1 if $rel_source->_pk_depends_on($key, $us);
370f2ba2 152 }
153 return 0;
154}
155
7624b19f 156sub new {
448f820f 157 my ($class, $attrs) = @_;
7624b19f 158 $class = ref $class if ref $class;
04786a4c 159
e60dc79f 160 my $new = {
161 _column_data => {},
162 };
04786a4c 163 bless $new, $class;
164
448f820f 165 if (my $handle = delete $attrs->{-source_handle}) {
166 $new->_source_handle($handle);
167 }
370f2ba2 168
169 my $source;
170 if ($source = delete $attrs->{-result_source}) {
e9fe476b 171 $new->result_source($source);
172 }
a6a280b9 173
fa7a51af 174 if (my $related = delete $attrs->{-cols_from_relations}) {
09e1f723 175 @{$new->{_ignore_at_insert}={}}{@$related} = ();
176 }
177
7624b19f 178 if ($attrs) {
27f01d1f 179 $new->throw_exception("attrs must be a hashref")
180 unless ref($attrs) eq 'HASH';
b6d347e0 181
61a622ee 182 my ($related,$inflated);
8222f722 183
61a622ee 184 foreach my $key (keys %$attrs) {
185 if (ref $attrs->{$key}) {
af2d42c0 186 ## Can we extract this lot to use with update(_or .. ) ?
1a58752c 187 $new->throw_exception("Can't do multi-create without result source")
188 unless $source;
370f2ba2 189 my $info = $source->relationship_info($key);
b82c8a28 190 my $acc_type = $info->{attrs}{accessor} || '';
191 if ($acc_type eq 'single') {
de7c7c53 192 my $rel_obj = delete $attrs->{$key};
6298a324 193 if(!blessed $rel_obj) {
370f2ba2 194 $rel_obj = $new->__new_related_find_or_new_helper($key, $rel_obj);
33dd4e80 195 }
2bc3c81e 196
e0cdf2cb 197 if ($rel_obj->in_storage) {
d4fe33d0 198 $new->{_rel_in_storage}{$key} = 1;
e0cdf2cb 199 $new->set_from_related($key, $rel_obj);
200 } else {
09e1f723 201 MULTICREATE_DEBUG and warn "MC $new uninserted $key $rel_obj\n";
e0cdf2cb 202 }
2bc3c81e 203
de7c7c53 204 $related->{$key} = $rel_obj;
61a622ee 205 next;
b82c8a28 206 }
207 elsif ($acc_type eq 'multi' && ref $attrs->{$key} eq 'ARRAY' ) {
2ec8e594 208 my $others = delete $attrs->{$key};
e0cdf2cb 209 my $total = @$others;
210 my @objects;
211 foreach my $idx (0 .. $#$others) {
212 my $rel_obj = $others->[$idx];
6298a324 213 if(!blessed $rel_obj) {
370f2ba2 214 $rel_obj = $new->__new_related_find_or_new_helper($key, $rel_obj);
33dd4e80 215 }
2bc3c81e 216
e0cdf2cb 217 if ($rel_obj->in_storage) {
d4fe33d0 218 $rel_obj->throw_exception ('A multi relationship can not be pre-existing when doing multicreate. Something went wrong');
e0cdf2cb 219 } else {
e0cdf2cb 220 MULTICREATE_DEBUG and
09e1f723 221 warn "MC $new uninserted $key $rel_obj (${\($idx+1)} of $total)\n";
e0cdf2cb 222 }
e0cdf2cb 223 push(@objects, $rel_obj);
2ec8e594 224 }
e0cdf2cb 225 $related->{$key} = \@objects;
2ec8e594 226 next;
b82c8a28 227 }
228 elsif ($acc_type eq 'filter') {
33dd4e80 229 ## 'filter' should disappear and get merged in with 'single' above!
2ec8e594 230 my $rel_obj = delete $attrs->{$key};
6298a324 231 if(!blessed $rel_obj) {
370f2ba2 232 $rel_obj = $new->__new_related_find_or_new_helper($key, $rel_obj);
33dd4e80 233 }
d4fe33d0 234 if ($rel_obj->in_storage) {
235 $new->{_rel_in_storage}{$key} = 1;
236 }
237 else {
09e1f723 238 MULTICREATE_DEBUG and warn "MC $new uninserted $key $rel_obj";
e0cdf2cb 239 }
33dd4e80 240 $inflated->{$key} = $rel_obj;
61a622ee 241 next;
2ec8e594 242 } elsif ($class->has_column($key)
243 && $class->column_info($key)->{_inflate_info}) {
61a622ee 244 $inflated->{$key} = $attrs->{$key};
245 next;
246 }
247 }
248 $new->throw_exception("No such column $key on $class")
249 unless $class->has_column($key);
b6d347e0 250 $new->store_column($key => $attrs->{$key});
7624b19f 251 }
f90375dd 252
61a622ee 253 $new->{_relationship_data} = $related if $related;
254 $new->{_inflated_column} = $inflated if $inflated;
7624b19f 255 }
04786a4c 256
7624b19f 257 return $new;
258}
259
8091aa91 260=head2 insert
7624b19f 261
a2531bf2 262 $row->insert;
263
264=over
7624b19f 265
a2531bf2 266=item Arguments: none
267
268=item Returns: The Row object
269
270=back
271
272Inserts an object previously created by L</new> into the database if
273it isn't already in there. Returns the object itself. Requires the
274object's result source to be set, or the class to have a
275result_source_instance method. To insert an entirely new row into
276the database, use C<create> (see L<DBIx::Class::ResultSet/create>).
7624b19f 277
e91e756c 278To fetch an uninserted row object, call
279L<new|DBIx::Class::ResultSet/new> on a resultset.
280
264f1571 281This will also insert any uninserted, related objects held inside this
282one, see L<DBIx::Class::ResultSet/create> for more details.
283
7624b19f 284=cut
285
286sub insert {
287 my ($self) = @_;
288 return $self if $self->in_storage;
6aba697f 289 my $source = $self->result_source;
290 $source ||= $self->result_source($self->result_source_instance)
097d3227 291 if $self->can('result_source_instance');
aeb1bf75 292 $self->throw_exception("No result_source set on this object; can't insert")
293 unless $source;
6e399b4f 294
9c6d6d93 295 my $rollback_guard;
296
33dd4e80 297 # Check if we stored uninserted relobjs here in new()
b6d347e0 298 my %related_stuff = (%{$self->{_relationship_data} || {}},
33dd4e80 299 %{$self->{_inflated_column} || {}});
9c6d6d93 300
d4fe33d0 301 # insert what needs to be inserted before us
302 my %pre_insert;
303 for my $relname (keys %related_stuff) {
304 my $rel_obj = $related_stuff{$relname};
9c6d6d93 305
d4fe33d0 306 if (! $self->{_rel_in_storage}{$relname}) {
6298a324 307 next unless (blessed $rel_obj && $rel_obj->isa('DBIx::Class::Row'));
a8c98174 308
d4fe33d0 309 next unless $source->_pk_depends_on(
310 $relname, { $rel_obj->get_columns }
311 );
a8c98174 312
d4fe33d0 313 # The guard will save us if we blow out of this scope via die
314 $rollback_guard ||= $source->storage->txn_scope_guard;
9c6d6d93 315
de404241 316 MULTICREATE_DEBUG and warn "MC $self pre-reconstructing $relname $rel_obj\n";
e0cdf2cb 317
380d34f5 318 my $them = { %{$rel_obj->{_relationship_data} || {} }, $rel_obj->get_columns };
68888c09 319 my $existing;
320
321 # if there are no keys - nothing to search for
322 if (keys %$them and $existing = $self->result_source
323 ->related_source($relname)
324 ->resultset
325 ->find($them)
326 ) {
327 %{$rel_obj} = %{$existing};
328 }
329 else {
330 $rel_obj->insert;
331 }
d4fe33d0 332
d4fe33d0 333 $self->{_rel_in_storage}{$relname} = 1;
33dd4e80 334 }
d4fe33d0 335
336 $self->set_from_related($relname, $rel_obj);
337 delete $related_stuff{$relname};
338 }
339
340 # start a transaction here if not started yet and there is more stuff
341 # to insert after us
342 if (keys %related_stuff) {
343 $rollback_guard ||= $source->storage->txn_scope_guard
33dd4e80 344 }
6e399b4f 345
1e45aa87 346 ## PK::Auto
347 my %auto_pri;
348 my $auto_idx = 0;
349 for ($self->primary_columns) {
350 if (
351 not defined $self->get_column($_)
352 ||
353 (ref($self->get_column($_)) eq 'SCALAR')
354 ) {
355 my $col_info = $source->column_info($_);
356 $auto_pri{$_} = $auto_idx++ unless $col_info->{auto_nextval}; # auto_nextval's are pre-fetched in the storage
357 }
358 }
359
09e1f723 360 MULTICREATE_DEBUG and do {
361 no warnings 'uninitialized';
362 warn "MC $self inserting (".join(', ', $self->get_columns).")\n";
363 };
1e45aa87 364 my $updated_cols = $source->storage->insert(
365 $source,
366 { $self->get_columns },
227d8366 367 (keys %auto_pri) && $source->storage->_supports_insert_returning
734868da 368 ? { returning => [ sort { $auto_pri{$a} <=> $auto_pri{$b} } keys %auto_pri ] }
1e45aa87 369 : ()
370 ,
371 );
372
645de900 373 foreach my $col (keys %$updated_cols) {
374 $self->store_column($col, $updated_cols->{$col});
1e45aa87 375 delete $auto_pri{$col};
645de900 376 }
ac8e89d7 377
1e45aa87 378 if (keys %auto_pri) {
379 my @missing = sort { $auto_pri{$a} <=> $auto_pri{$b} } keys %auto_pri;
380 MULTICREATE_DEBUG and warn "MC $self fetching missing PKs ".join(', ', @missing )."\n";
ac8e89d7 381 my $storage = $self->result_source->storage;
382 $self->throw_exception( "Missing primary key but Storage doesn't support last_insert_id" )
383 unless $storage->can('last_insert_id');
1e45aa87 384 my @ids = $storage->last_insert_id($self->result_source, @missing);
3fda409f 385 $self->throw_exception( "Can't get last insert id" )
1e45aa87 386 unless (@ids == @missing);
387 $self->store_column($missing[$_] => $ids[$_]) for 0 .. $#missing;
ac8e89d7 388 }
33dd4e80 389
370f2ba2 390 $self->{_dirty_columns} = {};
391 $self->{related_resultsets} = {};
392
d4fe33d0 393 foreach my $relname (keys %related_stuff) {
31c3800e 394 next unless $source->has_relationship ($relname);
395
396 my @cands = ref $related_stuff{$relname} eq 'ARRAY'
397 ? @{$related_stuff{$relname}}
398 : $related_stuff{$relname}
399 ;
d4fe33d0 400
6298a324 401 if (@cands && blessed $cands[0] && $cands[0]->isa('DBIx::Class::Row')
31c3800e 402 ) {
d4fe33d0 403 my $reverse = $source->reverse_relationship_info($relname);
404 foreach my $obj (@cands) {
405 $obj->set_from_related($_, $self) for keys %$reverse;
68888c09 406 if ($self->__their_pk_needs_us($relname)) {
d4fe33d0 407 if (exists $self->{_ignore_at_insert}{$relname}) {
408 MULTICREATE_DEBUG and warn "MC $self skipping post-insert on $relname";
65ee2b31 409 }
410 else {
411 MULTICREATE_DEBUG and warn "MC $self inserting $relname $obj";
412 $obj->insert;
370f2ba2 413 }
d4fe33d0 414 } else {
415 MULTICREATE_DEBUG and warn "MC $self post-inserting $obj";
416 $obj->insert();
8222f722 417 }
33dd4e80 418 }
419 }
420 }
33dd4e80 421
7624b19f 422 $self->in_storage(1);
d4fe33d0 423 delete $self->{_orig_ident};
613a5b03 424 delete $self->{_orig_ident_failreason};
d4fe33d0 425 delete $self->{_ignore_at_insert};
426 $rollback_guard->commit if $rollback_guard;
427
7624b19f 428 return $self;
429}
430
8091aa91 431=head2 in_storage
7624b19f 432
a2531bf2 433 $row->in_storage; # Get value
434 $row->in_storage(1); # Set value
435
436=over
437
438=item Arguments: none or 1|0
439
440=item Returns: 1|0
441
442=back
7624b19f 443
e91e756c 444Indicates whether the object exists as a row in the database or
445not. This is set to true when L<DBIx::Class::ResultSet/find>,
446L<DBIx::Class::ResultSet/create> or L<DBIx::Class::ResultSet/insert>
b6d347e0 447are used.
e91e756c 448
449Creating a row object using L<DBIx::Class::ResultSet/new>, or calling
450L</delete> on one, sets it to false.
7624b19f 451
452=cut
453
454sub in_storage {
455 my ($self, $val) = @_;
456 $self->{_in_storage} = $val if @_ > 1;
63bb9738 457 return $self->{_in_storage} ? 1 : 0;
7624b19f 458}
459
8091aa91 460=head2 update
7624b19f 461
a2531bf2 462 $row->update(\%columns?)
463
464=over
7624b19f 465
a2531bf2 466=item Arguments: none or a hashref
7624b19f 467
a2531bf2 468=item Returns: The Row object
469
470=back
471
472Throws an exception if the row object is not yet in the database,
473according to L</in_storage>.
474
475This method issues an SQL UPDATE query to commit any changes to the
d6988be8 476object to the database if required (see L</get_dirty_columns>).
477It throws an exception if a proper WHERE clause uniquely identifying
478the database row can not be constructed (see
479L<significance of primary keys|DBIx::Class::Manual::Intro/The Significance and Importance of Primary Keys>
480for more details).
a2531bf2 481
0d0fcdbf 482Also takes an optional hashref of C<< column_name => value >> pairs
a2531bf2 483to update on the object first. Be aware that the hashref will be
484passed to C<set_inflated_columns>, which might edit it in place, so
485don't rely on it being the same after a call to C<update>. If you
486need to preserve the hashref, it is sufficient to pass a shallow copy
487to C<update>, e.g. ( { %{ $href } } )
d5d833d9 488
05d1bc9c 489If the values passed or any of the column values set on the object
48580715 490contain scalar references, e.g.:
05d1bc9c 491
a2531bf2 492 $row->last_modified(\'NOW()');
05d1bc9c 493 # OR
a2531bf2 494 $row->update({ last_modified => \'NOW()' });
05d1bc9c 495
496The update will pass the values verbatim into SQL. (See
497L<SQL::Abstract> docs). The values in your Row object will NOT change
498as a result of the update call, if you want the object to be updated
499with the actual values from the database, call L</discard_changes>
500after the update.
501
a2531bf2 502 $row->update()->discard_changes();
503
504To determine before calling this method, which column values have
505changed and will be updated, call L</get_dirty_columns>.
506
507To check if any columns will be updated, call L</is_changed>.
508
509To force a column to be updated, call L</make_column_dirty> before
510this method.
05d1bc9c 511
7624b19f 512=cut
513
514sub update {
515 my ($self, $upd) = @_;
cf856357 516
97a50975 517 my $ident_cond = $self->{_orig_ident} || $self->ident_condition;
518
bacf6f12 519 $self->set_inflated_columns($upd) if $upd;
5a9e0e60 520 my %to_update = $self->get_dirty_columns;
521 return $self unless keys %to_update;
de5ce481 522
523 $self->throw_exception( "Not in database" ) unless $self->in_storage;
524
613a5b03 525 $self->throw_exception($self->{_orig_ident_failreason})
ad3f2296 526 if ! keys %$ident_cond;
527
88cb6a1d 528 my $rows = $self->result_source->storage->update(
cf856357 529 $self->result_source, \%to_update, $ident_cond
530 );
7624b19f 531 if ($rows == 0) {
701da8c4 532 $self->throw_exception( "Can't update ${self}: row not found" );
7624b19f 533 } elsif ($rows > 1) {
701da8c4 534 $self->throw_exception("Can't update ${self}: updated more than one row");
7624b19f 535 }
536 $self->{_dirty_columns} = {};
64acc2bc 537 $self->{related_resultsets} = {};
cf856357 538 delete $self->{_orig_ident};
7624b19f 539 return $self;
540}
541
8091aa91 542=head2 delete
7624b19f 543
a2531bf2 544 $row->delete
545
546=over
547
548=item Arguments: none
7624b19f 549
a2531bf2 550=item Returns: The Row object
551
552=back
553
554Throws an exception if the object is not in the database according to
d6988be8 555L</in_storage>. Also throws an exception if a proper WHERE clause
556uniquely identifying the database row can not be constructed (see
557L<significance of primary keys|DBIx::Class::Manual::Intro/The Significance and Importance of Primary Keys>
558for more details).
a2531bf2 559
560The object is still perfectly usable, but L</in_storage> will
ea36f4e4 561now return 0 and the object must be reinserted using L</insert>
b6d347e0 562before it can be used to L</update> the row again.
a2531bf2 563
564If you delete an object in a class with a C<has_many> relationship, an
565attempt is made to delete all the related objects as well. To turn
566this behaviour off, pass C<< cascade_delete => 0 >> in the C<$attr>
567hashref of the relationship, see L<DBIx::Class::Relationship>. Any
568database-level cascade or restrict will take precedence over a
281e677e 569DBIx-Class-based cascading delete, since DBIx-Class B<deletes the
570main row first> and only then attempts to delete any remaining related
571rows.
a2531bf2 572
b1d16ffd 573If you delete an object within a txn_do() (see L<DBIx::Class::Storage/txn_do>)
574and the transaction subsequently fails, the row object will remain marked as
575not being in storage. If you know for a fact that the object is still in
576storage (i.e. by inspecting the cause of the transaction's failure), you can
577use C<< $obj->in_storage(1) >> to restore consistency between the object and
578the database. This would allow a subsequent C<< $obj->delete >> to work
579as expected.
580
a2531bf2 581See also L<DBIx::Class::ResultSet/delete>.
7624b19f 582
583=cut
584
585sub delete {
586 my $self = shift;
587 if (ref $self) {
701da8c4 588 $self->throw_exception( "Not in database" ) unless $self->in_storage;
cf856357 589
728e60a3 590 my $ident_cond = $self->{_orig_ident} || $self->ident_condition;
613a5b03 591 $self->throw_exception($self->{_orig_ident_failreason})
4b12b3c2 592 if ! keys %$ident_cond;
cf856357 593
88cb6a1d 594 $self->result_source->storage->delete(
cf856357 595 $self->result_source, $ident_cond
596 );
597
613a5b03 598 delete $self->{_orig_ident}; # no longer identifiable
7624b19f 599 $self->in_storage(undef);
cf856357 600 }
601 else {
701da8c4 602 $self->throw_exception("Can't do class delete without a ResultSource instance")
097d3227 603 unless $self->can('result_source_instance');
aeb1bf75 604 my $attrs = @_ > 1 && ref $_[$#_] eq 'HASH' ? { %{pop(@_)} } : {};
605 my $query = ref $_[0] eq 'HASH' ? $_[0] : {@_};
097d3227 606 $self->result_source_instance->resultset->search(@_)->delete;
7624b19f 607 }
608 return $self;
609}
610
8091aa91 611=head2 get_column
7624b19f 612
a2531bf2 613 my $val = $row->get_column($col);
614
615=over
616
617=item Arguments: $columnname
618
619=item Returns: The value of the column
620
621=back
622
623Throws an exception if the column name given doesn't exist according
624to L</has_column>.
7624b19f 625
e91e756c 626Returns a raw column value from the row object, if it has already
627been fetched from the database or set by an accessor.
628
629If an L<inflated value|DBIx::Class::InflateColumn> has been set, it
630will be deflated and returned.
7624b19f 631
ea36f4e4 632Note that if you used the C<columns> or the C<select/as>
633L<search attributes|DBIx::Class::ResultSet/ATTRIBUTES> on the resultset from
634which C<$row> was derived, and B<did not include> C<$columnname> in the list,
635this method will return C<undef> even if the database contains some value.
636
a2531bf2 637To retrieve all loaded column values as a hash, use L</get_columns>.
638
7624b19f 639=cut
640
641sub get_column {
642 my ($self, $column) = @_;
701da8c4 643 $self->throw_exception( "Can't fetch data as class method" ) unless ref $self;
aeb1bf75 644 return $self->{_column_data}{$column} if exists $self->{_column_data}{$column};
61a622ee 645 if (exists $self->{_inflated_column}{$column}) {
646 return $self->store_column($column,
b6d347e0 647 $self->_deflated_column($column, $self->{_inflated_column}{$column}));
61a622ee 648 }
701da8c4 649 $self->throw_exception( "No such column '${column}'" ) unless $self->has_column($column);
7624b19f 650 return undef;
651}
652
9b83fccd 653=head2 has_column_loaded
654
a2531bf2 655 if ( $row->has_column_loaded($col) ) {
9b83fccd 656 print "$col has been loaded from db";
657 }
658
a2531bf2 659=over
660
661=item Arguments: $columnname
662
663=item Returns: 0|1
664
665=back
666
9b83fccd 667Returns a true value if the column value has been loaded from the
668database (or set locally).
669
670=cut
671
def81720 672sub has_column_loaded {
673 my ($self, $column) = @_;
674 $self->throw_exception( "Can't call has_column data as class method" ) unless ref $self;
61a622ee 675 return 1 if exists $self->{_inflated_column}{$column};
aeb1bf75 676 return exists $self->{_column_data}{$column};
def81720 677}
678
8091aa91 679=head2 get_columns
076a6864 680
a2531bf2 681 my %data = $row->get_columns;
682
683=over
684
685=item Arguments: none
076a6864 686
a2531bf2 687=item Returns: A hash of columnname, value pairs.
688
689=back
690
691Returns all loaded column data as a hash, containing raw values. To
692get just one value for a particular column, use L</get_column>.
076a6864 693
c0a171bf 694See L</get_inflated_columns> to get the inflated values.
695
076a6864 696=cut
697
698sub get_columns {
699 my $self = shift;
61a622ee 700 if (exists $self->{_inflated_column}) {
701 foreach my $col (keys %{$self->{_inflated_column}}) {
702 $self->store_column($col, $self->_deflated_column($col, $self->{_inflated_column}{$col}))
c4a30d56 703 unless exists $self->{_column_data}{$col};
61a622ee 704 }
705 }
cb5f2eea 706 return %{$self->{_column_data}};
d7156e50 707}
708
709=head2 get_dirty_columns
710
a2531bf2 711 my %data = $row->get_dirty_columns;
712
713=over
714
715=item Arguments: none
d7156e50 716
a2531bf2 717=item Returns: A hash of column, value pairs
718
719=back
720
721Only returns the column, value pairs for those columns that have been
722changed on this object since the last L</update> or L</insert> call.
723
724See L</get_columns> to fetch all column/value pairs.
d7156e50 725
726=cut
727
728sub get_dirty_columns {
729 my $self = shift;
730 return map { $_ => $self->{_column_data}{$_} }
731 keys %{$self->{_dirty_columns}};
076a6864 732}
733
6dbea98e 734=head2 make_column_dirty
735
a2531bf2 736 $row->make_column_dirty($col)
737
738=over
739
740=item Arguments: $columnname
741
742=item Returns: undefined
743
744=back
745
746Throws an exception if the column does not exist.
747
748Marks a column as having been changed regardless of whether it has
b6d347e0 749really changed.
6dbea98e 750
751=cut
752sub make_column_dirty {
753 my ($self, $column) = @_;
754
755 $self->throw_exception( "No such column '${column}'" )
756 unless exists $self->{_column_data}{$column} || $self->has_column($column);
497d874a 757
b6d347e0 758 # the entire clean/dirty code relies on exists, not on true/false
497d874a 759 return 1 if exists $self->{_dirty_columns}{$column};
760
6dbea98e 761 $self->{_dirty_columns}{$column} = 1;
497d874a 762
763 # if we are just now making the column dirty, and if there is an inflated
764 # value, force it over the deflated one
765 if (exists $self->{_inflated_column}{$column}) {
766 $self->store_column($column,
767 $self->_deflated_column(
768 $column, $self->{_inflated_column}{$column}
769 )
770 );
771 }
6dbea98e 772}
773
ba4a6453 774=head2 get_inflated_columns
775
e91e756c 776 my %inflated_data = $obj->get_inflated_columns;
ba4a6453 777
a2531bf2 778=over
779
780=item Arguments: none
781
782=item Returns: A hash of column, object|value pairs
783
784=back
785
786Returns a hash of all column keys and associated values. Values for any
787columns set to use inflation will be inflated and returns as objects.
788
789See L</get_columns> to get the uninflated values.
790
791See L<DBIx::Class::InflateColumn> for how to setup inflation.
ba4a6453 792
793=cut
794
795sub get_inflated_columns {
796 my $self = shift;
d61b2132 797
798 my %loaded_colinfo = (map
799 { $_ => $self->column_info($_) }
800 (grep { $self->has_column_loaded($_) } $self->columns)
801 );
802
803 my %inflated;
804 for my $col (keys %loaded_colinfo) {
805 if (exists $loaded_colinfo{$col}{accessor}) {
806 my $acc = $loaded_colinfo{$col}{accessor};
9c042209 807 $inflated{$col} = $self->$acc if defined $acc;
d61b2132 808 }
809 else {
810 $inflated{$col} = $self->$col;
811 }
812 }
813
814 # return all loaded columns with the inflations overlayed on top
815 return ($self->get_columns, %inflated);
ba4a6453 816}
817
ca8a1270 818sub _is_column_numeric {
0bb1a52f 819 my ($self, $column) = @_;
820 my $colinfo = $self->column_info ($column);
821
822 # cache for speed (the object may *not* have a resultsource instance)
823 if (not defined $colinfo->{is_numeric} && $self->_source_handle) {
824 $colinfo->{is_numeric} =
825 $self->result_source->schema->storage->is_datatype_numeric ($colinfo->{data_type})
826 ? 1
827 : 0
828 ;
829 }
830
831 return $colinfo->{is_numeric};
832}
833
8091aa91 834=head2 set_column
7624b19f 835
a2531bf2 836 $row->set_column($col => $val);
837
838=over
839
840=item Arguments: $columnname, $value
841
842=item Returns: $value
843
844=back
7624b19f 845
e91e756c 846Sets a raw column value. If the new value is different from the old one,
a2531bf2 847the column is marked as dirty for when you next call L</update>.
7624b19f 848
ea36f4e4 849If passed an object or reference as a value, this method will happily
850attempt to store it, and a later L</insert> or L</update> will try and
a2531bf2 851stringify/numify as appropriate. To set an object to be deflated
852instead, see L</set_inflated_columns>.
e91e756c 853
7624b19f 854=cut
855
856sub set_column {
1d0057bd 857 my ($self, $column, $new_value) = @_;
858
cf856357 859 # if we can't get an ident condition on first try - mark the object as unidentifiable
613a5b03 860 # (by using an empty hashref) and store the error for further diag
861 unless ($self->{_orig_ident}) {
862 try {
863 $self->{_orig_ident} = $self->ident_condition
864 }
865 catch {
866 $self->{_orig_ident_failreason} = $_;
867 $self->{_orig_ident} = {};
868 };
869 }
1d0057bd 870
cf856357 871 my $old_value = $self->get_column($column);
b236052f 872 $new_value = $self->store_column($column, $new_value);
8f9eff75 873
cde96798 874 my $dirty =
875 $self->{_dirty_columns}{$column}
876 ||
877 $self->in_storage # no point tracking dirtyness on uninserted data
878 ? ! $self->_eq_column_values ($column, $old_value, $new_value)
879 : 1
880 ;
8f9eff75 881
cde96798 882 # FIXME sadly the update code just checks for keys, not for their value
8f9eff75 883 $self->{_dirty_columns}{$column} = 1 if $dirty;
e60dc79f 884
885 # XXX clear out the relation cache for this column
886 delete $self->{related_resultsets}{$column};
887
1d0057bd 888 return $new_value;
7624b19f 889}
890
cde96798 891sub _eq_column_values {
892 my ($self, $col, $old, $new) = @_;
893
894 if (defined $old xor defined $new) {
895 return 0;
896 }
897 elsif (not defined $old) { # both undef
898 return 1;
899 }
900 elsif ($old eq $new) {
901 return 1;
902 }
903 elsif ($self->_is_column_numeric($col)) { # do a numeric comparison if datatype allows it
904 return $old == $new;
905 }
906 else {
907 return 0;
908 }
909}
910
8091aa91 911=head2 set_columns
076a6864 912
a2531bf2 913 $row->set_columns({ $col => $val, ... });
914
b6d347e0 915=over
076a6864 916
a2531bf2 917=item Arguments: \%columndata
918
919=item Returns: The Row object
920
921=back
922
923Sets multiple column, raw value pairs at once.
924
925Works as L</set_column>.
076a6864 926
927=cut
928
929sub set_columns {
930 my ($self,$data) = @_;
a2ca474b 931 foreach my $col (keys %$data) {
932 $self->set_column($col,$data->{$col});
076a6864 933 }
c01ab172 934 return $self;
076a6864 935}
936
bacf6f12 937=head2 set_inflated_columns
938
a2531bf2 939 $row->set_inflated_columns({ $col => $val, $relname => $obj, ... });
940
941=over
942
943=item Arguments: \%columndata
944
945=item Returns: The Row object
946
947=back
948
949Sets more than one column value at once. Any inflated values are
b6d347e0 950deflated and the raw values stored.
bacf6f12 951
a2531bf2 952Any related values passed as Row objects, using the relation name as a
953key, are reduced to the appropriate foreign key values and stored. If
954instead of related row objects, a hashref of column, value data is
955passed, will create the related object first then store.
956
957Will even accept arrayrefs of data as a value to a
958L<DBIx::Class::Relationship/has_many> key, and create the related
959objects if necessary.
960
c1300297 961Be aware that the input hashref might be edited in place, so don't rely
a2531bf2 962on it being the same after a call to C<set_inflated_columns>. If you
963need to preserve the hashref, it is sufficient to pass a shallow copy
964to C<set_inflated_columns>, e.g. ( { %{ $href } } )
965
966See also L<DBIx::Class::Relationship::Base/set_from_related>.
bacf6f12 967
968=cut
969
970sub set_inflated_columns {
971 my ( $self, $upd ) = @_;
972 foreach my $key (keys %$upd) {
973 if (ref $upd->{$key}) {
974 my $info = $self->relationship_info($key);
b82c8a28 975 my $acc_type = $info->{attrs}{accessor} || '';
976 if ($acc_type eq 'single') {
bacf6f12 977 my $rel = delete $upd->{$key};
978 $self->set_from_related($key => $rel);
a7be8807 979 $self->{_relationship_data}{$key} = $rel;
bacf6f12 980 }
b82c8a28 981 elsif ($acc_type eq 'multi') {
982 $self->throw_exception(
983 "Recursive update is not supported over relationships of type '$acc_type' ($key)"
984 );
985 }
986 elsif ($self->has_column($key) && exists $self->column_info($key)->{_inflate_info}) {
a7be8807 987 $self->set_inflated_column($key, delete $upd->{$key});
bacf6f12 988 }
989 }
990 }
b6d347e0 991 $self->set_columns($upd);
bacf6f12 992}
993
8091aa91 994=head2 copy
076a6864 995
996 my $copy = $orig->copy({ change => $to, ... });
997
a2531bf2 998=over
999
1000=item Arguments: \%replacementdata
1001
1002=item Returns: The Row object copy
1003
1004=back
1005
1006Inserts a new row into the database, as a copy of the original
1007object. If a hashref of replacement data is supplied, these will take
ce0893e0 1008precedence over data in the original. Also any columns which have
1009the L<column info attribute|DBIx::Class::ResultSource/add_columns>
1010C<< is_auto_increment => 1 >> are explicitly removed before the copy,
1011so that the database can insert its own autoincremented values into
1012the new object.
a2531bf2 1013
f928c965 1014Relationships will be followed by the copy procedure B<only> if the
48580715 1015relationship specifies a true value for its
f928c965 1016L<cascade_copy|DBIx::Class::Relationship::Base> attribute. C<cascade_copy>
1017is set by default on C<has_many> relationships and unset on all others.
076a6864 1018
1019=cut
1020
c01ab172 1021sub copy {
1022 my ($self, $changes) = @_;
333cce60 1023 $changes ||= {};
fde6e28e 1024 my $col_data = { %{$self->{_column_data}} };
1025 foreach my $col (keys %$col_data) {
1026 delete $col_data->{$col}
1027 if $self->result_source->column_info($col)->{is_auto_increment};
1028 }
04786a4c 1029
1030 my $new = { _column_data => $col_data };
1031 bless $new, ref $self;
1032
83419ec6 1033 $new->result_source($self->result_source);
bacf6f12 1034 $new->set_inflated_columns($changes);
333cce60 1035 $new->insert;
35688220 1036
b6d347e0 1037 # Its possible we'll have 2 relations to the same Source. We need to make
48580715 1038 # sure we don't try to insert the same row twice else we'll violate unique
35688220 1039 # constraints
1040 my $rels_copied = {};
1041
333cce60 1042 foreach my $rel ($self->result_source->relationships) {
1043 my $rel_info = $self->result_source->relationship_info($rel);
35688220 1044
1045 next unless $rel_info->{attrs}{cascade_copy};
b6d347e0 1046
6d0ee587 1047 my $resolved = $self->result_source->_resolve_condition(
35688220 1048 $rel_info->{cond}, $rel, $new
1049 );
1050
1051 my $copied = $rels_copied->{ $rel_info->{source} } ||= {};
1052 foreach my $related ($self->search_related($rel)) {
1053 my $id_str = join("\0", $related->id);
1054 next if $copied->{$id_str};
1055 $copied->{$id_str} = 1;
1056 my $rel_copy = $related->copy($resolved);
333cce60 1057 }
b6d347e0 1058
333cce60 1059 }
2c4c67b6 1060 return $new;
c01ab172 1061}
1062
8091aa91 1063=head2 store_column
7624b19f 1064
a2531bf2 1065 $row->store_column($col => $val);
7624b19f 1066
a2531bf2 1067=over
1068
1069=item Arguments: $columnname, $value
1070
ea36f4e4 1071=item Returns: The value sent to storage
a2531bf2 1072
1073=back
1074
1075Set a raw value for a column without marking it as changed. This
1076method is used internally by L</set_column> which you should probably
1077be using.
1078
1079This is the lowest level at which data is set on a row object,
1080extend this method to catch all data setting methods.
7624b19f 1081
1082=cut
1083
1084sub store_column {
1085 my ($self, $column, $value) = @_;
75d07914 1086 $self->throw_exception( "No such column '${column}'" )
d7156e50 1087 unless exists $self->{_column_data}{$column} || $self->has_column($column);
75d07914 1088 $self->throw_exception( "set_column called for ${column} without value" )
7624b19f 1089 if @_ < 3;
1090 return $self->{_column_data}{$column} = $value;
1091}
1092
b52e9bf8 1093=head2 inflate_result
1094
c01ab172 1095 Class->inflate_result($result_source, \%me, \%prefetch?)
b52e9bf8 1096
a2531bf2 1097=over
1098
d4a20f38 1099=item Arguments: $result_source, \%columndata, \%prefetcheddata
a2531bf2 1100
1101=item Returns: A Row object
1102
1103=back
1104
1105All L<DBIx::Class::ResultSet> methods that retrieve data from the
1106database and turn it into row objects call this method.
1107
1108Extend this method in your Result classes to hook into this process,
1109for example to rebless the result into a different class.
1110
1111Reblessing can also be done more easily by setting C<result_class> in
1112your Result class. See L<DBIx::Class::ResultSource/result_class>.
b52e9bf8 1113
db2b2eb6 1114Different types of results can also be created from a particular
1115L<DBIx::Class::ResultSet>, see L<DBIx::Class::ResultSet/result_class>.
1116
b52e9bf8 1117=cut
1118
1119sub inflate_result {
d4a20f38 1120 my ($class, $source, $me, $prefetch) = @_;
1121
aec3eff1 1122 my ($source_handle) = $source;
1123
1124 if ($source->isa('DBIx::Class::ResultSourceHandle')) {
13d06949 1125 $source = $source_handle->resolve
fd323bf1 1126 }
13d06949 1127 else {
1128 $source_handle = $source->handle
aec3eff1 1129 }
1130
04786a4c 1131 my $new = {
aec3eff1 1132 _source_handle => $source_handle,
04786a4c 1133 _column_data => $me,
04786a4c 1134 };
1135 bless $new, (ref $class || $class);
1136
64acc2bc 1137 foreach my $pre (keys %{$prefetch||{}}) {
35c77aa3 1138
13d06949 1139 my $pre_source = $source->related_source($pre)
1140 or $class->throw_exception("Can't prefetch non-existent relationship ${pre}");
35c77aa3 1141
13d06949 1142 my $accessor = $source->relationship_info($pre)->{attrs}{accessor}
1143 or $class->throw_exception("No accessor for prefetched $pre");
35c77aa3 1144
13d06949 1145 my @pre_vals;
1146 if (ref $prefetch->{$pre}[0] eq 'ARRAY') {
1147 @pre_vals = @{$prefetch->{$pre}};
1148 }
1149 elsif ($accessor eq 'multi') {
1150 $class->throw_exception("Implicit prefetch (via select/columns) not supported with accessor 'multi'");
1151 }
1152 else {
1153 @pre_vals = $prefetch->{$pre};
1154 }
1155
1156 my @pre_objects;
1157 for my $me_pref (@pre_vals) {
1158
1159 # FIXME - this should not be necessary
35c77aa3 1160 # the collapser currently *could* return bogus elements with all
1161 # columns set to undef
1162 my $has_def;
1163 for (values %{$me_pref->[0]}) {
1164 if (defined $_) {
1165 $has_def++;
1166 last;
1167 }
a86b1efe 1168 }
35c77aa3 1169 next unless $has_def;
1170
1171 push @pre_objects, $pre_source->result_class->inflate_result(
1172 $pre_source, @$me_pref
1173 );
13d06949 1174 }
35c77aa3 1175
13d06949 1176 if ($accessor eq 'single') {
1177 $new->{_relationship_data}{$pre} = $pre_objects[0];
b52e9bf8 1178 }
13d06949 1179 elsif ($accessor eq 'filter') {
1180 $new->{_inflated_column}{$pre} = $pre_objects[0];
1181 }
1182
1183 $new->related_resultset($pre)->set_cache(\@pre_objects);
b52e9bf8 1184 }
35c77aa3 1185
1186 $new->in_storage (1);
7624b19f 1187 return $new;
1188}
1189
9b465d00 1190=head2 update_or_insert
7624b19f 1191
a2531bf2 1192 $row->update_or_insert
1193
1194=over
7624b19f 1195
a2531bf2 1196=item Arguments: none
1197
1198=item Returns: Result of update or insert operation
1199
1200=back
1201
1202L</Update>s the object if it's already in the database, according to
1203L</in_storage>, else L</insert>s it.
7624b19f 1204
9b83fccd 1205=head2 insert_or_update
1206
1207 $obj->insert_or_update
1208
1209Alias for L</update_or_insert>
1210
7624b19f 1211=cut
1212
370f2ba2 1213sub insert_or_update { shift->update_or_insert(@_) }
1214
9b465d00 1215sub update_or_insert {
7624b19f 1216 my $self = shift;
1217 return ($self->in_storage ? $self->update : $self->insert);
1218}
1219
8091aa91 1220=head2 is_changed
7624b19f 1221
a2531bf2 1222 my @changed_col_names = $row->is_changed();
1223 if ($row->is_changed()) { ... }
1224
1225=over
7624b19f 1226
a2531bf2 1227=item Arguments: none
1228
1229=item Returns: 0|1 or @columnnames
1230
1231=back
1232
1233In list context returns a list of columns with uncommited changes, or
9b83fccd 1234in scalar context returns a true value if there are uncommitted
1235changes.
1236
7624b19f 1237=cut
1238
1239sub is_changed {
1240 return keys %{shift->{_dirty_columns} || {}};
1241}
228dbcb4 1242
1243=head2 is_column_changed
1244
a2531bf2 1245 if ($row->is_column_changed('col')) { ... }
1246
1247=over
1248
1249=item Arguments: $columname
1250
1251=item Returns: 0|1
1252
1253=back
228dbcb4 1254
9b83fccd 1255Returns a true value if the column has uncommitted changes.
1256
228dbcb4 1257=cut
1258
1259sub is_column_changed {
1260 my( $self, $col ) = @_;
1261 return exists $self->{_dirty_columns}->{$col};
1262}
7624b19f 1263
097d3227 1264=head2 result_source
1265
a2531bf2 1266 my $resultsource = $row->result_source;
1267
1268=over
1269
1270=item Arguments: none
097d3227 1271
a2531bf2 1272=item Returns: a ResultSource instance
1273
1274=back
1275
1276Accessor to the L<DBIx::Class::ResultSource> this object was created from.
87c4e602 1277
aec3eff1 1278=cut
1279
1280sub result_source {
1281 my $self = shift;
1282
1283 if (@_) {
1284 $self->_source_handle($_[0]->handle);
1285 } else {
1286 $self->_source_handle->resolve;
1287 }
1288}
1289
9b83fccd 1290=head2 register_column
27f01d1f 1291
9b83fccd 1292 $column_info = { .... };
1293 $class->register_column($column_name, $column_info);
27f01d1f 1294
a2531bf2 1295=over
1296
1297=item Arguments: $columnname, \%columninfo
1298
1299=item Returns: undefined
1300
1301=back
1302
9b83fccd 1303Registers a column on the class. If the column_info has an 'accessor'
1304key, creates an accessor named after the value if defined; if there is
1305no such key, creates an accessor with the same name as the column
1f23a877 1306
9b83fccd 1307The column_info attributes are described in
1308L<DBIx::Class::ResultSource/add_columns>
1f23a877 1309
097d3227 1310=cut
1311
1f23a877 1312sub register_column {
1313 my ($class, $col, $info) = @_;
91b0fbd7 1314 my $acc = $col;
1315 if (exists $info->{accessor}) {
1316 return unless defined $info->{accessor};
1317 $acc = [ $info->{accessor}, $col ];
1318 }
1319 $class->mk_group_accessors('column' => $acc);
1f23a877 1320}
1321
a2531bf2 1322=head2 get_from_storage
1323
1324 my $copy = $row->get_from_storage($attrs)
1325
1326=over
b9b4e52f 1327
a2531bf2 1328=item Arguments: \%attrs
b9b4e52f 1329
a2531bf2 1330=item Returns: A Row object
1331
1332=back
1333
1334Fetches a fresh copy of the Row object from the database and returns it.
d6988be8 1335Throws an exception if a proper WHERE clause identifying the database row
1336can not be constructed (i.e. if the original object does not contain its
1337entire
1338 L<primary key|DBIx::Class::Manual::Intro/The Significance and Importance of Primary Keys>
1339). If passed the \%attrs argument, will first apply these attributes to
a2531bf2 1340the resultset used to find the row.
1341
1342This copy can then be used to compare to an existing row object, to
1343determine if any changes have been made in the database since it was
1344created.
1345
1346To just update your Row object with any latest changes from the
1347database, use L</discard_changes> instead.
1348
1349The \%attrs argument should be compatible with
1350L<DBIx::Class::ResultSet/ATTRIBUTES>.
7e38d850 1351
b9b4e52f 1352=cut
1353
a737512c 1354sub get_from_storage {
b9b4e52f 1355 my $self = shift @_;
7e38d850 1356 my $attrs = shift @_;
7e38d850 1357 my $resultset = $self->result_source->resultset;
b6d347e0 1358
7e38d850 1359 if(defined $attrs) {
bbd107cf 1360 $resultset = $resultset->search(undef, $attrs);
7e38d850 1361 }
b6d347e0 1362
cf856357 1363 my $ident_cond = $self->{_orig_ident} || $self->ident_condition;
1364
613a5b03 1365 $self->throw_exception($self->{_orig_ident_failreason})
cf856357 1366 if ! keys %$ident_cond;
1367
1368 return $resultset->find($ident_cond);
b9b4e52f 1369}
701da8c4 1370
bbd107cf 1371=head2 discard_changes ($attrs)
1372
1373Re-selects the row from the database, losing any changes that had
d6988be8 1374been made. Throws an exception if a proper WHERE clause identifying
1375the database row can not be constructed (i.e. if the original object
1376does not contain its entire
1377L<primary key|DBIx::Class::Manual::Intro/The Significance and Importance of Primary Keys>
1378).
bbd107cf 1379
1380This method can also be used to refresh from storage, retrieving any
1381changes made since the row was last read from storage.
1382
1383$attrs is expected to be a hashref of attributes suitable for passing as the
1384second argument to $resultset->search($cond, $attrs);
1385
1386=cut
1387
1388sub discard_changes {
1389 my ($self, $attrs) = @_;
bbd107cf 1390 return unless $self->in_storage; # Don't reload if we aren't real!
1391
1392 # add a replication default to read from the master only
1393 $attrs = { force_pool => 'master', %{$attrs||{}} };
1394
1395 if( my $current_storage = $self->get_from_storage($attrs)) {
1396
1397 # Set $self to the current.
1398 %$self = %$current_storage;
1399
1400 # Avoid a possible infinite loop with
1401 # sub DESTROY { $_[0]->discard_changes }
1402 bless $current_storage, 'Do::Not::Exist';
1403
1404 return $self;
1405 }
1406 else {
1407 $self->in_storage(0);
1408 return $self;
1409 }
1410}
1411
1412
5160b401 1413=head2 throw_exception
701da8c4 1414
a2531bf2 1415See L<DBIx::Class::Schema/throw_exception>.
701da8c4 1416
1417=cut
1418
1419sub throw_exception {
1420 my $self=shift;
1a58752c 1421
66cab05c 1422 if (ref $self && ref $self->result_source && $self->result_source->schema) {
1a58752c 1423 $self->result_source->schema->throw_exception(@_)
1424 }
1425 else {
1426 DBIx::Class::Exception->throw(@_);
701da8c4 1427 }
1428}
1429
33cf6616 1430=head2 id
1431
a2531bf2 1432 my @pk = $row->id;
1433
1434=over
1435
1436=item Arguments: none
1437
1438=item Returns: A list of primary key values
1439
1440=back
1441
33cf6616 1442Returns the primary key(s) for a row. Can't be called as a class method.
f7043881 1443Actually implemented in L<DBIx::Class::PK>
33cf6616 1444
1445=head2 discard_changes
1446
a2531bf2 1447 $row->discard_changes
1448
1449=over
1450
1451=item Arguments: none
1452
1453=item Returns: nothing (updates object in-place)
1454
1455=back
1456
1457Retrieves and sets the row object data from the database, losing any
1458local changes made.
33cf6616 1459
1460This method can also be used to refresh from storage, retrieving any
1461changes made since the row was last read from storage. Actually
f7043881 1462implemented in L<DBIx::Class::PK>
33cf6616 1463
071bbccb 1464Note: If you are using L<DBIx::Class::Storage::DBI::Replicated> as your
1465storage, please kept in mind that if you L</discard_changes> on a row that you
1466just updated or created, you should wrap the entire bit inside a transaction.
1467Otherwise you run the risk that you insert or update to the master database
1468but read from a replicant database that has not yet been updated from the
1469master. This will result in unexpected results.
1470
33cf6616 1471=cut
1472
7624b19f 14731;
1474
7624b19f 1475=head1 AUTHORS
1476
daec44b8 1477Matt S. Trout <mst@shadowcatsystems.co.uk>
7624b19f 1478
1479=head1 LICENSE
1480
1481You may distribute this code under the same terms as Perl itself.
1482
1483=cut