Row::update encapsulates this when passed a hashref; no point in duplication
[dbsrgits/DBIx-Class.git] / lib / DBIx / Class / ResultSet.pm
index 584ec96..db58023 100644 (file)
@@ -196,45 +196,66 @@ call it as C<search(undef, \%attrs)>.
 
 sub search {
   my $self = shift;
+  my $rs = $self->search_rs( @_ );
+  return (wantarray ? $rs->all : $rs);
+}
 
-  my $rs;
-  if( @_ ) {
-    
-    my $attrs = { %{$self->{attrs}} };
-    my $having = delete $attrs->{having};
-    $attrs = { %$attrs, %{ pop(@_) } } if @_ > 1 and ref $_[$#_] eq 'HASH';
-
-    my $where = (@_
-                  ? ((@_ == 1 || ref $_[0] eq "HASH")
-                      ? shift
-                      : ((@_ % 2)
-                          ? $self->throw_exception(
-                              "Odd number of arguments to search")
-                          : {@_}))
-                  : undef());
-    if (defined $where) {
-      $attrs->{where} = (defined $attrs->{where}
-                ? { '-and' =>
-                    [ map { ref $_ eq 'ARRAY' ? [ -or => $_ ] : $_ }
-                        $where, $attrs->{where} ] }
-                : $where);
-    }
+=head2 search_rs
 
-    if (defined $having) {
-      $attrs->{having} = (defined $attrs->{having}
-                ? { '-and' =>
-                    [ map { ref $_ eq 'ARRAY' ? [ -or => $_ ] : $_ }
-                        $having, $attrs->{having} ] }
-                : $having);
-    }
+=over 4
 
-    $rs = (ref $self)->new($self->result_source, $attrs);
+=item Arguments: $cond, \%attrs?
+
+=item Return Value: $resultset
+
+=back
+
+This method does the same exact thing as search() except it will 
+always return a resultset, even in list context.
+
+=cut
+
+sub search_rs {
+  my $self = shift;
+
+  my $attrs = { %{$self->{attrs}} };
+  my $having = delete $attrs->{having};
+  $attrs = { %$attrs, %{ pop(@_) } } if @_ > 1 and ref $_[$#_] eq 'HASH';
+
+  my $where = (@_
+                ? ((@_ == 1 || ref $_[0] eq "HASH")
+                    ? shift
+                    : ((@_ % 2)
+                        ? $self->throw_exception(
+                            "Odd number of arguments to search")
+                        : {@_}))
+                : undef());
+  if (defined $where) {
+    $attrs->{where} = (defined $attrs->{where}
+              ? { '-and' =>
+                  [ map { ref $_ eq 'ARRAY' ? [ -or => $_ ] : $_ }
+                      $where, $attrs->{where} ] }
+              : $where);
   }
-  else {
-    $rs = $self;
-    $rs->reset;
+
+  if (defined $having) {
+    $attrs->{having} = (defined $attrs->{having}
+              ? { '-and' =>
+                  [ map { ref $_ eq 'ARRAY' ? [ -or => $_ ] : $_ }
+                      $having, $attrs->{having} ] }
+              : $having);
   }
-  return (wantarray ? $rs->all : $rs);
+
+  my $rs = (ref $self)->new($self->result_source, $attrs);
+
+  unless (@_) { # no search, effectively just a clone
+    my $rows = $self->get_cache;
+    if ($rows) {
+      $rs->set_cache($rows);
+    }
+  }
+  
+  return $rs;
 }
 
 =head2 search_literal
@@ -277,8 +298,8 @@ a row by its primary key:
 
   my $cd = $schema->resultset('CD')->find(5);
 
-You can also find a row by a specific key or unique constraint by specifying
-the C<key> attribute. For example:
+You can also find a row by a specific unique constraint using the C<key>
+attribute. For example:
 
   my $cd = $schema->resultset('CD')->find('Massive Attack', 'Mezzanine', { key => 'artist_title' });
 
@@ -304,24 +325,27 @@ L<DBIx::Class::ResultSource/add_unique_constraint>.
 =cut
 
 sub find {
-  my ($self, @vals) = @_;
-  my $attrs = (@vals > 1 && ref $vals[$#vals] eq 'HASH' ? pop(@vals) : {});
+  my $self = shift;
+  my $attrs = (@_ > 1 && ref $_[$#_] eq 'HASH' ? pop(@_) : {});
 
   # Parse out a hash from input
-  my @unique_cols = exists $attrs->{key}
+  my @cols = exists $attrs->{key}
     ? $self->result_source->unique_constraint_columns($attrs->{key})
     : $self->result_source->primary_columns;
 
-  my %hash;
-  if (ref $vals[0] eq 'HASH') {
-    %hash = %{ $vals[0] };
+  my $hash;
+  if (ref $_[0] eq 'HASH') {
+    $hash = { %{$_[0]} };
   }
-  elsif (@vals == @unique_cols) {
-    @hash{@unique_cols} = @vals;
+  elsif (@_ == @cols) {
+    $hash = {};
+    @{$hash}{@cols} = @_;
   }
   else {
-    # Hack for CDBI queries
-    %hash = @vals;
+    $self->throw_exception(
+      "Arguments to find must be a hashref or match the number of columns in the "
+        . exists $attrs->{key} ? "$attrs->{key} unique constraint" : "primary key"
+    );
   }
 
   # Check the hash we just parsed against our source's unique constraints
@@ -332,25 +356,21 @@ sub find {
     "Can't find unless a primary key or unique constraint is defined"
   ) unless @constraint_names;
 
-  my @unique_hashes;
+  my @unique_queries;
   foreach my $name (@constraint_names) {
     my @unique_cols = $self->result_source->unique_constraint_columns($name);
-    my %unique_hash = $self->_unique_hash(\%hash, \@unique_cols);
-
-    # TODO: Check that the ResultSet defines the rest of the query
-    push @unique_hashes, \%unique_hash
-      if scalar keys %unique_hash;# == scalar @unique_cols;
-  }
+    my $unique_query = $self->_build_unique_query($hash, \@unique_cols);
 
-  # Add the ResultSet's alias
-  foreach my $unique_hash (@unique_hashes) {
-    foreach my $key (grep { ! m/\./ } keys %$unique_hash) {
-      $unique_hash->{"$self->{attrs}{alias}.$key"} = delete $unique_hash->{$key};
+    # Add the ResultSet's alias
+    foreach my $key (grep { ! m/\./ } keys %$unique_query) {
+      $unique_query->{"$self->{attrs}{alias}.$key"} = delete $unique_query->{$key};
     }
+
+    push @unique_queries, $unique_query if %$unique_query;
   }
 
   # Handle cases where the ResultSet already defines the query
-  my $query = @unique_hashes ? \@unique_hashes : undef;
+  my $query = @unique_queries ? \@unique_queries : undef;
 
   # Run the query
   if (keys %$attrs) {
@@ -364,26 +384,19 @@ sub find {
   }
 }
 
-# _unique_hash
+# _build_unique_query
 #
-# Constrain the specified hash based on the specific column names.
+# Constrain the specified query hash based on the specified column names.
 
-sub _unique_hash {
-  my ($self, $hash, $unique_cols) = @_;
+sub _build_unique_query {
+  my ($self, $query, $unique_cols) = @_;
 
-  # Ugh, CDBI lowercases column names
-  if (exists $INC{'DBIx/Class/CDBICompat/ColumnCase.pm'}) {
-    foreach my $key (keys %$hash) {
-      $hash->{lc $key} = delete $hash->{$key};
-    }
-  }
-
-  my %unique_hash =
-    map  { $_ => $hash->{$_} }
-    grep { exists $hash->{$_} }
+  my %unique_query =
+    map  { $_ => $query->{$_} }
+    grep { exists $query->{$_} }
     @$unique_cols;
 
-  return %unique_hash;
+  return \%unique_query;
 }
 
 =head2 search_related
@@ -577,9 +590,9 @@ first record from the resultset.
 
 sub next {
   my ($self) = @_;
-  if (@{$self->{all_cache} || []}) {
+  if (my $cache = $self->get_cache) {
     $self->{all_cache_position} ||= 0;
-    return $self->{all_cache}->[$self->{all_cache_position}++];
+    return $cache->[$self->{all_cache_position}++];
   }
   if ($self->{attrs}{cache}) {
     $self->{all_cache_position} = 1;
@@ -669,9 +682,9 @@ sub _collapse_result {
       last unless (@raw = $self->cursor->next);
       $row = $self->{stashed_row} = \@raw;
       $tree = $self->_collapse_result($as, $row, $c_prefix);
-      #warn Data::Dumper::Dumper($tree, $row);
     }
-    @$target = @final;
+    @$target = (@final ? @final : [ {}, {} ]);
+      # single empty result to indicate an empty prefetched has_many
   }
 
   return $info;
@@ -718,7 +731,7 @@ clause.
 sub count {
   my $self = shift;
   return $self->search(@_)->count if @_ and defined $_[0];
-  return scalar @{ $self->get_cache } if @{ $self->get_cache };
+  return scalar @{ $self->get_cache } if $self->get_cache;
 
   my $count = $self->_count;
   return 0 unless $count;
@@ -795,7 +808,7 @@ is returned in list context.
 
 sub all {
   my ($self) = @_;
-  return @{ $self->get_cache } if @{ $self->get_cache };
+  return @{ $self->get_cache } if $self->get_cache;
 
   my @obj;
 
@@ -1246,8 +1259,7 @@ sub update_or_create {
 
   my $row = $self->find($hash, $attrs);
   if (defined $row) {
-    $row->set_columns($hash);
-    $row->update;
+    $row->update($hash);
     return $row;
   }
 
@@ -1269,7 +1281,7 @@ Gets the contents of the cache for the resultset, if the cache is set.
 =cut
 
 sub get_cache {
-  shift->{all_cache} || [];
+  shift->{all_cache};
 }
 
 =head2 set_cache
@@ -1292,13 +1304,7 @@ than re-querying the database even if the cache attr is not set.
 sub set_cache {
   my ( $self, $data ) = @_;
   $self->throw_exception("set_cache requires an arrayref")
-    if ref $data ne 'ARRAY';
-  my $result_class = $self->result_class;
-  foreach( @$data ) {
-    $self->throw_exception(
-      "cannot cache object of type '$_', expected '$result_class'"
-    ) if ref $_ ne $result_class;
-  }
+    if defined($data) && (ref $data ne 'ARRAY');
   $self->{all_cache} = $data;
 }
 
@@ -1317,7 +1323,7 @@ Clears the cache for the resultset.
 =cut
 
 sub clear_cache {
-  shift->set_cache([]);
+  shift->set_cache(undef);
 }
 
 =head2 related_resultset