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;
38 my $new = { _column_data => {} };
42 $new->throw_exception("attrs must be a hashref")
43 unless ref($attrs) eq 'HASH';
44 foreach my $k (keys %$attrs) {
45 $new->throw_exception("No such column $k on $class")
46 unless $class->has_column($k);
47 $new->store_column($k => $attrs->{$k});
58 Inserts an object into the database if it isn't already in there. Returns
59 the object itself. Requires the object's result source to be set, or the
60 class to have a result_source_instance method.
66 return $self if $self->in_storage;
67 $self->{result_source} ||= $self->result_source_instance
68 if $self->can('result_source_instance');
69 my $source = $self->{result_source};
70 $self->throw_exception("No result_source set on this object; can't insert")
72 #use Data::Dumper; warn Dumper($self);
73 $source->storage->insert($source->from, { $self->get_columns });
75 $self->{_dirty_columns} = {};
76 $self->{related_resultsets} = {};
82 $obj->in_storage; # Get value
83 $obj->in_storage(1); # Set value
85 Indicated whether the object exists as a row in the database or not
90 my ($self, $val) = @_;
91 $self->{_in_storage} = $val if @_ > 1;
92 return $self->{_in_storage};
99 Must be run on an object that is already in the database; issues an SQL
100 UPDATE query to commit any changes to the object to the db if required.
105 my ($self, $upd) = @_;
106 $self->throw_exception( "Not in database" ) unless $self->in_storage;
107 $self->set_columns($upd) if $upd;
108 my %to_update = $self->get_dirty_columns;
109 return $self unless keys %to_update;
110 my $ident_cond = $self->ident_condition;
111 $self->throw_exception("Cannot safely update a row in a PK-less table")
112 if ! keys %$ident_cond;
113 my $rows = $self->result_source->storage->update(
114 $self->result_source->from, \%to_update, $ident_cond);
116 $self->throw_exception( "Can't update ${self}: row not found" );
117 } elsif ($rows > 1) {
118 $self->throw_exception("Can't update ${self}: updated more than one row");
120 $self->{_dirty_columns} = {};
121 $self->{related_resultsets} = {};
129 Deletes the object from the database. The object is still perfectly usable,
130 but ->in_storage() will now return 0 and the object must re inserted using
131 ->insert() before ->update() can be used on it.
138 $self->throw_exception( "Not in database" ) unless $self->in_storage;
139 my $ident_cond = $self->ident_condition;
140 $self->throw_exception("Cannot safely delete a row in a PK-less table")
141 if ! keys %$ident_cond;
142 foreach my $column (keys %$ident_cond) {
143 $self->throw_exception("Can't delete the object unless it has loaded the primary keys")
144 unless exists $self->{_column_data}{$column};
146 $self->result_source->storage->delete(
147 $self->result_source->from, $ident_cond);
148 $self->in_storage(undef);
150 $self->throw_exception("Can't do class delete without a ResultSource instance")
151 unless $self->can('result_source_instance');
152 my $attrs = @_ > 1 && ref $_[$#_] eq 'HASH' ? { %{pop(@_)} } : {};
153 my $query = ref $_[0] eq 'HASH' ? $_[0] : {@_};
154 $self->result_source_instance->resultset->search(@_)->delete;
161 my $val = $obj->get_column($col);
163 Gets a column value from a row object. Currently, does not do
164 any queries; the column must have already been fetched from
165 the database and stored in the object.
170 my ($self, $column) = @_;
171 $self->throw_exception( "Can't fetch data as class method" ) unless ref $self;
172 return $self->{_column_data}{$column} if exists $self->{_column_data}{$column};
173 $self->throw_exception( "No such column '${column}'" ) unless $self->has_column($column);
177 =head2 has_column_loaded
179 if ( $obj->has_column_loaded($col) ) {
180 print "$col has been loaded from db";
183 Returns a true value if the column value has been loaded from the
184 database (or set locally).
188 sub has_column_loaded {
189 my ($self, $column) = @_;
190 $self->throw_exception( "Can't call has_column data as class method" ) unless ref $self;
191 return exists $self->{_column_data}{$column};
196 my %data = $obj->get_columns;
198 Does C<get_column>, for all column values at once.
204 return %{$self->{_column_data}};
207 =head2 get_dirty_columns
209 my %data = $obj->get_dirty_columns;
211 Identical to get_columns but only returns those that have been changed.
215 sub get_dirty_columns {
217 return map { $_ => $self->{_column_data}{$_} }
218 keys %{$self->{_dirty_columns}};
223 $obj->set_column($col => $val);
225 Sets a column value. If the new value is different from the old one,
226 the column is marked as dirty for when you next call $obj->update.
233 my $old = $self->get_column($column);
234 my $ret = $self->store_column(@_);
235 $self->{_dirty_columns}{$column} = 1
236 if (defined $old ^ defined $ret) || (defined $old && $old ne $ret);
242 my $copy = $orig->set_columns({ $col => $val, ... });
244 Sets more than one column value at once.
249 my ($self,$data) = @_;
250 foreach my $col (keys %$data) {
251 $self->set_column($col,$data->{$col});
258 my $copy = $orig->copy({ change => $to, ... });
260 Inserts a new row with the specified changes.
265 my ($self, $changes) = @_;
267 my $col_data = { %{$self->{_column_data}} };
268 foreach my $col (keys %$col_data) {
269 delete $col_data->{$col}
270 if $self->result_source->column_info($col)->{is_auto_increment};
273 my $new = { _column_data => $col_data };
274 bless $new, ref $self;
276 $new->result_source($self->result_source);
277 $new->set_columns($changes);
279 foreach my $rel ($self->result_source->relationships) {
280 my $rel_info = $self->result_source->relationship_info($rel);
281 if ($rel_info->{attrs}{cascade_copy}) {
282 my $resolved = $self->result_source->resolve_condition(
283 $rel_info->{cond}, $rel, $new);
284 foreach my $related ($self->search_related($rel)) {
285 $related->copy($resolved);
294 $obj->store_column($col => $val);
296 Sets a column value without marking it as dirty.
301 my ($self, $column, $value) = @_;
302 $self->throw_exception( "No such column '${column}'" )
303 unless exists $self->{_column_data}{$column} || $self->has_column($column);
304 $self->throw_exception( "set_column called for ${column} without value" )
306 return $self->{_column_data}{$column} = $value;
309 =head2 inflate_result
311 Class->inflate_result($result_source, \%me, \%prefetch?)
313 Called by ResultSet to inflate a result from storage
318 my ($class, $source, $me, $prefetch) = @_;
319 #use Data::Dumper; print Dumper(@_);
321 result_source => $source,
325 bless $new, (ref $class || $class);
328 foreach my $pre (keys %{$prefetch||{}}) {
329 my $pre_val = $prefetch->{$pre};
330 my $pre_source = $source->related_source($pre);
331 $class->throw_exception("Can't prefetch non-existent relationship ${pre}")
333 if (ref($pre_val->[0]) eq 'ARRAY') { # multi
335 foreach my $pre_rec (@$pre_val) {
336 unless ($pre_source->primary_columns == grep { exists $pre_rec->[0]{$_}
337 and defined $pre_rec->[0]{$_} } $pre_source->primary_columns) {
340 push(@pre_objects, $pre_source->result_class->inflate_result(
341 $pre_source, @{$pre_rec}));
343 $new->related_resultset($pre)->set_cache(\@pre_objects);
344 } elsif (defined $pre_val->[0]) {
346 unless ($pre_source->primary_columns == grep { exists $pre_val->[0]{$_}
347 and !defined $pre_val->[0]{$_} } $pre_source->primary_columns)
349 $fetched = $pre_source->result_class->inflate_result(
350 $pre_source, @{$pre_val});
352 my $accessor = $source->relationship_info($pre)->{attrs}{accessor};
353 $class->throw_exception("No accessor for prefetched $pre")
354 unless defined $accessor;
355 if ($accessor eq 'single') {
356 $new->{_relationship_data}{$pre} = $fetched;
357 } elsif ($accessor eq 'filter') {
358 $new->{_inflated_column}{$pre} = $fetched;
360 $class->throw_exception("Prefetch not supported with accessor '$accessor'");
367 =head2 update_or_insert
369 $obj->update_or_insert
371 Updates the object if it's already in the db, else inserts it.
373 =head2 insert_or_update
375 $obj->insert_or_update
377 Alias for L</update_or_insert>
381 *insert_or_update = \&update_or_insert;
382 sub update_or_insert {
384 return ($self->in_storage ? $self->update : $self->insert);
389 my @changed_col_names = $obj->is_changed();
390 if ($obj->is_changed()) { ... }
392 In array context returns a list of columns with uncommited changes, or
393 in scalar context returns a true value if there are uncommitted
399 return keys %{shift->{_dirty_columns} || {}};
402 =head2 is_column_changed
404 if ($obj->is_column_changed('col')) { ... }
406 Returns a true value if the column has uncommitted changes.
410 sub is_column_changed {
411 my( $self, $col ) = @_;
412 return exists $self->{_dirty_columns}->{$col};
417 my $resultsource = $object->result_source;
419 Accessor to the ResultSource this object was created from
421 =head2 register_column
423 $column_info = { .... };
424 $class->register_column($column_name, $column_info);
426 Registers a column on the class. If the column_info has an 'accessor'
427 key, creates an accessor named after the value if defined; if there is
428 no such key, creates an accessor with the same name as the column
430 The column_info attributes are described in
431 L<DBIx::Class::ResultSource/add_columns>
435 sub register_column {
436 my ($class, $col, $info) = @_;
438 if (exists $info->{accessor}) {
439 return unless defined $info->{accessor};
440 $acc = [ $info->{accessor}, $col ];
442 $class->mk_group_accessors('column' => $acc);
446 =head2 throw_exception
448 See Schema's throw_exception.
452 sub throw_exception {
454 if (ref $self && ref $self->result_source) {
455 $self->result_source->schema->throw_exception(@_);
465 Matt S. Trout <mst@shadowcatsystems.co.uk>
469 You may distribute this code under the same terms as Perl itself.