Merge 'DBIx-Class-current' into 'param_bind'
[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/;
1edd1722 8
aec3eff1 9__PACKAGE__->mk_group_accessors('simple' => qw/_source_handle/);
8c49f629 10
75d07914 11=head1 NAME
7624b19f 12
13DBIx::Class::Row - Basic row methods
14
15=head1 SYNOPSIS
16
17=head1 DESCRIPTION
18
19This class is responsible for defining and doing basic operations on rows
1ea77c14 20derived from L<DBIx::Class::ResultSource> objects.
7624b19f 21
22=head1 METHODS
23
8091aa91 24=head2 new
7624b19f 25
26 my $obj = My::Class->new($attrs);
27
28Creates a new row object from column => value mappings passed as a hash ref
29
30=cut
31
32sub new {
61354236 33 my ($class, $attrs, $source) = @_;
7624b19f 34 $class = ref $class if ref $class;
04786a4c 35
36 my $new = { _column_data => {} };
37 bless $new, $class;
38
a6a280b9 39 $new->_source_handle($source) if $source;
40
7624b19f 41 if ($attrs) {
27f01d1f 42 $new->throw_exception("attrs must be a hashref")
43 unless ref($attrs) eq 'HASH';
61a622ee 44
45 my ($related,$inflated);
46 foreach my $key (keys %$attrs) {
47 if (ref $attrs->{$key}) {
48 my $info = $class->relationship_info($key);
49 if ($info && $info->{attrs}{accessor}
c4a30d56 50 && $info->{attrs}{accessor} eq 'single')
61a622ee 51 {
52 $new->set_from_related($key, $attrs->{$key});
53 $related->{$key} = $attrs->{$key};
54 next;
55 }
56 elsif ($class->has_column($key)
57 && exists $class->column_info($key)->{_inflate_info})
58 {
59 $inflated->{$key} = $attrs->{$key};
60 next;
61 }
62 }
63 $new->throw_exception("No such column $key on $class")
64 unless $class->has_column($key);
65 $new->store_column($key => $attrs->{$key});
7624b19f 66 }
096f4212 67 if (my $source = delete $attrs->{-result_source}) {
68 $new->result_source($source);
69 }
f90375dd 70
61a622ee 71 $new->{_relationship_data} = $related if $related;
72 $new->{_inflated_column} = $inflated if $inflated;
7624b19f 73 }
04786a4c 74
7624b19f 75 return $new;
76}
77
8091aa91 78=head2 insert
7624b19f 79
80 $obj->insert;
81
b8810cc5 82Inserts an object into the database if it isn't already in
83there. Returns the object itself. Requires the object's result source to
84be set, or the class to have a result_source_instance method. To insert
85an entirely new object into the database, use C<create> (see
86L<DBIx::Class::ResultSet/create>).
7624b19f 87
88=cut
89
90sub insert {
91 my ($self) = @_;
92 return $self if $self->in_storage;
6aba697f 93 my $source = $self->result_source;
94 $source ||= $self->result_source($self->result_source_instance)
097d3227 95 if $self->can('result_source_instance');
aeb1bf75 96 $self->throw_exception("No result_source set on this object; can't insert")
97 unless $source;
6e399b4f 98
7af8b477 99 $source->storage->insert($source, { $self->get_columns });
7624b19f 100 $self->in_storage(1);
101 $self->{_dirty_columns} = {};
64acc2bc 102 $self->{related_resultsets} = {};
729b29ae 103 undef $self->{_orig_ident};
7624b19f 104 return $self;
105}
106
8091aa91 107=head2 in_storage
7624b19f 108
109 $obj->in_storage; # Get value
110 $obj->in_storage(1); # Set value
111
112Indicated whether the object exists as a row in the database or not
113
114=cut
115
116sub in_storage {
117 my ($self, $val) = @_;
118 $self->{_in_storage} = $val if @_ > 1;
119 return $self->{_in_storage};
120}
121
8091aa91 122=head2 update
7624b19f 123
124 $obj->update;
125
126Must be run on an object that is already in the database; issues an SQL
d3b0e369 127UPDATE query to commit any changes to the object to the database if
128required.
7624b19f 129
130=cut
131
132sub update {
133 my ($self, $upd) = @_;
701da8c4 134 $self->throw_exception( "Not in database" ) unless $self->in_storage;
4b12b3c2 135 my $ident_cond = $self->ident_condition;
136 $self->throw_exception("Cannot safely update a row in a PK-less table")
137 if ! keys %$ident_cond;
6e399b4f 138
61a622ee 139 if ($upd) {
140 foreach my $key (keys %$upd) {
141 if (ref $upd->{$key}) {
142 my $info = $self->relationship_info($key);
143 if ($info && $info->{attrs}{accessor}
144 && $info->{attrs}{accessor} eq 'single')
145 {
146 my $rel = delete $upd->{$key};
147 $self->set_from_related($key => $rel);
148 $self->{_relationship_data}{$key} = $rel;
149 }
150 elsif ($self->has_column($key)
151 && exists $self->column_info($key)->{_inflate_info})
152 {
153 $self->set_inflated_column($key, delete $upd->{$key});
154 }
155 }
156 }
157 $self->set_columns($upd);
158 }
5a9e0e60 159 my %to_update = $self->get_dirty_columns;
160 return $self unless keys %to_update;
88cb6a1d 161 my $rows = $self->result_source->storage->update(
f4afcd5d 162 $self->result_source, \%to_update,
163 $self->{_orig_ident} || $ident_cond
164 );
7624b19f 165 if ($rows == 0) {
701da8c4 166 $self->throw_exception( "Can't update ${self}: row not found" );
7624b19f 167 } elsif ($rows > 1) {
701da8c4 168 $self->throw_exception("Can't update ${self}: updated more than one row");
7624b19f 169 }
170 $self->{_dirty_columns} = {};
64acc2bc 171 $self->{related_resultsets} = {};
729b29ae 172 undef $self->{_orig_ident};
7624b19f 173 return $self;
174}
175
8091aa91 176=head2 delete
7624b19f 177
178 $obj->delete
179
b8810cc5 180Deletes the object from the database. The object is still perfectly
61a622ee 181usable, but C<< ->in_storage() >> will now return 0 and the object must
182reinserted using C<< ->insert() >> before C<< ->update() >> can be used
b8810cc5 183on it. If you delete an object in a class with a C<has_many>
184relationship, all the related objects will be deleted as well. To turn
185this behavior off, pass C<cascade_delete => 0> in the C<$attr>
186hashref. Any database-level cascade or restrict will take precedence
187over a DBIx-Class-based cascading delete. See also L<DBIx::Class::ResultSet/delete>.
7624b19f 188
189=cut
190
191sub delete {
192 my $self = shift;
193 if (ref $self) {
701da8c4 194 $self->throw_exception( "Not in database" ) unless $self->in_storage;
4b12b3c2 195 my $ident_cond = $self->ident_condition;
196 $self->throw_exception("Cannot safely delete a row in a PK-less table")
197 if ! keys %$ident_cond;
e0f56292 198 foreach my $column (keys %$ident_cond) {
75d07914 199 $self->throw_exception("Can't delete the object unless it has loaded the primary keys")
200 unless exists $self->{_column_data}{$column};
e0f56292 201 }
88cb6a1d 202 $self->result_source->storage->delete(
7af8b477 203 $self->result_source, $ident_cond);
7624b19f 204 $self->in_storage(undef);
7624b19f 205 } else {
701da8c4 206 $self->throw_exception("Can't do class delete without a ResultSource instance")
097d3227 207 unless $self->can('result_source_instance');
aeb1bf75 208 my $attrs = @_ > 1 && ref $_[$#_] eq 'HASH' ? { %{pop(@_)} } : {};
209 my $query = ref $_[0] eq 'HASH' ? $_[0] : {@_};
097d3227 210 $self->result_source_instance->resultset->search(@_)->delete;
7624b19f 211 }
212 return $self;
213}
214
8091aa91 215=head2 get_column
7624b19f 216
217 my $val = $obj->get_column($col);
218
61a622ee 219Gets a column value from a row object. Does not do any queries; the column
220must have already been fetched from the database and stored in the object. If
221there is an inflated value stored that has not yet been deflated, it is deflated
222when the method is invoked.
7624b19f 223
224=cut
225
226sub get_column {
227 my ($self, $column) = @_;
701da8c4 228 $self->throw_exception( "Can't fetch data as class method" ) unless ref $self;
aeb1bf75 229 return $self->{_column_data}{$column} if exists $self->{_column_data}{$column};
61a622ee 230 if (exists $self->{_inflated_column}{$column}) {
231 return $self->store_column($column,
232 $self->_deflated_column($column, $self->{_inflated_column}{$column}));
233 }
701da8c4 234 $self->throw_exception( "No such column '${column}'" ) unless $self->has_column($column);
7624b19f 235 return undef;
236}
237
9b83fccd 238=head2 has_column_loaded
239
240 if ( $obj->has_column_loaded($col) ) {
241 print "$col has been loaded from db";
242 }
243
244Returns a true value if the column value has been loaded from the
245database (or set locally).
246
247=cut
248
def81720 249sub has_column_loaded {
250 my ($self, $column) = @_;
251 $self->throw_exception( "Can't call has_column data as class method" ) unless ref $self;
61a622ee 252 return 1 if exists $self->{_inflated_column}{$column};
aeb1bf75 253 return exists $self->{_column_data}{$column};
def81720 254}
255
8091aa91 256=head2 get_columns
076a6864 257
258 my %data = $obj->get_columns;
259
8091aa91 260Does C<get_column>, for all column values at once.
076a6864 261
262=cut
263
264sub get_columns {
265 my $self = shift;
61a622ee 266 if (exists $self->{_inflated_column}) {
267 foreach my $col (keys %{$self->{_inflated_column}}) {
268 $self->store_column($col, $self->_deflated_column($col, $self->{_inflated_column}{$col}))
c4a30d56 269 unless exists $self->{_column_data}{$col};
61a622ee 270 }
271 }
cb5f2eea 272 return %{$self->{_column_data}};
d7156e50 273}
274
275=head2 get_dirty_columns
276
277 my %data = $obj->get_dirty_columns;
278
279Identical to get_columns but only returns those that have been changed.
280
281=cut
282
283sub get_dirty_columns {
284 my $self = shift;
285 return map { $_ => $self->{_column_data}{$_} }
286 keys %{$self->{_dirty_columns}};
076a6864 287}
288
8091aa91 289=head2 set_column
7624b19f 290
291 $obj->set_column($col => $val);
292
8091aa91 293Sets a column value. If the new value is different from the old one,
294the column is marked as dirty for when you next call $obj->update.
7624b19f 295
296=cut
297
298sub set_column {
299 my $self = shift;
300 my ($column) = @_;
729b29ae 301 $self->{_orig_ident} ||= $self->ident_condition;
7624b19f 302 my $old = $self->get_column($column);
303 my $ret = $self->store_column(@_);
87772e46 304 $self->{_dirty_columns}{$column} = 1
305 if (defined $old ^ defined $ret) || (defined $old && $old ne $ret);
7624b19f 306 return $ret;
307}
308
8091aa91 309=head2 set_columns
076a6864 310
dc818523 311 my $copy = $orig->set_columns({ $col => $val, ... });
076a6864 312
8091aa91 313Sets more than one column value at once.
076a6864 314
315=cut
316
317sub set_columns {
318 my ($self,$data) = @_;
a2ca474b 319 foreach my $col (keys %$data) {
320 $self->set_column($col,$data->{$col});
076a6864 321 }
c01ab172 322 return $self;
076a6864 323}
324
8091aa91 325=head2 copy
076a6864 326
327 my $copy = $orig->copy({ change => $to, ... });
328
8091aa91 329Inserts a new row with the specified changes.
076a6864 330
331=cut
332
c01ab172 333sub copy {
334 my ($self, $changes) = @_;
333cce60 335 $changes ||= {};
fde6e28e 336 my $col_data = { %{$self->{_column_data}} };
337 foreach my $col (keys %$col_data) {
338 delete $col_data->{$col}
339 if $self->result_source->column_info($col)->{is_auto_increment};
340 }
04786a4c 341
342 my $new = { _column_data => $col_data };
343 bless $new, ref $self;
344
83419ec6 345 $new->result_source($self->result_source);
ecd1f408 346 $new->set_columns($changes);
333cce60 347 $new->insert;
348 foreach my $rel ($self->result_source->relationships) {
349 my $rel_info = $self->result_source->relationship_info($rel);
350 if ($rel_info->{attrs}{cascade_copy}) {
351 my $resolved = $self->result_source->resolve_condition(
352 $rel_info->{cond}, $rel, $new);
353 foreach my $related ($self->search_related($rel)) {
354 $related->copy($resolved);
355 }
356 }
357 }
2c4c67b6 358 return $new;
c01ab172 359}
360
8091aa91 361=head2 store_column
7624b19f 362
363 $obj->store_column($col => $val);
364
8091aa91 365Sets a column value without marking it as dirty.
7624b19f 366
367=cut
368
369sub store_column {
370 my ($self, $column, $value) = @_;
75d07914 371 $self->throw_exception( "No such column '${column}'" )
d7156e50 372 unless exists $self->{_column_data}{$column} || $self->has_column($column);
75d07914 373 $self->throw_exception( "set_column called for ${column} without value" )
7624b19f 374 if @_ < 3;
375 return $self->{_column_data}{$column} = $value;
376}
377
b52e9bf8 378=head2 inflate_result
379
c01ab172 380 Class->inflate_result($result_source, \%me, \%prefetch?)
b52e9bf8 381
382Called by ResultSet to inflate a result from storage
383
384=cut
385
386sub inflate_result {
c01ab172 387 my ($class, $source, $me, $prefetch) = @_;
aec3eff1 388
389 my ($source_handle) = $source;
390
391 if ($source->isa('DBIx::Class::ResultSourceHandle')) {
392 $source = $source_handle->resolve
393 } else {
394 $source_handle = $source->handle
395 }
396
04786a4c 397 my $new = {
aec3eff1 398 _source_handle => $source_handle,
04786a4c 399 _column_data => $me,
400 _in_storage => 1
401 };
402 bless $new, (ref $class || $class);
403
7fb16f1a 404 my $schema;
64acc2bc 405 foreach my $pre (keys %{$prefetch||{}}) {
406 my $pre_val = $prefetch->{$pre};
f9cc31dd 407 my $pre_source = $source->related_source($pre);
a86b1efe 408 $class->throw_exception("Can't prefetch non-existent relationship ${pre}")
409 unless $pre_source;
0f66a01b 410 if (ref($pre_val->[0]) eq 'ARRAY') { # multi
a86b1efe 411 my @pre_objects;
412 foreach my $pre_rec (@$pre_val) {
75d07914 413 unless ($pre_source->primary_columns == grep { exists $pre_rec->[0]{$_}
5a5bec6c 414 and defined $pre_rec->[0]{$_} } $pre_source->primary_columns) {
a86b1efe 415 next;
416 }
417 push(@pre_objects, $pre_source->result_class->inflate_result(
418 $pre_source, @{$pre_rec}));
419 }
420 $new->related_resultset($pre)->set_cache(\@pre_objects);
62e87ea8 421 } elsif (defined $pre_val->[0]) {
a86b1efe 422 my $fetched;
75d07914 423 unless ($pre_source->primary_columns == grep { exists $pre_val->[0]{$_}
a86b1efe 424 and !defined $pre_val->[0]{$_} } $pre_source->primary_columns)
425 {
426 $fetched = $pre_source->result_class->inflate_result(
75d07914 427 $pre_source, @{$pre_val});
a86b1efe 428 }
429 my $accessor = $source->relationship_info($pre)->{attrs}{accessor};
430 $class->throw_exception("No accessor for prefetched $pre")
431 unless defined $accessor;
432 if ($accessor eq 'single') {
433 $new->{_relationship_data}{$pre} = $fetched;
434 } elsif ($accessor eq 'filter') {
435 $new->{_inflated_column}{$pre} = $fetched;
436 } else {
437 $class->throw_exception("Prefetch not supported with accessor '$accessor'");
438 }
b52e9bf8 439 }
440 }
7624b19f 441 return $new;
442}
443
9b465d00 444=head2 update_or_insert
7624b19f 445
9b465d00 446 $obj->update_or_insert
7624b19f 447
8091aa91 448Updates the object if it's already in the db, else inserts it.
7624b19f 449
9b83fccd 450=head2 insert_or_update
451
452 $obj->insert_or_update
453
454Alias for L</update_or_insert>
455
7624b19f 456=cut
457
9b465d00 458*insert_or_update = \&update_or_insert;
459sub update_or_insert {
7624b19f 460 my $self = shift;
461 return ($self->in_storage ? $self->update : $self->insert);
462}
463
8091aa91 464=head2 is_changed
7624b19f 465
228dbcb4 466 my @changed_col_names = $obj->is_changed();
467 if ($obj->is_changed()) { ... }
7624b19f 468
9b83fccd 469In array context returns a list of columns with uncommited changes, or
470in scalar context returns a true value if there are uncommitted
471changes.
472
7624b19f 473=cut
474
475sub is_changed {
476 return keys %{shift->{_dirty_columns} || {}};
477}
228dbcb4 478
479=head2 is_column_changed
480
481 if ($obj->is_column_changed('col')) { ... }
482
9b83fccd 483Returns a true value if the column has uncommitted changes.
484
228dbcb4 485=cut
486
487sub is_column_changed {
488 my( $self, $col ) = @_;
489 return exists $self->{_dirty_columns}->{$col};
490}
7624b19f 491
097d3227 492=head2 result_source
493
9b83fccd 494 my $resultsource = $object->result_source;
097d3227 495
9b83fccd 496Accessor to the ResultSource this object was created from
87c4e602 497
aec3eff1 498=cut
499
500sub result_source {
501 my $self = shift;
502
503 if (@_) {
504 $self->_source_handle($_[0]->handle);
505 } else {
506 $self->_source_handle->resolve;
507 }
508}
509
9b83fccd 510=head2 register_column
27f01d1f 511
9b83fccd 512 $column_info = { .... };
513 $class->register_column($column_name, $column_info);
27f01d1f 514
9b83fccd 515Registers a column on the class. If the column_info has an 'accessor'
516key, creates an accessor named after the value if defined; if there is
517no such key, creates an accessor with the same name as the column
1f23a877 518
9b83fccd 519The column_info attributes are described in
520L<DBIx::Class::ResultSource/add_columns>
1f23a877 521
097d3227 522=cut
523
1f23a877 524sub register_column {
525 my ($class, $col, $info) = @_;
91b0fbd7 526 my $acc = $col;
527 if (exists $info->{accessor}) {
528 return unless defined $info->{accessor};
529 $acc = [ $info->{accessor}, $col ];
530 }
531 $class->mk_group_accessors('column' => $acc);
1f23a877 532}
533
701da8c4 534
5160b401 535=head2 throw_exception
701da8c4 536
537See Schema's throw_exception.
538
539=cut
540
541sub throw_exception {
542 my $self=shift;
543 if (ref $self && ref $self->result_source) {
544 $self->result_source->schema->throw_exception(@_);
545 } else {
546 croak(@_);
547 }
548}
549
7624b19f 5501;
551
7624b19f 552=head1 AUTHORS
553
daec44b8 554Matt S. Trout <mst@shadowcatsystems.co.uk>
7624b19f 555
556=head1 LICENSE
557
558You may distribute this code under the same terms as Perl itself.
559
560=cut