X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=blobdiff_plain;f=lib%2FDBIx%2FClass%2FResultSet.pm;h=ae6fb3e581381d8f6a46dca85eafff55fff1d21e;hb=91fa659e479fec2f148dad9084b14017915cc553;hp=340e4c54b8d3a45d27b4954d6631becaf058ae2d;hpb=6aeb91850d436202eecee51d6c51c8e548961105;p=dbsrgits%2FDBIx-Class-Historic.git diff --git a/lib/DBIx/Class/ResultSet.pm b/lib/DBIx/Class/ResultSet.pm index 340e4c5..ae6fb3e 100644 --- a/lib/DBIx/Class/ResultSet.pm +++ b/lib/DBIx/Class/ResultSet.pm @@ -6,6 +6,7 @@ use overload '0+' => 'count', fallback => 1; use Data::Page; +use Storable; =head1 NAME @@ -33,10 +34,11 @@ not perform any queries -- these are executed as needed by the other methods. =cut sub new { - my ($class, $source, $attrs) = @_; - #use Data::Dumper; warn Dumper(@_); - $class = ref $class if ref $class; - $attrs = { %{ $attrs || {} } }; + my $class = shift; + return $class->new_result(@_) if ref $class; + my ($source, $attrs) = @_; + #use Data::Dumper; warn Dumper($attrs); + $attrs = Storable::dclone($attrs || {}); # { %{ $attrs || {} } }; my %seen; my $alias = ($attrs->{alias} ||= 'me'); if (!$attrs->{select}) { @@ -47,7 +49,7 @@ sub new { } $attrs->{as} ||= [ map { m/^$alias\.(.*)$/ ? $1 : $_ } @{$attrs->{select}} ]; #use Data::Dumper; warn Dumper(@{$attrs}{qw/select as/}); - $attrs->{from} ||= [ { $alias => $source->name } ]; + $attrs->{from} ||= [ { $alias => $source->from } ]; if (my $join = delete $attrs->{join}) { foreach my $j (ref $join eq 'ARRAY' ? (@{$join}) : ($join)) { @@ -57,15 +59,15 @@ sub new { $seen{$j} = 1; } } - push(@{$attrs->{from}}, $source->result_class->_resolve_join($join, $attrs->{alias})); + push(@{$attrs->{from}}, $source->resolve_join($join, $attrs->{alias})); } $attrs->{group_by} ||= $attrs->{select} if delete $attrs->{distinct}; foreach my $pre (@{delete $attrs->{prefetch} || []}) { - push(@{$attrs->{from}}, $source->result_class->_resolve_join($pre, $attrs->{alias})) + push(@{$attrs->{from}}, $source->resolve_join($pre, $attrs->{alias})) unless $seen{$pre}; my @pre = map { "$pre.$_" } - $source->result_class->_relationships->{$pre}->{class}->columns; + $source->related_source($pre)->columns; push(@{$attrs->{select}}, @pre); push(@{$attrs->{as}}, @pre); } @@ -79,10 +81,10 @@ sub new { cond => $attrs->{where}, from => $attrs->{from}, count => undef, + page => delete $attrs->{page}, pager => undef, attrs => $attrs }; bless ($new, $class); - #$new->pager if $attrs->{page}; return $new; } @@ -111,12 +113,14 @@ sub search { my $where = (@_ ? ((@_ == 1 || ref $_[0] eq "HASH") ? shift : {@_}) : undef()); if (defined $where) { $where = (defined $attrs->{where} - ? { '-and' => [ $where, $attrs->{where} ] } + ? { '-and' => + [ map { ref $_ eq 'ARRAY' ? [ -or => $_ ] : $_ } + $where, $attrs->{where} ] } : $where); $attrs->{where} = $where; } - my $rs = $self->new($self->{source}, $attrs); + my $rs = (ref $self)->new($self->{source}, $attrs); return (wantarray ? $rs->all : $rs); } @@ -137,6 +141,36 @@ sub search_literal { return $self->search(\$cond, $attrs); } +=head2 find(@colvalues), find(\%cols) + +Finds a row based on its primary key(s). + +=cut + +sub find { + my ($self, @vals) = @_; + my $attrs = (@vals > 1 && ref $vals[$#vals] eq 'HASH' ? pop(@vals) : {}); + my @pk = $self->{source}->primary_columns; + #use Data::Dumper; warn Dumper($attrs, @vals, @pk); + $self->{source}->result_class->throw( "Can't find unless primary columns are defined" ) + unless @pk; + my $query; + if (ref $vals[0] eq 'HASH') { + $query = $vals[0]; + } elsif (@pk == @vals) { + $query = {}; + @{$query}{@pk} = @vals; + } else { + $query = {@vals}; + } + #warn Dumper($query); + # Useless -> disabled + #$self->{source}->result_class->throw( "Can't find unless all primary keys are specified" ) + # unless (keys %$query >= @pk); # If we check 'em we run afoul of uc/lc + # column names etc. Not sure what to do yet + return $self->search($query)->next; +} + =head2 search_related $rs->search_related('relname', $cond?, $attrs?); @@ -145,22 +179,17 @@ sub search_literal { sub search_related { my ($self, $rel, @rest) = @_; - my $rel_obj = $self->{source}->result_class->_relationships->{$rel}; + my $rel_obj = $self->{source}->relationship_info($rel); $self->{source}->result_class->throw( "No such relationship ${rel} in search_related") unless $rel_obj; - my $r_class = $self->{source}->result_class->resolve_class($rel_obj->{class}); - my $source = $r_class->result_source; - $source = bless({ %{$source} }, ref $source || $source); - $source->storage($self->{source}->storage); - $source->result_class($r_class); my $rs = $self->search(undef, { join => $rel }); - #use Data::Dumper; warn Dumper($rs); - return $source->resultset_class->new( - $source, { %{$rs->{attrs}}, - alias => $rel, - select => undef(), - as => undef() } + return $self->{source}->schema->resultset($rel_obj->{class} + )->search( undef, + { %{$rs->{attrs}}, + alias => $rel, + select => undef(), + as => undef() } )->search(@rest); } @@ -208,7 +237,7 @@ sub slice { $attrs->{offset} ||= 0; $attrs->{offset} += $min; $attrs->{rows} = ($max ? ($max - $min + 1) : 1); - my $slice = $self->new($self->{source}, $attrs); + my $slice = (ref $self)->new($self->{source}, $attrs); return (wantarray ? $slice->all : $slice); } @@ -237,7 +266,8 @@ sub _construct_object { $me{$col} = shift @row; } } - my $new = $self->{source}->result_class->inflate_result(\%me, \%pre); + my $new = $self->{source}->result_class->inflate_result( + $self->{source}, \%me, \%pre); $new = $self->{attrs}{record_filter}->($new) if exists $self->{attrs}{record_filter}; return $new; @@ -259,10 +289,10 @@ sub count { my $attrs = { %{ $self->{attrs} }, select => { 'count' => '*' }, as => [ 'count' ] }; - # offset, order by and page are not needed to count - delete $attrs->{$_} for qw/rows offset order_by page pager/; + # offset, order by and page are not needed to count. record_filter is cdbi + delete $attrs->{$_} for qw/rows offset order_by page pager record_filter/; - ($self->{count}) = $self->new($self->{source}, $attrs)->cursor->next; + ($self->{count}) = (ref $self)->new($self->{source}, $attrs)->cursor->next; } return 0 unless $self->{count}; my $count = $self->{count}; @@ -315,19 +345,59 @@ sub first { return $_[0]->reset->next; } +=head2 update(\%values) + +Sets the specified columns in the resultset to the supplied values + +=cut + +sub update { + my ($self, $values) = @_; + die "Values for update must be a hash" unless ref $values eq 'HASH'; + return $self->{source}->storage->update( + $self->{source}->from, $values, $self->{cond}); +} + +=head2 update_all(\%values) + +Fetches all objects and updates them one at a time. ->update_all will run +cascade triggers, ->update will not. + +=cut + +sub update_all { + my ($self, $values) = @_; + die "Values for update must be a hash" unless ref $values eq 'HASH'; + foreach my $obj ($self->all) { + $obj->set_columns($values)->update; + } + return 1; +} + =head2 delete -Deletes all elements in the resultset. +Deletes the contents of the resultset from its result source. =cut sub delete { my ($self) = @_; - $_->delete for $self->all; + $self->{source}->storage->delete($self->{source}->from, $self->{cond}); return 1; } -*delete_all = \&delete; # Yeah, yeah, yeah ... +=head2 delete_all + +Fetches all objects and deletes them one at a time. ->delete_all will run +cascade triggers, ->delete will not. + +=cut + +sub delete_all { + my ($self) = @_; + $_->delete for $self->all; + return 1; +} =head2 pager @@ -339,11 +409,11 @@ sense for queries with page turned on. sub pager { my ($self) = @_; my $attrs = $self->{attrs}; - die "Can't create pager for non-paged rs" unless $attrs->{page}; + die "Can't create pager for non-paged rs" unless $self->{page}; $attrs->{rows} ||= 10; $self->count; return $self->{pager} ||= Data::Page->new( - $self->{count}, $attrs->{rows}, $attrs->{page}); + $self->{count}, $attrs->{rows}, $self->{page}); } =head2 page($page_num) @@ -356,10 +426,62 @@ sub page { my ($self, $page) = @_; my $attrs = { %{$self->{attrs}} }; $attrs->{page} = $page; - return $self->new($self->{source}, $attrs); + return (ref $self)->new($self->{source}, $attrs); +} + +=head2 new_result(\%vals) + +Creates a result in the resultset's result class + +=cut + +sub new_result { + my ($self, $values) = @_; + $self->{source}->result_class->throw( "new_result needs a hash" ) + unless (ref $values eq 'HASH'); + $self->{source}->result_class->throw( "Can't abstract implicit construct, condition not a hash" ) + if ($self->{cond} && !(ref $self->{cond} eq 'HASH')); + my %new = %$values; + my $alias = $self->{attrs}{alias}; + foreach my $key (keys %{$self->{cond}||{}}) { + $new{$1} = $self->{cond}{$key} if ($key =~ m/^(?:$alias\.)?([^\.]+)$/); + } + my $obj = $self->{source}->result_class->new(\%new); + $obj->result_source($self->{source}) if $obj->can('result_source'); + $obj; +} + +=head2 create(\%vals) + +Inserts a record into the resultset and returns the object + +Effectively a shortcut for ->new_result(\%vals)->insert + +=cut + +sub create { + my ($self, $attrs) = @_; + $self->{source}->result_class->throw( "create needs a hashref" ) unless ref $attrs eq 'HASH'; + return $self->new_result($attrs)->insert; +} + +=head2 find_or_create(\%vals) + + $class->find_or_create({ key => $val, ... }); + +Searches for a record matching the search condition; if it doesn't find one, +creates one and returns that instead. + +=cut + +sub find_or_create { + my $self = shift; + my $hash = ref $_[0] eq "HASH" ? shift: {@_}; + my $exists = $self->find($hash); + return defined($exists) ? $exists : $self->create($hash); } -=head1 Attributes +=head1 ATTRIBUTES The resultset takes various attributes that modify its behavior. Here's an overview of them: