From: Matt S Trout Date: Tue, 21 Aug 2007 18:25:33 +0000 (+0000) Subject: Merge 'on_disconnect_do' into 'trunk' X-Git-Tag: v0.08010~80 X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=commitdiff_plain;h=5322ea52d80043df028d7b7670bc0db5aeafc8a1;hp=6d2e7a96f2b9f1e9f5718a421831344dcdb400a3;p=dbsrgits%2FDBIx-Class.git Merge 'on_disconnect_do' into 'trunk' --- diff --git a/Changes b/Changes index b86c2b5..2cae3a3 100644 --- a/Changes +++ b/Changes @@ -1,4 +1,30 @@ Revision history for DBIx::Class + +0.08006 2007-08-12 15:12:00 + - Move to using Class::C3::Componentised + - Remove warn statement from DBIx::Class::Row + +0.08005 2007-08-06 + - add timestamp fix re rt.cpan 26978 - no test yet but change + clearly should cause no regressions + - provide alias for related_resultset via local() so it's set + correctly at resultset construction time (fixes RestrictWithObject) + - fixes bind params in debug statements + (original test from abraxxa) + - fixed storage->connected fork bug + (test and fix from Radu Greab) + - add 1; to AccessorGroup.pm for stuff that still uses it + - refactor Statistics to create debugging filehandle to fix bug with + closed STDERR, update docs and modify Versioned to use Statistics + (original fix from diz) + +0.08004 2007-08-06 19:00:00 + - fix storage connect code to not trigger bug via auto-viv + (test from aherzog) + - fixup cursor_class to be an 'inherited' attr for per-package defaults + - add default_resultset_attributes entry to Schema + - optimisation in DBI::Cursor to check software_limit before falling + back to base Cursor->all - fix bug with create_multi not inserting non-storage objects (test and fix from davinchi) - DBIx::Class::AccessorGroup made empty subclass of diff --git a/Makefile.PL b/Makefile.PL index 91b1b4d..1e84a29 100644 --- a/Makefile.PL +++ b/Makefile.PL @@ -9,6 +9,7 @@ requires 'Scalar::Util' => 0; requires 'SQL::Abstract' => 1.20; requires 'SQL::Abstract::Limit' => 0.101; requires 'Class::C3' => 0.13; +requires 'Class::C3::Componentised' => 0; requires 'Storable' => 0; requires 'Carp::Clan' => 0; requires 'DBI' => 1.40; @@ -21,7 +22,7 @@ requires 'Scope::Guard' => 0.03; # Perl 5.8.0 doesn't have utf8::is_utf8() requires 'Encode' => 0 if ($] <= 5.008000); -build_requires 'DBD::SQLite' => 1.11; +build_requires 'DBD::SQLite' => 1.13; build_requires 'Test::Builder' => 0.33; install_script 'script/dbicadmin'; diff --git a/lib/DBIx/Class.pm b/lib/DBIx/Class.pm index 3f267ea..3f0fb8e 100644 --- a/lib/DBIx/Class.pm +++ b/lib/DBIx/Class.pm @@ -23,7 +23,7 @@ sub component_base_class { 'DBIx::Class' } # i.e. first release of 0.XX *must* be 0.XX000. This avoids fBSD ports # brain damage and presumably various other packaging systems too -$VERSION = '0.08003'; +$VERSION = '0.08006'; sub MODIFY_CODE_ATTRIBUTES { my ($class,$code,@attrs) = @_; @@ -123,6 +123,8 @@ Then you can use these classes in your application's code: my $cd = $millennium_cds_rs->next; # SELECT ... FROM cds JOIN artists ... my $cd_artist_name = $cd->artist->name; # Already has the data so no query + # new() makes a DBIx::Class::Row object but doesnt insert it into the DB. + # create() is the same as new() then insert(). my $new_cd = $schema->resultset('CD')->new({ title => 'Spoon' }); $new_cd->artist($cd->artist); $new_cd->insert; # Auto-increment primary key filled in after INSERT @@ -161,9 +163,9 @@ APIs, since DBIx::Class is used in production in many organisations, and even backwards incompatible changes to non-published APIs will be fixed if they're reported and doing so doesn't cost the codebase anything. -The test suite is quite substantial, and several developer releases are -generally made to CPAN before the -current branch is merged back to trunk for -a major release. +The test suite is quite substantial, and several developer releases +are generally made to CPAN before the branch for the next release is +merged back to trunk for a major release. The community can be found via: @@ -171,6 +173,8 @@ The community can be found via: SVN: http://dev.catalyst.perl.org/repos/bast/DBIx-Class/ + SVNWeb: http://dev.catalyst.perl.org/svnweb/bast/browse/DBIx-Class/ + IRC: irc.perl.org#dbix-class =head1 WHERE TO GO NEXT @@ -189,6 +193,8 @@ is traditional :) abraxxa: Alexander Hartmaier +aherzog: Adam Herzog + andyg: Andy Grundman ank: Andres Kievsky diff --git a/lib/DBIx/Class/AccessorGroup.pm b/lib/DBIx/Class/AccessorGroup.pm index f553a87..ae4d490 100644 --- a/lib/DBIx/Class/AccessorGroup.pm +++ b/lib/DBIx/Class/AccessorGroup.pm @@ -5,6 +5,8 @@ use warnings; use base qw/Class::Accessor::Grouped/; +1; + =head1 NAME DBIx::Class::AccessorGroup - See Class::Accessor::Grouped diff --git a/lib/DBIx/Class/Componentised.pm b/lib/DBIx/Class/Componentised.pm index 1bb6a68..db70c7b 100644 --- a/lib/DBIx/Class/Componentised.pm +++ b/lib/DBIx/Class/Componentised.pm @@ -4,8 +4,7 @@ package # hide from PAUSE use strict; use warnings; -use Class::C3; -use Class::Inspector; +use base 'Class::C3::Componentised'; use Carp::Clan qw/^DBIx::Class/; sub inject_base { @@ -29,66 +28,7 @@ sub inject_base { } } - # Yes, this is hack. But it *does* work. Please don't submit tickets about - # it on the basis of the comments in Class::C3, the author was on #dbix-class - # while I was implementing this. - - eval "package $target; import Class::C3;" unless exists $Class::C3::MRO{$target}; -} - -sub load_components { - my $class = shift; - my $base = $class->component_base_class; - my @comp = map { /^\+(.*)$/ ? $1 : "${base}::$_" } grep { $_ !~ /^#/ } @_; - $class->_load_components(@comp); - Class::C3::reinitialize(); -} - -sub load_own_components { - my $class = shift; - my @comp = map { "${class}::$_" } grep { $_ !~ /^#/ } @_; - $class->_load_components(@comp); -} - -sub _load_components { - my ($class, @comp) = @_; - foreach my $comp (@comp) { - $class->ensure_class_loaded($comp); - } - $class->inject_base($class => @comp); -} - -# Given a class name, tests to see if it is already loaded or otherwise -# defined. If it is not yet loaded, the package is require'd, and an exception -# is thrown if the class is still not loaded. -# -# TODO: handle ->has_many('rel', 'Class'...) instead of -# ->has_many('rel', 'Some::Schema::Class'...) -# -# BUG: For some reason, packages with syntax errors are added to %INC on -# require -sub ensure_class_loaded { - my ($class, $f_class) = @_; - - croak "Invalid class name $f_class" - if ($f_class=~m/(?:\b:\b|\:{3,})/); - return if Class::Inspector->loaded($f_class); - eval "require $f_class"; # require needs a bareword or filename - if ($@) { - if ($class->can('throw_exception')) { - $class->throw_exception($@); - } else { - croak $@; - } - } -} - -# Returns true if the specified class is installed or already loaded, false -# otherwise -sub ensure_class_found { - my ($class, $f_class) = @_; - return Class::Inspector->loaded($f_class) || - Class::Inspector->installed($f_class); + $class->next::method($target, @to_inject); } # Returns a true value if the specified class is installed and loaded diff --git a/lib/DBIx/Class/InflateColumn/DateTime.pm b/lib/DBIx/Class/InflateColumn/DateTime.pm index f92481d..df92390 100644 --- a/lib/DBIx/Class/InflateColumn/DateTime.pm +++ b/lib/DBIx/Class/InflateColumn/DateTime.pm @@ -54,7 +54,7 @@ sub register_column { $self->next::method($column, $info, @rest); return unless defined($info->{data_type}); my $type = lc($info->{data_type}); - $type = 'datetime' if ($type eq 'timestamp'); + $type = 'datetime' if ($type =~ /^timestamp/); if ($type eq 'datetime' || $type eq 'date') { my ($parse, $format) = ("parse_${type}", "format_${type}"); $self->inflate_column( diff --git a/lib/DBIx/Class/Manual/Cookbook.pod b/lib/DBIx/Class/Manual/Cookbook.pod index 1bc0c8a..9a96f34 100644 --- a/lib/DBIx/Class/Manual/Cookbook.pod +++ b/lib/DBIx/Class/Manual/Cookbook.pod @@ -9,7 +9,7 @@ DBIx::Class::Manual::Cookbook - Miscellaneous recipes =head3 Paged results When you expect a large number of results, you can ask L for a -paged resultset, which will fetch only a small number of records at a time: +paged resultset, which will fetch only a defined number of records at a time: my $rs = $schema->resultset('Artist')->search( undef, @@ -32,7 +32,7 @@ The C attribute does not have to be specified in your search: return $rs->page(1); # DBIx::Class::ResultSet containing first 10 records -In either of the above cases, you can return a L object for the +In either of the above cases, you can get a L object for the resultset (suitable for use in e.g. a template) using the C method: return $rs->pager(); @@ -294,7 +294,7 @@ it in your C attribute: join => [qw/ artist /], order_by => [qw/ artist.name /] } - }; + ); # Equivalent SQL: # SELECT cd.* FROM cd @@ -668,25 +668,50 @@ your main database class to make sure it disconnects cleanly: =head2 Schema import/export -This functionality requires you to have L (also known as -"SQL Fairy") installed. +To create a DBIx::Class schema from an existing database, use +L's C: -To create a DBIx::Class schema from an existing database: + perl -MDBIx::Class::Schema::Loader=make_schema_at,dump_to_dir:./lib -e 'make_schema_at("My::Schema", { debug => 1 }, [ "dbi:Pg:dbname=foo","postgres" ])' - sqlt --from DBI - --to DBIx::Class::File - --prefix "MySchema" > MySchema.pm +The following functionality requires you to have L +(also known as "SQL Fairy") installed. -To create a MySQL database from an existing L schema, convert the -schema to MySQL's dialect of SQL: +To create a set of database-specific .sql files for the above schema: - sqlt --from SQL::Translator::Parser::DBIx::Class - --to MySQL - --DBIx::Class "MySchema.pm" > Schema1.sql - -And import using the mysql client: + my $schema = My::Schema->connect($dsn); + $schema->create_ddl_dir(['MySQL', 'SQLite', 'PostgreSQL'], + '0.1', + '/dbscriptdir/' + ); + +By default this will create schema files in the current directory, for +MySQL, SQLite and PostgreSQL, using the $VERSION from your Schema.pm. + +To create a new database using the schema: + + my $schema = My::Schema->connect($dsn); + $schema->deploy({ add_drop_tables => 1}); + +To import created .sql files using the mysql client: + + mysql -h "host" -D "database" -u "user" -p < My_Schema_1.0_MySQL.sql + +To create C conversion scripts to update a database to a +newer version of your schema at a later point, first set a new +$VERSION in your Schema file, then: + + my $schema = My::Schema->connect($dsn); + $schema->create_ddl_dir(['MySQL', 'SQLite', 'PostgreSQL'], + '0.2', + '/dbscriptdir/', + '0.1' + ); + +This will produce new database-specific .sql files for the new version +of the schema, plus scripts to convert from version 0.1 to 0.2. This +requires that the files for 0.1 as created above are available in the +given directory to diff against. - mysql -h "host" -D "database" -u "user" -p < Schema1.sql =head2 Easy migration from class-based to schema-based setup @@ -762,19 +787,8 @@ described under C. =head3 Save the schema -Use C to transform your schema into an SQL script suitable for your -customer's database. E.g. for MySQL: - - sqlt --from SQL::Translator::Parser::DBIx::Class - --to MySQL - --DBIx::Class "MySchema.pm" > Schema1.mysql.sql - -If you need to target databases from multiple vendors, just generate an SQL -script suitable for each. To support PostgreSQL too: - - sqlt --from SQL::Translator::DBIx::Class - --to PostgreSQL - --DBIx::Class "MySchema.pm" > Schema1.pgsql.sql +Call L as above under L. =head3 Deploy to customers @@ -796,22 +810,21 @@ all part of your install. =head3 Modify the schema to change functionality -As your application evolves, it may be necessary to modify your schema to -change functionality. Once the changes are made to your schema in DBIx::Class, -export the modified schema as before, taking care not to overwrite the original: - - sqlt --from SQL::Translator::DBIx::Class - --to MySQL - --DBIx::Class "Anything.pm" > Schema2.mysql.sql - -Next, use sqlt-diff to create an SQL script that will update the customer's -database schema: - - sqlt-diff --to MySQL Schema1=MySQL Schema2=MySQL > SchemaUpdate.mysql.sql +As your application evolves, it may be necessary to modify your schema +to change functionality. Once the changes are made to your schema in +DBIx::Class, export the modified schema and the conversion scripts as +in L. =head3 Deploy update to customers -The schema update can be deployed to customers using the same method as before. +Add the L schema component to your +Schema class. This will add a new table to your database called +C which will keep track of which version is installed +and warn if the user trys to run a newer schema version than the +database thinks it has. + +Alternatively, you can send the conversion sql scripts to your +customers as above. =head2 Setting limit dialect for SQL::Abstract::Limit @@ -964,7 +977,7 @@ mechanism: my $sql = shift(); my $params = @_; - print "Executing $sql: ".join(', ', @params)."\n"; + $self->print("Executing $sql: ".join(', ', @params)."\n"); $start = time(); } @@ -973,7 +986,8 @@ mechanism: my $sql = shift(); my @params = @_; - printf("Execution took %0.4f seconds.\n", time() - $start); + my $elapsed = sprintf("%0.4f", time() - $start); + $self->print("Execution took $elapsed seconds.\n"); $start = undef; } @@ -981,8 +995,8 @@ mechanism: You can then install that class as the debugging object: - __PACKAGE__->storage()->debugobj(new My::Profiler()); - __PACKAGE__->storage()->debug(1); + __PACKAGE__->storage->debugobj(new My::Profiler()); + __PACKAGE__->storage->debug(1); A more complicated example might involve storing each execution of SQL in an array: @@ -1001,6 +1015,7 @@ array: You could then create average, high and low execution times for an SQL statement and dig down to see if certain parameters cause aberrant behavior. +You might want to check out L as well. =head2 Getting the value of the primary key for the last database insert diff --git a/lib/DBIx/Class/Manual/FAQ.pod b/lib/DBIx/Class/Manual/FAQ.pod index df0f773..39f4b99 100644 --- a/lib/DBIx/Class/Manual/FAQ.pod +++ b/lib/DBIx/Class/Manual/FAQ.pod @@ -259,7 +259,7 @@ L for details. In your table schema class, create a "private" column accessor with: - __PACKAGE__->add_columns(my_common => { accessor => '_hidden_my_column' }); + __PACKAGE__->add_columns(my_column => { accessor => '_hidden_my_column' }); Then, in the same class, implement a subroutine called "my_column" that fetches the real value and does the formatting you want. diff --git a/lib/DBIx/Class/Manual/Intro.pod b/lib/DBIx/Class/Manual/Intro.pod index f5bf73d..d4ee303 100644 --- a/lib/DBIx/Class/Manual/Intro.pod +++ b/lib/DBIx/Class/Manual/Intro.pod @@ -4,75 +4,71 @@ DBIx::Class::Manual::Intro - Introduction to DBIx::Class =head1 INTRODUCTION -So, you are bored with SQL, and want a native Perl interface for your -database? Or you've been doing this for a while with L, -and think there's a better way? You've come to the right place. +You're bored with SQL, and want a native Perl interface for your database? Or +you've been doing this for a while with L, and think there's a +better way? You've come to the right place. =head1 THE DBIx::Class WAY -Here are a few simple tips that will help you get your bearings -with DBIx::Class. +Here are a few simple tips that will help you get your bearings with +DBIx::Class. =head2 Tables become ResultSources -DBIx::Class needs to know what your Table structure looks like. You do that -by defining Ls. Each table get's a ResultSource, -which defines the Columns it has, along with any Relationships it has to -other tables. (And oh, so much more besides) The important thing to -understand: - +DBIx::Class needs to know what your Table structure looks like. You do that by +defining Ls. Each table gets a ResultSource, which +defines the Columns it has, along with any Relationships it has to other tables. +(And oh, so much more besides) The important thing to understand: + A ResultSource == Table - + (most of the time, but just bear with my simplification) =head2 It's all about the ResultSet -So, we've got some ResultSources defined. Now, we want to actually use -those definitions to help us translate the queries we need into -handy perl objects! +So, we've got some ResultSources defined. Now, we want to actually use those +definitions to help us translate the queries we need into handy perl objects! + +Let's say we defined a ResultSource for an "album" table with three columns: +"albumid", "artist", and "title". Any time we want to query this table, we'll +be creating a L from its ResultSource. For example, the +results of: -Let's say we defined a ResultSource for an "album" table with three -columns: "albumid", "artist", and "title". Any time we want to query -this table, we'll be creating a L from it's -ResultSource. For example, the results of: + SELECT albumid, artist, title FROM album; - SELECT albumid, artist, title FROM album; - -Would be retrieved by creating a ResultSet object from the album -table's ResultSource, likely by using the "search" method. +Would be retrieved by creating a ResultSet object from the album table's +ResultSource, likely by using the "search" method. -DBIx::Class doesn't limit you to creating only simple ResultSets -- -if you wanted to do something like: +DBIx::Class doesn't limit you to creating only simple ResultSets -- if you +wanted to do something like: + + SELECT title FROM album GROUP BY title; - SELECT title FROM album GROUP BY title; - You could easily achieve it. The important thing to understand: - Any time you would reach for a SQL query in DBI, you are - creating a DBIx::Class::ResultSet. + Any time you would reach for a SQL query in DBI, you are + creating a DBIx::Class::ResultSet. =head2 Search is like "prepare" -DBIx::Class tends to wait until it absolutely must fetch information -from the database. If you are returning a ResultSet, the query won't -execute until you use a method that wants to access the data. (Such -as "next", or "first") +DBIx::Class tends to wait until it absolutely must fetch information from the +database. If you are returning a ResultSet, the query won't execute until you +use a method that wants to access the data. (Such as "next", or "first") The important thing to understand: - Setting up a ResultSet does not execute the query; retrieving - the data does. + Setting up a ResultSet does not execute the query; retrieving + the data does. =head1 SETTING UP DBIx::Class -Let's look at how you can set and use your first native L -tree. +Let's look at how you can set and use your first native L tree. -First we'll see how you can set up your classes yourself. If you want -them to be auto-discovered, just skip to the next section, which shows -you how to use L. +First we'll see how you can set up your classes yourself. If you want them to +be auto-discovered, just skip to the next section, which shows you how to use +L. =head2 Setting it up manually @@ -82,8 +78,8 @@ L: package My::Schema; use base qw/DBIx::Class::Schema/; -In this class you load your result_source ("table", "model") classes, which -we will define later, using the load_classes() method. You can specify which +In this class you load your result_source ("table", "model") classes, which we +will define later, using the load_classes() method. You can specify which classes to load manually: # load My::Schema::Album and My::Schema::Artist @@ -115,8 +111,8 @@ For example, if you want serial/auto-incrementing primary keys: __PACKAGE__->load_components(qw/ PK::Auto Core /); -C is supported for many databases; see -L for more information. +C is supported for many databases; see L +for more information. Set the table for your class: @@ -126,9 +122,8 @@ Add columns to your class: __PACKAGE__->add_columns(qw/ albumid artist title /); -Each column can also be set up with its own accessor, data_type and other -pieces of information that it may be useful to have, just pass C -a hash such as: +Each column can also be set up with its own accessor, data_type and other pieces +of information that it may be useful to have -- just pass C a hash: __PACKAGE__->add_columns(albumid => { accessor => 'album', @@ -154,10 +149,10 @@ a hash such as: } ); -Most of this data isn't yet used directly by DBIx::Class, but various related -modules such as L make use of it. Also it allows you -to create your database tables from your Schema, instead of the other way -around. See L for details. +DBIx::Class doesn't directly use most of this data yet, but various related +modules such as L make use of it. Also it allows you to +create your database tables from your Schema, instead of the other way around. +See L for details. See L for more details of the possible column attributes. @@ -174,22 +169,21 @@ If you have a multi-column primary key, just pass a list instead: __PACKAGE__->set_primary_key( qw/ albumid artistid / ); -Define relationships that the class has with any other classes by using -either C to describe a column which contains an ID of another -table, or C to make a predefined accessor for fetching objects -that contain this tables foreign key in one of their columns: +Define this class' relationships with other classes using either C +to describe a column which contains an ID of another Table, or C to +make a predefined accessor for fetching objects that contain this Table's +foreign key: __PACKAGE__->has_many('albums', 'My::Schema::Artist', 'album_id'); -More information about the various types of relationships available, and -how you can design your own, can be found in L. +See L for more information about the various types of +available relationships and how you can design your own. =head2 Using L -This is an external module, and not part of the L -distribution. Like L, it inspects your database, -and automatically creates classes for all the tables in your database. -Here's a simple setup: +This is an external module, and not part of the L distribution. +Like L, it inspects your database, and automatically creates +classes for all the tables in your database. Here's a simple setup: package My::Schema; use base qw/DBIx::Class::Schema::Loader/; @@ -198,29 +192,29 @@ Here's a simple setup: 1; -The actual autoloading process will occur when you create a connected -instance of your schema below. +The actual autoloading process will occur when you create a connected instance +of your schema below. -L takes lots of other options. For more -information, consult its documentation. +See the L documentation for more information on its +many options. =head2 Connecting -To connect to your Schema, you also need to provide the connection details. -The arguments are the same as you would use for L: +To connect to your Schema, you need to provide the connection details. The +arguments are the same as for L: my $schema = My::Schema->connect('dbi:SQLite:/home/me/myapp/my.db'); -You can create as many different schema instances as you need. So if you have -a second database you want to access: +You can create as many different schema instances as you need. So if you have a +second database you want to access: my $other_schema = My::Schema->connect( $dsn, $user, $password, $attrs ); -Note that L does not cache connections for you. If you -use multiple connections, you need to do this manually. +Note that L does not cache connections for you. If you use +multiple connections, you need to do this manually. -To execute some sql statements on every connect you can add them as an option -in a special fifth argument to connect, like so: +To execute some sql statements on every connect you can add them as an option in +a special fifth argument to connect: my $another_schema = My::Schema->connect( $dsn, @@ -230,51 +224,52 @@ in a special fifth argument to connect, like so: { on_connect_do => \@on_connect_sql_statments } ); -For more information about this and other special C-time options, -see L. +See L for more information about +this and other special C-time options. =head2 Basic usage Once you've defined the basic classes, either manually or using L, you can start interacting with your database. -To access your database using your $schema object, you can fetch a L -representing each of your tables by calling the ->resultset method. +To access your database using your $schema object, you can fetch a +L representing each of your tables by +calling the C method. The simplest way to get a record is by primary key: my $album = $schema->resultset('Album')->find(14); -This will run a C with C in the C clause, and +return an instance of C that represents this row. Once you +have that row, you can access and update columns: $album->title('Physical Graffiti'); my $title = $album->title; # $title holds 'Physical Graffiti' -If you prefer, you can use the C and C -accessors instead: +If you prefer, you can use the C and C accessors +instead: $album->set_column('title', 'Presence'); $title = $album->get_column('title'); -Just like with L, you call C to commit your -changes to the database: +Just like with L, you call C to commit your changes to the +database: $album->update; -If needed, you can throw away your local changes like this: +If needed, you can throw away your local changes: $album->discard_changes if $album->is_changed; -As you can see, C allows you to check if there are local -changes to your object. +As you can see, C allows you to check if there are local changes to +your object. =head2 Adding and removing rows -To create a new record in the database, you can use the C -method. It returns an instance of C that can be -used to access the data in the new record: +To create a new record in the database, you can use the C method. It +returns an instance of C that can be used to access the data +in the new record: my $new_album = $schema->resultset('Album')->create({ title => 'Wish You Were Here', @@ -287,27 +282,26 @@ Now you can add data to the new record: $new_album->year('1975'); $new_album->update; -Likewise, you can remove it from the database like this: +Likewise, you can remove it from the database: $new_album->delete; -You can also remove records without retrieving them first, by calling -delete directly on a ResultSet object. +You can also remove records without retrieving them first, by calling delete +directly on a ResultSet object. # Delete all of Falco's albums $schema->resultset('Album')->search({ artist => 'Falco' })->delete; =head2 Finding your objects -L provides a few different ways to retrieve data from -your database. Here's one example: +L provides a few different ways to retrieve data from your +database. Here's one example: # Find all of Santana's albums my $rs = $schema->resultset('Album')->search({ artist => 'Santana' }); -In scalar context, as above, C returns a -L object. It can be used to peek at the first -album returned by the database: +In scalar context, as above, C returns a L +object. It can be used to peek at the first album returned by the database: my $album = $rs->first; print $album->title; @@ -324,11 +318,7 @@ Or, you can update them all at once: $rs->update({ year => 2001 }); -For more information on what you can do with a -L, see L. - -In list context, the C method returns all of the matching -rows: +In list context, the C method returns all of the matching rows: # Fetch immediately all of Carlos Santana's albums my @albums = $schema->resultset('Album')->search( @@ -343,15 +333,15 @@ We also provide a handy shortcut for doing a C search: # Find albums whose artist starts with 'Jimi' my $rs = $schema->resultset('Album')->search_like({ artist => 'Jimi%' }); -Or you can provide your own C clause, like: +Or you can provide your own C clause: # Find Peter Frampton albums from the year 1986 my $where = 'artist = ? AND year = ?'; my @bind = ( 'Peter Frampton', 1986 ); my $rs = $schema->resultset('Album')->search_literal( $where, @bind ); -The preferred way to generate complex queries is to provide a -L construct to C: +The preferred way to generate complex queries is to provide a L +construct to C: my $rs = $schema->resultset('Album')->search({ artist => { '!=', 'Janis Joplin' }, @@ -365,8 +355,7 @@ This results in something like the following C clause: AND year < 1980 AND albumid IN (1, 14, 15, 65, 43) -For more examples of complex queries, see -L. +For more examples of complex queries, see L. The search can also be modified by passing another hash with attributes: @@ -378,6 +367,9 @@ attributes: C<@albums> then holds the two most recent Bob Marley albums. +For more information on what you can do with a L, see +L. + For a complete overview of the available attributes, see L. diff --git a/lib/DBIx/Class/ResultSet.pm b/lib/DBIx/Class/ResultSet.pm index 75ba410..b5c36e4 100644 --- a/lib/DBIx/Class/ResultSet.pm +++ b/lib/DBIx/Class/ResultSet.pm @@ -971,7 +971,7 @@ sub _count { # Separated out so pager can get the full count # 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/; - my $tmp_rs = (ref $self)->new($self->_source_handle, $attrs); + my $tmp_rs = (ref $self)->new($self->result_source, $attrs); my ($count) = $tmp_rs->cursor->next; return $count; } @@ -1251,7 +1251,7 @@ Pass an arrayref of hashrefs. Each hashref should be a structure suitable for submitting to a $resultset->create(...) method. In void context, C in L is used -to insert the data, as this is a faster method. +to insert the data, as this is a faster method. Otherwise, each set of data is inserted into the database using L, and a arrayref of the resulting row @@ -1288,6 +1288,14 @@ Example: Assuming an Artist Class that has many CDs Classes relating: print $ArtistOne->name; ## response is 'Artist One' print $ArtistThree->cds->count ## reponse is '2' + +Please note an important effect on your data when choosing between void and +wantarray context. Since void context goes straight to C in +L this will skip any component that is overriding +c. So if you are using something like L to +create primary keys for you, you will find that your PKs are empty. In this +case you will have to use the wantarray context in order to create those +values. =cut @@ -1409,7 +1417,7 @@ attribute set on the resultset (10 by default). sub page { my ($self, $page) = @_; - return (ref $self)->new($self->_source_handle, { %{$self->{attrs}}, page => $page }); + return (ref $self)->new($self->result_source, { %{$self->{attrs}}, page => $page }); } =head2 new_result @@ -1422,7 +1430,12 @@ sub page { =back -Creates an object in the resultset's result class and returns it. +Creates a new row object in the resultset's result class and returns +it. The row is not inserted into the database at this point, call +L to do that. Calling L +will tell you whether the row object has been inserted or not. + +Passes the hashref of input on to L. =cut @@ -1540,10 +1553,59 @@ sub find_or_new { =back -Inserts a record into the resultset and returns the object representing it. +Attempt to create a single new row or a row with multiple related rows +in the table represented by the resultset (and related tables). This +will not check for duplicate rows before inserting, use +L to do that. + +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. + +To create related objects, pass a hashref for the value if the related +item is a foreign key relationship (L), +and use the name of the relationship as the key. (NOT the name of the field, +necessarily). For C and C relationships, pass an arrayref +of hashrefs containing the data for each of the rows to create in the foreign +tables, again using the relationship name as the key. + +Instead of hashrefs of plain related data (key/value pairs), you may +also pass new or inserted objects. New objects (not inserted yet, see +L), will be inserted into their appropriate tables. Effectively a shortcut for C<< ->new_result(\%vals)->insert >>. +Example of creating a new row. + + $person_rs->create({ + name=>"Some Person", + email=>"somebody@someplace.com" + }); + +Example of creating a new row and also creating rows in a related C +or C resultset. Note Arrayref. + + $artist_rs->create( + { artistid => 4, name => 'Manufactured Crap', cds => [ + { title => 'My First CD', year => 2006 }, + { title => 'Yet More Tweeny-Pop crap', year => 2007 }, + ], + }, + ); + +Example of creating a new row and also creating a row in a related +Cresultset. Note Hashref. + + $cd_rs->create({ + title=>"Music for Silly Walks", + year=>2000, + artist => { + name=>"Silly Musician", + } + }); + =cut sub create { @@ -1737,7 +1799,7 @@ sub related_resultset { my $rel_obj = $self->result_source->relationship_info($rel); $self->throw_exception( - "search_related: result source '" . $self->_source_handle->source_moniker . + "search_related: result source '" . $self->result_source->source_name . "' has no such relationship $rel") unless $rel_obj; @@ -1748,7 +1810,7 @@ sub related_resultset { #XXX - temp fix for result_class bug. There likely is a more elegant fix -groditi my %attrs = %{$self->{attrs}||{}}; - delete $attrs{result_class}; + delete @attrs{qw(result_class alias)}; my $new_cache; @@ -1759,21 +1821,32 @@ sub related_resultset { } } - my $new = $self->_source_handle - ->schema - ->resultset($rel_obj->{class}) - ->search_rs( - undef, { - %attrs, - join => undef, - prefetch => undef, - select => undef, - as => undef, - alias => $alias, - where => $self->{cond}, - seen_join => $seen, - from => $from, - }); + my $rel_source = $self->result_source->related_source($rel); + + my $new = do { + + # The reason we do this now instead of passing the alias to the + # search_rs below is that if you wrap/overload resultset on the + # source you need to know what alias it's -going- to have for things + # to work sanely (e.g. RestrictWithObject wants to be able to add + # extra query restrictions, and these may need to be $alias.) + + my $attrs = $rel_source->resultset_attributes; + local $attrs->{alias} = $alias; + + $rel_source->resultset + ->search_rs( + undef, { + %attrs, + join => undef, + prefetch => undef, + select => undef, + as => undef, + where => $self->{cond}, + seen_join => $seen, + from => $from, + }); + }; $new->set_cache($new_cache) if $new_cache; $new; }; @@ -2142,7 +2215,7 @@ C method (or via the object accessor, B). It has nothing to do with the SQL code C< SELECT foo AS bar >. -The C< as > attribute is used in conjunction with C, usually when C