Documentation cleanup
[dbsrgits/DBIx-Class.git] / lib / DBIx / Class / ResultSet.pm
index 0289c0f..6bf92be 100644 (file)
@@ -287,7 +287,14 @@ constraint. For example:
     { key => 'artist_title' }
   );
 
-See also L</find_or_create> and L</update_or_create>.
+If no C<key> is specified, it searches on all unique constraints defined on the
+source, including the primary key.
+
+If the C<key> is specified as C<primary>, it searches only on the primary key.
+
+See also L</find_or_create> and L</update_or_create>. For information on how to
+declare unique constraints, see
+L<DBIx::Class::ResultSource/add_unique_constraint>.
 
 =cut
 
@@ -295,40 +302,64 @@ sub find {
   my ($self, @vals) = @_;
   my $attrs = (@vals > 1 && ref $vals[$#vals] eq 'HASH' ? pop(@vals) : {});
 
-  my @cols = $self->result_source->primary_columns;
+  my %unique_constraints = $self->result_source->unique_constraints;
+  $self->throw_exception(
+    "Can't find unless a primary key or unique constraint is defined"
+  ) unless %unique_constraints;
+
+  my @constraint_names = keys %unique_constraints;
   if (exists $attrs->{key}) {
-    my %uniq = $self->result_source->unique_constraints;
     $self->throw_exception(
       "Unknown key $attrs->{key} on '" . $self->result_source->name . "'"
-    ) unless exists $uniq{$attrs->{key}};
-    @cols = @{ $uniq{$attrs->{key}} };
-  }
-  #use Data::Dumper; warn Dumper($attrs, @vals, @cols);
-  $self->throw_exception(
-    "Can't find unless a primary key or unique constraint is defined"
-  ) unless @cols;
-
-  my $query;
-  if (ref $vals[0] eq 'HASH') {
-    $query = { %{$vals[0]} };
-  } elsif (@cols == @vals) {
-    $query = {};
-    @{$query}{@cols} = @vals;
-  } else {
-    $query = {@vals};
+    ) unless exists $unique_constraints{$attrs->{key}};
+
+    @constraint_names = ($attrs->{key});
   }
-  foreach my $key (grep { ! m/\./ } keys %$query) {
-    $query->{"$self->{attrs}{alias}.$key"} = delete $query->{$key};
+
+  my @unique_hashes;
+  foreach my $name (@constraint_names) {
+    my @unique_cols = @{ $unique_constraints{$name} };
+    my %unique_hash;
+    if (ref $vals[0] eq 'HASH') {
+      # Stupid hack for CDBICompat
+      my %hash = %{ $vals[0] };
+      foreach my $key (keys %hash) {
+        $hash{lc $key} = delete $hash{$key};
+      }
+
+      %unique_hash =
+        map  { $_ => $hash{$_} }
+        grep { exists $hash{$_} }
+        @unique_cols;
+    }
+    elsif (@unique_cols == @vals) {
+      # Assume the argument order corresponds to the constraint definition
+      @unique_hash{@unique_cols} = @vals;
+    }
+    elsif (@vals % 2 == 0) {
+      # Fix for CDBI calling with a hash
+      %unique_hash = @vals;
+    }
+
+    foreach my $key (grep { ! m/\./ } keys %unique_hash) {
+      $unique_hash{"$self->{attrs}{alias}.$key"} = delete $unique_hash{$key};
+    }
+
+    #use Data::Dumper; warn Dumper \@vals, \@unique_cols, \%unique_hash;
+    push @unique_hashes, \%unique_hash if %unique_hash;
   }
-  #warn Dumper($query);
-  
+
+  # Handle cases where the ResultSet already defines the query
+  my $query = @unique_hashes ? \@unique_hashes : undef;
+
   if (keys %$attrs) {
-      my $rs = $self->search($query,$attrs);
-      return keys %{$rs->{collapse}} ? $rs->next : $rs->single;
-  } else {
-      return keys %{$self->{collapse}} ?
-        $self->search($query)->next :
-        $self->single($query);
+    my $rs = $self->search($query, $attrs);
+    return keys %{$rs->{collapse}} ? $rs->next : $rs->single;
+  }
+  else {
+    return keys %{$self->{collapse}}
+      ? $self->search($query)->next
+      : $self->single($query);
   }
 }
 
@@ -391,7 +422,7 @@ sub cursor {
   my $cd = $schema->resultset('CD')->single({ year => 2001 });
 
 Inflates the first result without creating a cursor if the resultset has
-any records in it; if not returns nothing. Used by find() as an optimisation.
+any records in it; if not returns nothing. Used by L</find> as an optimisation.
 
 =cut
 
@@ -810,14 +841,14 @@ sub first {
 #
 # update/delete require the condition to be modified to handle
 # the differing SQL syntax available.  This transforms the $self->{cond}
-# appropriately, returning the new condition
+# appropriately, returning the new condition.
 
 sub _cond_for_update_delete {
   my ($self) = @_;
   my $cond = {};
 
   if (!ref($self->{cond})) {
-    # No-op. No condition, we're update/deleting everything
+    # No-op. No condition, we're updating/deleting everything
   }
   elsif (ref $self->{cond} eq 'ARRAY') {
     $cond = [
@@ -828,21 +859,31 @@ sub _cond_for_update_delete {
           $hash{$1} = $_->{$key};
         }
         \%hash;
-        } @{$self->{cond}}
+      } @{$self->{cond}}
     ];
   }
   elsif (ref $self->{cond} eq 'HASH') {
     if ((keys %{$self->{cond}})[0] eq '-and') {
-      $cond->{-and} = [
-        map {
-          my %hash;
-          foreach my $key (keys %{$_}) {
+      $cond->{-and} = [];
+
+      my @cond = @{$self->{cond}{-and}};
+      for (my $i = 0; $i < @cond - 1; $i++) {
+        my $entry = $cond[$i];
+
+        my %hash;
+        if (ref $entry eq 'HASH') {
+          foreach my $key (keys %{$entry}) {
             $key =~ /([^.]+)$/;
-            $hash{$1} = $_->{$key};
+            $hash{$1} = $entry->{$key};
           }
-          \%hash;
-          } @{$self->{cond}{-and}}
-      ];
+        }
+        else {
+          $entry =~ /([^.]+)$/;
+          $hash{$entry} = $cond[++$i];
+        }
+
+        push @{$cond->{-and}}, \%hash;
+      }
     }
     else {
       foreach my $key (keys %{$self->{cond}}) {
@@ -853,8 +894,10 @@ sub _cond_for_update_delete {
   }
   else {
     $self->throw_exception(
-               "Can't update/delete on resultset with condition unless hash or array");
+      "Can't update/delete on resultset with condition unless hash or array"
+    );
   }
+
   return $cond;
 }
 
@@ -1038,6 +1081,32 @@ sub new_result {
   return $obj;
 }
 
+=head2 find_or_new
+
+=over 4
+
+=item Arguments: \%vals, \%attrs?
+
+=item Return Value: $object
+
+=back
+
+Find an existing record from this resultset. If none exists, instantiate a new
+result object and return it. The object will not be saved into your storage
+until you call L<DBIx::Class::Row/insert> on it.
+
+If you want objects to be saved immediately, use L</find_or_create> instead.
+
+=cut
+
+sub find_or_new {
+  my $self     = shift;
+  my $attrs    = (@_ > 1 && ref $_[$#_] eq 'HASH' ? pop(@_) : {});
+  my $hash     = ref $_[0] eq 'HASH' ? shift : {@_};
+  my $exists   = $self->find($hash, $attrs);
+  return defined $exists ? $exists : $self->new_result($hash);
+}
+
 =head2 create
 
 =over 4
@@ -1094,7 +1163,8 @@ constraint. For example:
     { key => 'artist_title' }
   );
 
-See also L</find> and L</update_or_create>.
+See also L</find> and L</update_or_create>. For information on how to declare
+unique constraints, see L<DBIx::Class::ResultSource/add_unique_constraint>.
 
 =cut
 
@@ -1141,7 +1211,8 @@ source, including the primary key.
 
 If the C<key> is specified as C<primary>, it searches only on the primary key.
 
-See also L</find> and L</find_or_create>.
+See also L</find> and L</find_or_create>. For information on how to declare
+unique constraints, see L<DBIx::Class::ResultSource/add_unique_constraint>.
 
 =cut
 
@@ -1150,30 +1221,11 @@ sub update_or_create {
   my $attrs = (@_ > 1 && ref $_[$#_] eq 'HASH' ? pop(@_) : {});
   my $hash = ref $_[0] eq 'HASH' ? shift : {@_};
 
-  my %unique_constraints = $self->result_source->unique_constraints;
-  my @constraint_names   = (exists $attrs->{key}
-                            ? ($attrs->{key})
-                            : keys %unique_constraints);
-
-  my @unique_hashes;
-  foreach my $name (@constraint_names) {
-    my @unique_cols = @{ $unique_constraints{$name} };
-    my %unique_hash =
-      map  { $_ => $hash->{$_} }
-      grep { exists $hash->{$_} }
-      @unique_cols;
-
-    push @unique_hashes, \%unique_hash
-      if (scalar keys %unique_hash == scalar @unique_cols);
-  }
-
-  if (@unique_hashes) {
-    my $row = $self->single(\@unique_hashes);
-    if (defined $row) {
-      $row->set_columns($hash);
-      $row->update;
-      return $row;
-    }
+  my $row = $self->find($hash, $attrs);
+  if (defined $row) {
+    $row->set_columns($hash);
+    $row->update;
+    return $row;
   }
 
   return $self->create($hash);