* Fixed the bug with omitting 'FOR UPDATE'/'FOR SHARED' (introduced from the 'subque...
[dbsrgits/DBIx-Class.git] / lib / DBIx / Class / ResultSet.pm
index 7eb88f9..29810f5 100644 (file)
@@ -52,6 +52,86 @@ In the examples below, the following table classes are used:
   __PACKAGE__->belongs_to(artist => 'MyApp::Schema::Artist');
   1;
 
+=head1 DISCUSSION
+
+When you create a resultset (usually as a result of calling search()), DBIx::Class
+B<doesn't> make a DB call. Not yet. A resultset is (in simplistic terms) a set of
+where conditions, join conditions, and other metadata that would be needed to execute
+a SELECT statement. This has several big implications:
+
+=over 4
+
+=item * You can chain resultsets
+
+=item * You can run multiple queries using the same resultset
+
+=back
+
+=head2 Chaining resultsets
+
+Let's say you've got a query that needs to be run to return some data to the user. But,
+you have an authorization system in place that prevents certain users from seeing certain
+information. So, you want to construct the query in one method, but add constraints to it
+in another.
+
+  sub get_data {
+    my $self = shift;
+    my $request = $self->get_request; # Get a request object somehow.
+    my $schema = $self->get_schema;   # Get the DBIC schema object somehow.
+
+    my $rs = $schema->resultset('some_data')->search({
+      foo => $request->param('foo'),
+      bar => $request->param('bar'),
+    });
+
+    $self->apply_security_policy( $rs );
+
+    return $rs->all;
+  }
+
+  sub apply_security_policy {
+    my $self = shift;
+    my ($rs) = @_;
+
+    return $rs->search({
+      hidden_data => 0,
+    });
+  }
+
+=head2 Multiple queries
+
+Since a resultset hasn't hit the database yet, you can do all sorts of things with it.
+
+  # Don't hit the DB yet.
+  my $rs = $schema->resultset('some_table')->search({
+    foo => 1,
+    bar => 2,
+  });
+
+  # Each of these hits the DB individually.
+  my $count = $rs->count;
+  my $max_baz = $rs->get_column('baz')->max;
+  my @records = $rs->all;
+
+And it's not just limited to SELECT statements.
+
+  $rs->delete;
+
+This is even cooler
+
+  $rs->create({ baz => 20 });
+
+That is equivalent to
+
+  $schema->resultset('some_table')->create({
+    foo => 1,
+    bar => 2,
+    baz => 20,
+  });
+
+Note that C<get_column()> returns a ResultSetColumn object. This will behave almost
+exactly like a resultset, except it has methods tuned for working with columns.
+
 =head1 OVERLOADING
 
 If a resultset is used in a numeric context it returns the L</count>.
@@ -607,6 +687,10 @@ of the resultset.
 
 sub single {
   my ($self, $where) = @_;
+  if(@_ > 2) {
+      $self->throw_exception('single() only takes search conditions, no attributes. You want ->search( $cond, $attrs )->single()');
+  }
+
   my $attrs = { %{$self->_resolved_attrs} };
   if ($where) {
     if (defined $attrs->{where}) {
@@ -1109,7 +1193,11 @@ is returned in list context.
 =cut
 
 sub all {
-  my ($self) = @_;
+  my $self = shift;
+  if(@_) {
+      $self->throw_exception("all() doesn't take any arguments, you probably wanted ->search(...)->all()");
+  }
+
   return @{ $self->get_cache } if $self->get_cache;
 
   my @obj;
@@ -1261,6 +1349,11 @@ sub update {
   $self->throw_exception("Values for update must be a hash")
     unless ref $values eq 'HASH';
 
+  carp(   'WARNING! Currently $rs->update() does not generate proper SQL'
+        . ' on joined resultsets, and may affect rows well outside of the'
+        . ' contents of $rs. Use at your own risk' )
+    if ( $self->{attrs}{seen_join} );
+
   my $cond = $self->_cond_for_update_delete;
    
   return $self->result_source->storage->update(
@@ -1506,7 +1599,7 @@ sub populate {
 
 =head2 _normalize_populate_args ($args)
 
-Private method used by L</populate> to normalize it's incoming arguments.  Factored
+Private method used by L</populate> to normalize its incoming arguments.  Factored
 out in case you want to subclass and accept new argument structures to the
 L</populate> method.
 
@@ -1709,6 +1802,24 @@ sub _remove_alias {
   return \%unaliased;
 }
 
+=head2 as_query
+
+=over 4
+
+=item Arguments: none
+
+=item Return Value: \[ $sql, @bind ]
+
+=back
+
+Returns the SQL query and bind vars associated with the invocant.
+
+This is generally used as the RHS for a subquery.
+
+=cut
+
+sub as_query { return shift->cursor->as_query(@_) }
+
 =head2 find_or_new
 
 =over 4
@@ -1725,7 +1836,7 @@ sub _remove_alias {
   $cd->cd_to_producer->find_or_new({ producer => $producer },
                                    { key => 'primary });
 
-Find an existing record from this resultset, based on it's primary
+Find an existing record from this resultset, based on its primary
 key, or a unique constraint. 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.
@@ -1771,7 +1882,7 @@ To create one row for this resultset, pass a hashref of key/value
 pairs representing the columns of the table and the values you wish to
 store. If the appropriate relationships are set up, foreign key fields
 can also be passed an object representing the foreign row, and the
-value will be set to it's primary key.
+value will be set to its primary key.
 
 To create related objects, pass a hashref for the value if the related
 item is a foreign key relationship (L<DBIx::Class::Relationship/belongs_to>),
@@ -1790,7 +1901,7 @@ Example of creating a new row.
 
   $person_rs->create({
     name=>"Some Person",
-       email=>"somebody@someplace.com"
+    email=>"somebody@someplace.com"
   });
   
 Example of creating a new row and also creating rows in a related C<has_many>
@@ -1809,10 +1920,10 @@ C<belongs_to>resultset. Note Hashref.
 
   $cd_rs->create({
     title=>"Music for Silly Walks",
-       year=>2000,
-       artist => {
-         name=>"Silly Musician",
-       }
+    year=>2000,
+    artist => {
+      name=>"Silly Musician",
+    }
   });
 
 =cut
@@ -2104,9 +2215,8 @@ sub related_resultset {
 
 =back
 
-Returns the current alias of the result source that corrensponds to the result
-set (this alias will eventually be used as the SQL table alias in the SQL
-query). Usually it is C<me>.
+Returns the current table alias for the result source this resultset is built
+on, that will be used in the SQL query. Usually it is C<me>.
 
 Currently the source alias that refers to the result set returned by a
 L</search>/L</find> family method depends on how you got to the resultset: it's
@@ -2243,7 +2353,7 @@ sub _resolved_attrs {
   if (my $prefetch = delete $attrs->{prefetch}) {
     $prefetch = $self->_merge_attr({}, $prefetch);
     my @pre_order;
-    my $seen = $attrs->{seen_join} || {};
+    my $seen = { %{ $attrs->{seen_join} || {} } };
     foreach my $p (ref $prefetch eq 'ARRAY' ? @$prefetch : ($prefetch)) {
       # bring joins back to level of current class
       my @prefetch = $source->resolve_prefetch(
@@ -2399,8 +2509,12 @@ sub throw_exception {
 
 =head1 ATTRIBUTES
 
-The resultset takes various attributes that modify its behavior. Here's an
-overview of them:
+Attributes are used to refine a ResultSet in various ways when
+searching for data. They can be passed to any method which takes an
+C<\%attrs> argument. See L</search>, L</search_rs>, L</find>,
+L</count>.
+
+These are in no particular order:
 
 =head2 order_by
 
@@ -2419,6 +2533,10 @@ L<DBIx::Class::Storage::DBI/connect_info>) you will need to do C<\'year DESC' >
 specify an order. (The scalar ref causes it to be passed as raw sql to the DB,
 so you will need to manually quote things as appropriate.)
 
+If your L<SQL::Abstract> version supports it (>=1.50), you can also use
+C<{-desc => 'year'}>, which takes care of the quoting for you. This is the
+recommended syntax.
+
 =head2 columns
 
 =over 4
@@ -2489,7 +2607,7 @@ L</select> but adds columns to the selection.
 
 =over 4
 
-Indicates additional column names for those added via L</+select>.
+Indicates additional column names for those added via L</+select>. See L</as>.
 
 =back