1 package DBIx::Class::Row;
6 use base qw/DBIx::Class/;
7 use Carp::Clan qw/^DBIx::Class/;
9 __PACKAGE__->load_components(qw/AccessorGroup/);
11 __PACKAGE__->mk_group_accessors('simple' => 'result_source');
15 DBIx::Class::Row - Basic row methods
21 This class is responsible for defining and doing basic operations on rows
22 derived from L<DBIx::Class::ResultSource> objects.
28 my $obj = My::Class->new($attrs);
30 Creates a new row object from column => value mappings passed as a hash ref
35 my ($class, $attrs) = @_;
36 $class = ref $class if ref $class;
37 my $new = bless { _column_data => {} }, $class;
39 $new->throw_exception("attrs must be a hashref")
40 unless ref($attrs) eq 'HASH';
41 foreach my $k (keys %$attrs) {
42 $new->throw_exception("No such column $k on $class")
43 unless $class->has_column($k);
44 $new->store_column($k => $attrs->{$k});
54 Inserts an object into the database if it isn't already in there. Returns
55 the object itself. Requires the object's result source to be set, or the
56 class to have a result_source_instance method.
62 return $self if $self->in_storage;
63 $self->{result_source} ||= $self->result_source_instance
64 if $self->can('result_source_instance');
65 my $source = $self->{result_source};
66 $self->throw_exception("No result_source set on this object; can't insert")
68 #use Data::Dumper; warn Dumper($self);
69 $source->storage->insert($source->from, { $self->get_columns });
71 $self->{_dirty_columns} = {};
72 $self->{related_resultsets} = {};
78 $obj->in_storage; # Get value
79 $obj->in_storage(1); # Set value
81 Indicated whether the object exists as a row in the database or not
86 my ($self, $val) = @_;
87 $self->{_in_storage} = $val if @_ > 1;
88 return $self->{_in_storage};
95 Must be run on an object that is already in the database; issues an SQL
96 UPDATE query to commit any changes to the object to the db if required.
101 my ($self, $upd) = @_;
102 $self->throw_exception( "Not in database" ) unless $self->in_storage;
103 $self->set_columns($upd) if $upd;
104 my %to_update = $self->get_dirty_columns;
105 return $self unless keys %to_update;
106 my $ident_cond = $self->ident_condition;
107 $self->throw_exception("Cannot safely update a row in a PK-less table")
108 if ! keys %$ident_cond;
109 my $rows = $self->result_source->storage->update(
110 $self->result_source->from, \%to_update, $ident_cond);
112 $self->throw_exception( "Can't update ${self}: row not found" );
113 } elsif ($rows > 1) {
114 $self->throw_exception("Can't update ${self}: updated more than one row");
116 $self->{_dirty_columns} = {};
117 $self->{related_resultsets} = {};
125 Deletes the object from the database. The object is still perfectly usable,
126 but ->in_storage() will now return 0 and the object must re inserted using
127 ->insert() before ->update() can be used on it.
134 $self->throw_exception( "Not in database" ) unless $self->in_storage;
135 my $ident_cond = $self->ident_condition;
136 $self->throw_exception("Cannot safely delete a row in a PK-less table")
137 if ! keys %$ident_cond;
138 foreach my $column (keys %$ident_cond) {
139 $self->throw_exception("Can't delete the object unless it has loaded the primary keys")
140 unless exists $self->{_column_data}{$column};
142 $self->result_source->storage->delete(
143 $self->result_source->from, $ident_cond);
144 $self->in_storage(undef);
146 $self->throw_exception("Can't do class delete without a ResultSource instance")
147 unless $self->can('result_source_instance');
148 my $attrs = @_ > 1 && ref $_[$#_] eq 'HASH' ? { %{pop(@_)} } : {};
149 my $query = ref $_[0] eq 'HASH' ? $_[0] : {@_};
150 $self->result_source_instance->resultset->search(@_)->delete;
157 my $val = $obj->get_column($col);
159 Gets a column value from a row object. Currently, does not do
160 any queries; the column must have already been fetched from
161 the database and stored in the object.
166 my ($self, $column) = @_;
167 $self->throw_exception( "Can't fetch data as class method" ) unless ref $self;
168 return $self->{_column_data}{$column} if exists $self->{_column_data}{$column};
169 $self->throw_exception( "No such column '${column}'" ) unless $self->has_column($column);
173 =head2 has_column_loaded
175 if ( $obj->has_column_loaded($col) ) {
176 print "$col has been loaded from db";
179 Returns a true value if the column value has been loaded from the
180 database (or set locally).
184 sub has_column_loaded {
185 my ($self, $column) = @_;
186 $self->throw_exception( "Can't call has_column data as class method" ) unless ref $self;
187 return exists $self->{_column_data}{$column};
192 my %data = $obj->get_columns;
194 Does C<get_column>, for all column values at once.
200 return %{$self->{_column_data}};
203 =head2 get_dirty_columns
205 my %data = $obj->get_dirty_columns;
207 Identical to get_columns but only returns those that have been changed.
211 sub get_dirty_columns {
213 return map { $_ => $self->{_column_data}{$_} }
214 keys %{$self->{_dirty_columns}};
219 $obj->set_column($col => $val);
221 Sets a column value. If the new value is different from the old one,
222 the column is marked as dirty for when you next call $obj->update.
229 my $old = $self->get_column($column);
230 my $ret = $self->store_column(@_);
231 $self->{_dirty_columns}{$column} = 1
232 if (defined $old ^ defined $ret) || (defined $old && $old ne $ret);
238 my $copy = $orig->set_columns({ $col => $val, ... });
240 Sets more than one column value at once.
245 my ($self,$data) = @_;
246 foreach my $col (keys %$data) {
247 $self->set_column($col,$data->{$col});
254 my $copy = $orig->copy({ change => $to, ... });
256 Inserts a new row with the specified changes.
261 my ($self, $changes) = @_;
263 my $col_data = { %{$self->{_column_data}} };
264 foreach my $col (keys %$col_data) {
265 delete $col_data->{$col}
266 if $self->result_source->column_info($col)->{is_auto_increment};
268 my $new = bless { _column_data => $col_data }, ref $self;
269 $new->result_source($self->result_source);
270 $new->set_columns($changes);
272 foreach my $rel ($self->result_source->relationships) {
273 my $rel_info = $self->result_source->relationship_info($rel);
274 if ($rel_info->{attrs}{cascade_copy}) {
275 my $resolved = $self->result_source->resolve_condition(
276 $rel_info->{cond}, $rel, $new);
277 foreach my $related ($self->search_related($rel)) {
278 $related->copy($resolved);
287 $obj->store_column($col => $val);
289 Sets a column value without marking it as dirty.
294 my ($self, $column, $value) = @_;
295 $self->throw_exception( "No such column '${column}'" )
296 unless exists $self->{_column_data}{$column} || $self->has_column($column);
297 $self->throw_exception( "set_column called for ${column} without value" )
299 return $self->{_column_data}{$column} = $value;
302 =head2 inflate_result
304 Class->inflate_result($result_source, \%me, \%prefetch?)
306 Called by ResultSet to inflate a result from storage
311 my ($class, $source, $me, $prefetch) = @_;
312 #use Data::Dumper; print Dumper(@_);
313 my $new = bless({ result_source => $source,
317 ref $class || $class);
319 foreach my $pre (keys %{$prefetch||{}}) {
320 my $pre_val = $prefetch->{$pre};
321 my $pre_source = $source->related_source($pre);
322 $class->throw_exception("Can't prefetch non-existent relationship ${pre}")
324 if (ref($pre_val->[0]) eq 'ARRAY') { # multi
326 foreach my $pre_rec (@$pre_val) {
327 unless ($pre_source->primary_columns == grep { exists $pre_rec->[0]{$_}
328 and defined $pre_rec->[0]{$_} } $pre_source->primary_columns) {
331 push(@pre_objects, $pre_source->result_class->inflate_result(
332 $pre_source, @{$pre_rec}));
334 $new->related_resultset($pre)->set_cache(\@pre_objects);
335 } elsif (defined $pre_val->[0]) {
337 unless ($pre_source->primary_columns == grep { exists $pre_val->[0]{$_}
338 and !defined $pre_val->[0]{$_} } $pre_source->primary_columns)
340 $fetched = $pre_source->result_class->inflate_result(
341 $pre_source, @{$pre_val});
343 my $accessor = $source->relationship_info($pre)->{attrs}{accessor};
344 $class->throw_exception("No accessor for prefetched $pre")
345 unless defined $accessor;
346 if ($accessor eq 'single') {
347 $new->{_relationship_data}{$pre} = $fetched;
348 } elsif ($accessor eq 'filter') {
349 $new->{_inflated_column}{$pre} = $fetched;
351 $class->throw_exception("Prefetch not supported with accessor '$accessor'");
358 =head2 update_or_insert
360 $obj->update_or_insert
362 Updates the object if it's already in the db, else inserts it.
364 =head2 insert_or_update
366 $obj->insert_or_update
368 Alias for L</update_or_insert>
372 *insert_or_update = \&update_or_insert;
373 sub update_or_insert {
375 return ($self->in_storage ? $self->update : $self->insert);
380 my @changed_col_names = $obj->is_changed();
381 if ($obj->is_changed()) { ... }
383 In array context returns a list of columns with uncommited changes, or
384 in scalar context returns a true value if there are uncommitted
390 return keys %{shift->{_dirty_columns} || {}};
393 =head2 is_column_changed
395 if ($obj->is_column_changed('col')) { ... }
397 Returns a true value if the column has uncommitted changes.
401 sub is_column_changed {
402 my( $self, $col ) = @_;
403 return exists $self->{_dirty_columns}->{$col};
408 my $resultsource = $object->result_source;
410 Accessor to the ResultSource this object was created from
412 =head2 register_column
414 $column_info = { .... };
415 $class->register_column($column_name, $column_info);
417 Registers a column on the class. If the column_info has an 'accessor'
418 key, creates an accessor named after the value if defined; if there is
419 no such key, creates an accessor with the same name as the column
421 The column_info attributes are described in
422 L<DBIx::Class::ResultSource/add_columns>
426 sub register_column {
427 my ($class, $col, $info) = @_;
429 if (exists $info->{accessor}) {
430 return unless defined $info->{accessor};
431 $acc = [ $info->{accessor}, $col ];
433 $class->mk_group_accessors('column' => $acc);
437 =head2 throw_exception
439 See Schema's throw_exception.
443 sub throw_exception {
445 if (ref $self && ref $self->result_source) {
446 $self->result_source->schema->throw_exception(@_);
456 Matt S. Trout <mst@shadowcatsystems.co.uk>
460 You may distribute this code under the same terms as Perl itself.