From: Peter Rabbitson Date: Fri, 20 Mar 2015 16:34:01 +0000 (+0100) Subject: Documentation improvements cherry-picked from latest X-Git-Tag: v0.082820~2 X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=commitdiff_plain;h=60fbfe7d4286d1d366dff7f56be399e8e6a804fa;p=dbsrgits%2FDBIx-Class.git Documentation improvements cherry-picked from latest Combines 4dbfa426, b339d2ef, 2d707084, f7d6f0e1, b00f9ad4, 372b98a6 and 409a3b1e --- diff --git a/AUTHORS b/AUTHORS index 4a56099..fe261d1 100644 --- a/AUTHORS +++ b/AUTHORS @@ -20,6 +20,7 @@ aherzog: Adam Herzog Alexander Keusch alexrj: Alessandro Ranellucci alnewkirk: Al Newkirk +Altreus: Alastair McGowan-Douglas amiri: Amiri Barksdale amoore: Andrew Moore Andrew Mehta @@ -43,6 +44,7 @@ caelum: Rafael Kitover caldrin: Maik Hentsche castaway: Jess Robinson chorny: Alexandr Ciornii +cj: C.J. Adams-Collier claco: Christopher H. Laco clkao: CL Kao Ctrl-O http://ctrlo.com/ @@ -59,6 +61,7 @@ dim0xff: Dmitry Latin dkubb: Dan Kubb dnm: Justin Wheeler dpetrov: Dimitar Petrov +Dr^ZigMan: Robert Stone dsteinbrunner: David Steinbrunner duncan_dmg: Duncan Garland dwc: Daniel Westermann-Clark @@ -84,6 +87,7 @@ hobbs: Andrew Rodland Ian Wells idn: Ian Norton ilmari: Dagfinn Ilmari Mannsåker +ingy: Ingy döt Net initself: Mike Baas ironcamel: Naveed Massjouni jasonmay: Jason May @@ -208,6 +212,7 @@ willert: Sebastian Willert wintermute: Toby Corkindale wreis: Wallace Reis xenoterracide: Caleb Cushing +xmikew: Mike Wisener yrlnry: Mark Jason Dominus zamolxes: Bogdan Lucaciu Zefram: Andrew Main diff --git a/Changes b/Changes index ab49735..6314a30 100644 --- a/Changes +++ b/Changes @@ -18,6 +18,7 @@ Revision history for DBIx::Class * Misc - Remove warning about potential side effects of RT#79576 (scheduled) + - Various doc improvements (GH#35, GH#62, GH#66, GH#70, GH#71, GH#72) - Depend on newer Moo, to benefit from a safer runtime (RT#93004) - Fix intermittent failures in the LeakTracer on 5.18+ - Fix failures of t/54taint.t on Windows with spaces in the $^X diff --git a/examples/Schema/MyApp/Schema/Result/Artist.pm b/examples/Schema/MyApp/Schema/Result/Artist.pm index 70074b1..3cde36c 100644 --- a/examples/Schema/MyApp/Schema/Result/Artist.pm +++ b/examples/Schema/MyApp/Schema/Result/Artist.pm @@ -21,7 +21,7 @@ __PACKAGE__->set_primary_key('artistid'); __PACKAGE__->add_unique_constraint([qw( name )]); -__PACKAGE__->has_many('cds' => 'MyApp::Schema::Result::Cd'); +__PACKAGE__->has_many('cds' => 'MyApp::Schema::Result::Cd', 'artistid'); 1; diff --git a/examples/Schema/MyApp/Schema/Result/Cd.pm b/examples/Schema/MyApp/Schema/Result/Cd.pm index 9b0602c..d788744 100644 --- a/examples/Schema/MyApp/Schema/Result/Cd.pm +++ b/examples/Schema/MyApp/Schema/Result/Cd.pm @@ -12,7 +12,7 @@ __PACKAGE__->add_columns( data_type => 'integer', is_auto_increment => 1 }, - artist => { + artistid => { data_type => 'integer', }, title => { @@ -26,9 +26,9 @@ __PACKAGE__->add_columns( __PACKAGE__->set_primary_key('cdid'); -__PACKAGE__->add_unique_constraint([qw( title artist )]); +__PACKAGE__->add_unique_constraint([qw( title artistid )]); -__PACKAGE__->belongs_to('artist' => 'MyApp::Schema::Result::Artist'); -__PACKAGE__->has_many('tracks' => 'MyApp::Schema::Result::Track'); +__PACKAGE__->belongs_to('artist' => 'MyApp::Schema::Result::Artist', 'artistid'); +__PACKAGE__->has_many('tracks' => 'MyApp::Schema::Result::Track', 'cdid'); 1; diff --git a/examples/Schema/MyApp/Schema/Result/Track.pm b/examples/Schema/MyApp/Schema/Result/Track.pm index dc0951a..a32a27e 100644 --- a/examples/Schema/MyApp/Schema/Result/Track.pm +++ b/examples/Schema/MyApp/Schema/Result/Track.pm @@ -12,7 +12,7 @@ __PACKAGE__->add_columns( data_type => 'integer', is_auto_increment => 1 }, - cd => { + cdid => { data_type => 'integer', }, title => { @@ -22,8 +22,8 @@ __PACKAGE__->add_columns( __PACKAGE__->set_primary_key('trackid'); -__PACKAGE__->add_unique_constraint([qw( title cd )]); +__PACKAGE__->add_unique_constraint([qw( title cdid )]); -__PACKAGE__->belongs_to('cd' => 'MyApp::Schema::Result::Cd'); +__PACKAGE__->belongs_to('cd' => 'MyApp::Schema::Result::Cd', 'cdid'); 1; diff --git a/examples/Schema/insertdb.pl b/examples/Schema/insertdb.pl index c57460e..ae919b3 100755 --- a/examples/Schema/insertdb.pl +++ b/examples/Schema/insertdb.pl @@ -31,7 +31,7 @@ foreach my $lp (keys %albums) { } $schema->populate('Cd', [ - [qw/title artist/], + [qw/title artistid/], @cds, ]); @@ -55,6 +55,6 @@ foreach my $track (keys %tracks) { } $schema->populate('Track',[ - [qw/cd title/], + [qw/cdid title/], @tracks, ]); diff --git a/examples/Schema/testdb.pl b/examples/Schema/testdb.pl index 2a1061a..32cbd6d 100755 --- a/examples/Schema/testdb.pl +++ b/examples/Schema/testdb.pl @@ -53,7 +53,8 @@ sub get_tracks_by_artist { } ); while (my $track = $rs->next) { - print $track->title . "\n"; + print $track->title . " (from the CD '" . $track->cd->title + . "')\n"; } print "\n"; } @@ -70,7 +71,7 @@ sub get_cd_by_track { } ); my $cd = $rs->first; - print $cd->title . "\n\n"; + print $cd->title . " has the track '$tracktitle'.\n\n"; } sub get_cds_by_artist { @@ -104,7 +105,7 @@ sub get_artist_by_track { } ); my $artist = $rs->first; - print $artist->name . "\n\n"; + print $artist->name . " recorded the track '$tracktitle'.\n\n"; } sub get_artist_by_cd { @@ -119,5 +120,5 @@ sub get_artist_by_cd { } ); my $artist = $rs->first; - print $artist->name . "\n\n"; + print $artist->name . " recorded the CD '$cdtitle'.\n\n"; } diff --git a/lib/DBIx/Class/Manual/Cookbook.pod b/lib/DBIx/Class/Manual/Cookbook.pod index 7c2c58e..ef995fe 100644 --- a/lib/DBIx/Class/Manual/Cookbook.pod +++ b/lib/DBIx/Class/Manual/Cookbook.pod @@ -1770,11 +1770,11 @@ C<< [column_name => value] >>. =head2 Formatting DateTime objects in queries To ensure C conditions containing L arguments are properly -formatted to be understood by your RDBMS, you must use the C +formatted to be understood by your RDBMS, you must use the L formatter returned by L to format any L objects you pass to L conditions. Any L object attached to your -L provides a correct C formatter, so +L provides a correct L formatter, so all you have to do is: my $dtf = $schema->storage->datetime_parser; @@ -1793,12 +1793,11 @@ Without doing this the query will contain the simple stringification of the C object, which almost never matches the RDBMS expectations. This kludge is necessary only for conditions passed to -L, whereas -L, -L, -L (but not L) are all +L and L, +whereas L and +L (but not L) are L-aware and will do the right thing when supplied -an inflated C object. +an inflated L object. =head2 Using Unicode diff --git a/lib/DBIx/Class/Manual/Example.pod b/lib/DBIx/Class/Manual/Example.pod index 2fe95d7..3f9b882 100644 --- a/lib/DBIx/Class/Manual/Example.pod +++ b/lib/DBIx/Class/Manual/Example.pod @@ -8,361 +8,98 @@ This tutorial will guide you through the process of setting up and testing a very basic CD database using SQLite, with DBIx::Class::Schema as the database frontend. -The database consists of the following: - - table 'artist' with columns: artistid, name - table 'cd' with columns: cdid, artist, title, year - table 'track' with columns: trackid, cd, title +The database structure is based on the following rules: + An artist can have many cds, and each cd belongs to just one artist. + A cd can have many tracks, and each track belongs to just one cd. -And these rules exists: +The database is implemented with the following: - one artist can have many cds - one cd belongs to one artist - one cd can have many tracks - one track belongs to one cd + table 'artist' with columns: artistid, name + table 'cd' with columns: cdid, artistid, title, year + table 'track' with columns: trackid, cdid, title +Each of the table's first columns is the primary key; any subsequent +keys are foreign keys. =head2 Installation -Install DBIx::Class via CPAN should be sufficient. - -=head3 Create the database/tables - -First make and change the directory: - - mkdir app - cd app - mkdir db - cd db - -This example uses SQLite which is a dependency of DBIx::Class, so you -shouldn't have to install extra software. - -Save the following into a example.sql in the directory db - - CREATE TABLE artist ( - artistid INTEGER PRIMARY KEY, - name TEXT NOT NULL - ); - - CREATE TABLE cd ( - cdid INTEGER PRIMARY KEY, - artist INTEGER NOT NULL REFERENCES artist(artistid), - title TEXT NOT NULL - ); - - CREATE TABLE track ( - trackid INTEGER PRIMARY KEY, - cd INTEGER NOT NULL REFERENCES cd(cdid), - title TEXT NOT NULL - ); - -and create the SQLite database file: - - sqlite3 example.db < example.sql - -=head3 Set up DBIx::Class::Schema - -Change directory back from db to the directory app: - - cd ../ - -Now create some more directories: - - mkdir MyApp - mkdir MyApp/Schema - mkdir MyApp/Schema/Result - mkdir MyApp/Schema/ResultSet - -Then, create the following DBIx::Class::Schema classes: - -MyApp/Schema.pm: - - package MyApp::Schema; - use base qw/DBIx::Class::Schema/; - __PACKAGE__->load_namespaces; - - 1; - - -MyApp/Schema/Result/Artist.pm: - - package MyApp::Schema::Result::Artist; - use base qw/DBIx::Class::Core/; - __PACKAGE__->table('artist'); - __PACKAGE__->add_columns(qw/ artistid name /); - __PACKAGE__->set_primary_key('artistid'); - __PACKAGE__->has_many('cds' => 'MyApp::Schema::Result::Cd'); - - 1; - +You'll need to install DBIx::Class via CPAN, and you'll also need to +install sqlite3 (not sqlite) if it's not already intalled. -MyApp/Schema/Result/Cd.pm: +=head3 The database/tables/data - package MyApp::Schema::Result::Cd; - use base qw/DBIx::Class::Core/; - __PACKAGE__->load_components(qw/InflateColumn::DateTime/); - __PACKAGE__->table('cd'); - __PACKAGE__->add_columns(qw/ cdid artist title year/); - __PACKAGE__->set_primary_key('cdid'); - __PACKAGE__->belongs_to('artist' => 'MyApp::Schema::Result::Artist'); - __PACKAGE__->has_many('tracks' => 'MyApp::Schema::Result::Track'); +Your distribution already comes with a pre-filled SQLite database +F. You can see it by e.g. - 1; + cpanm --look DBIx::Class +If for some reason the file is unreadable on your system, you can +recreate it as follows: -MyApp/Schema/Result/Track.pm: + cp -a /examples/Schema dbicapp + cd dbicapp + rm db/example.db + sqlite3 db/example.db < db/example.sql + perl insertdb.pl - package MyApp::Schema::Result::Track; - use base qw/DBIx::Class::Core/; - __PACKAGE__->table('track'); - __PACKAGE__->add_columns(qw/ trackid cd title /); - __PACKAGE__->set_primary_key('trackid'); - __PACKAGE__->belongs_to('cd' => 'MyApp::Schema::Result::Cd'); +=head3 Testing the database - 1; +Enter the example Schema directory + cd /examples/Schema -=head3 Write a script to insert some records +Run the script testdb.pl, which will test that the database has +successfully been filled. -insertdb.pl +When this script is run, it should output the following: - #!/usr/bin/perl + get_tracks_by_cd(Bad): + Leave Me Alone + Smooth Criminal + Dirty Diana - use strict; - use warnings; + get_tracks_by_artist(Michael Jackson): + Billie Jean (from the CD 'Thriller') + Beat It (from the CD 'Thriller') + Leave Me Alone (from the CD 'Bad') + Smooth Criminal (from the CD 'Bad') + Dirty Diana (from the CD 'Bad') - use MyApp::Schema; + get_cd_by_track(Stan): + The Marshall Mathers LP has the track 'Stan'. - my $schema = MyApp::Schema->connect('dbi:SQLite:db/example.db'); + get_cds_by_artist(Michael Jackson): + Thriller + Bad - my @artists = (['Michael Jackson'], ['Eminem']); - $schema->populate('Artist', [ - [qw/name/], - @artists, - ]); + get_artist_by_track(Dirty Diana): + Michael Jackson recorded the track 'Dirty Diana'. - my %albums = ( - 'Thriller' => 'Michael Jackson', - 'Bad' => 'Michael Jackson', - 'The Marshall Mathers LP' => 'Eminem', - ); + get_artist_by_cd(The Marshall Mathers LP): + Eminem recorded the CD 'The Marshall Mathers LP'. - my @cds; - foreach my $lp (keys %albums) { - my $artist = $schema->resultset('Artist')->find({ - name => $albums{$lp} - }); - push @cds, [$lp, $artist->id]; - } - $schema->populate('Cd', [ - [qw/title artist/], - @cds, - ]); +=head3 Discussion about the results +The data model defined in this example has an artist with multiple CDs, +and a CD with multiple tracks; thus, it's simple to traverse from a +track back to a CD, and from there back to an artist. This is +demonstrated in the get_tracks_by_artist routine, where we easily walk +from the individual track back to the title of the CD that the track +came from ($track->cd->title). - my %tracks = ( - 'Beat It' => 'Thriller', - 'Billie Jean' => 'Thriller', - 'Dirty Diana' => 'Bad', - 'Smooth Criminal' => 'Bad', - 'Leave Me Alone' => 'Bad', - 'Stan' => 'The Marshall Mathers LP', - 'The Way I Am' => 'The Marshall Mathers LP', - ); - - my @tracks; - foreach my $track (keys %tracks) { - my $cdname = $schema->resultset('Cd')->find({ - title => $tracks{$track}, - }); - push @tracks, [$cdname->id, $track]; - } - - $schema->populate('Track',[ - [qw/cd title/], - @tracks, - ]); - -=head3 Create and run the test scripts - -testdb.pl: - - #!/usr/bin/perl - - use strict; - use warnings; - - use MyApp::Schema; - - my $schema = MyApp::Schema->connect('dbi:SQLite:db/example.db'); - # for other DSNs, e.g. MySQL, see the perldoc for the relevant dbd - # driver, e.g perldoc L. - - get_tracks_by_cd('Bad'); - get_tracks_by_artist('Michael Jackson'); - - get_cd_by_track('Stan'); - get_cds_by_artist('Michael Jackson'); - - get_artist_by_track('Dirty Diana'); - get_artist_by_cd('The Marshall Mathers LP'); - - - sub get_tracks_by_cd { - my $cdtitle = shift; - print "get_tracks_by_cd($cdtitle):\n"; - my $rs = $schema->resultset('Track')->search( - { - 'cd.title' => $cdtitle - }, - { - join => [qw/ cd /], - } - ); - while (my $track = $rs->next) { - print $track->title . "\n"; - } - print "\n"; - } - - sub get_tracks_by_artist { - my $artistname = shift; - print "get_tracks_by_artist($artistname):\n"; - my $rs = $schema->resultset('Track')->search( - { - 'artist.name' => $artistname - }, - { - join => { - 'cd' => 'artist' - }, - } - ); - while (my $track = $rs->next) { - print $track->title . "\n"; - } - print "\n"; - } - - - sub get_cd_by_track { - my $tracktitle = shift; - print "get_cd_by_track($tracktitle):\n"; - my $rs = $schema->resultset('Cd')->search( - { - 'tracks.title' => $tracktitle - }, - { - join => [qw/ tracks /], - } - ); - my $cd = $rs->first; - print $cd->title . "\n\n"; - } - - sub get_cds_by_artist { - my $artistname = shift; - print "get_cds_by_artist($artistname):\n"; - my $rs = $schema->resultset('Cd')->search( - { - 'artist.name' => $artistname - }, - { - join => [qw/ artist /], - } - ); - while (my $cd = $rs->next) { - print $cd->title . "\n"; - } - print "\n"; - } - - - - sub get_artist_by_track { - my $tracktitle = shift; - print "get_artist_by_track($tracktitle):\n"; - my $rs = $schema->resultset('Artist')->search( - { - 'tracks.title' => $tracktitle - }, - { - join => { - 'cds' => 'tracks' - } - } - ); - my $artist = $rs->first; - print $artist->name . "\n\n"; - } - - sub get_artist_by_cd { - my $cdtitle = shift; - print "get_artist_by_cd($cdtitle):\n"; - my $rs = $schema->resultset('Artist')->search( - { - 'cds.title' => $cdtitle - }, - { - join => [qw/ cds /], - } - ); - my $artist = $rs->first; - print $artist->name . "\n\n"; - } - - - -It should output: - - get_tracks_by_cd(Bad): - Dirty Diana - Smooth Criminal - Leave Me Alone - - get_tracks_by_artist(Michael Jackson): - Beat it - Billie Jean - Dirty Diana - Smooth Criminal - Leave Me Alone - - get_cd_by_track(Stan): - The Marshall Mathers LP - - get_cds_by_artist(Michael Jackson): - Thriller - Bad - - get_artist_by_track(Dirty Diana): - Michael Jackson - - get_artist_by_cd(The Marshall Mathers LP): - Eminem - -=head1 Notes - -A reference implementation of the database and scripts in this example -are available in the main distribution for DBIx::Class under the -directory F. - -With these scripts we're relying on @INC looking in the current -working directory. You may want to add the MyApp namespaces to -@INC in a different way when it comes to deployment. - -The F script is an excellent start for testing your database -model. +Note also that in the get_tracks_by_cd and get_tracks_by_artist +routines, the result set is called multiple times with the 'next' +iterator. In contrast, get_cd_by_track uses the 'first' result set +method, since only one CD is expected to have a specific track. This example uses L to load in the appropriate L classes from the C namespace, and any required L classes from the -C namespace (although we created the directory -in the directions above we did not add, or need to add, any resultset -classes). +C namespace (although we did not add, nor needed +any such classes in this example). =head1 FURTHER QUESTIONS? diff --git a/lib/DBIx/Class/Manual/FAQ.pod b/lib/DBIx/Class/Manual/FAQ.pod index 4a78e24..b8dbe17 100644 --- a/lib/DBIx/Class/Manual/FAQ.pod +++ b/lib/DBIx/Class/Manual/FAQ.pod @@ -82,8 +82,8 @@ L call. Eg: __PACKAGE__->table('mydb.mytablename'); -And load all the Result classes for both / all databases using one -L call. +And load all the Result classes for both / all databases by calling +L. =item .. use DBIx::Class across PostgreSQL/DB2/Oracle schemas? @@ -262,6 +262,39 @@ alter session statements on database connection establishment: ->on_connect_do("ALTER SESSION SET NLS_SORT = 'BINARY_CI'"); ->on_connect_do("ALTER SESSION SET NLS_SORT = 'GERMAN_CI'"); +=item .. format a DateTime object for searching? + +L and L +do not take L into account, and so your L +object will not be correctly deflated into a format your RDBMS expects. + +The L method on your +storage object can be used to return the object that would normally do this, so +it's easy to do it manually: + + my $dtf = $schema->storage->datetime_parser; + my $rs = $schema->resultset('users')->search( + { + signup_date => { + -between => [ + $dtf->format_datetime($dt_start), + $dtf->format_datetime($dt_end), + ], + } + }, + ); + +With in a Result Class method, you can get this from the +L|DBIx::Class::Row/result_source>. + + my $dtf = $self->result_source->storage->datetime_parser; + +This kludge is necessary only for conditions passed to +L and L, +whereas L and L +(but not L) are +L-aware and will do the right thing when supplied +an inflated L object. =back diff --git a/lib/DBIx/Class/ResultSet.pm b/lib/DBIx/Class/ResultSet.pm index e30e14c..a8b3471 100644 --- a/lib/DBIx/Class/ResultSet.pm +++ b/lib/DBIx/Class/ResultSet.pm @@ -3323,6 +3323,9 @@ source alias of the current result set: }); } +The alias of L can be altered by the +L. + =cut sub current_source_alias { @@ -4086,13 +4089,13 @@ is the same as as => [qw(some_column dbic_slot)] If you want to individually retrieve related columns (in essence perform -manual prefetch) you have to make sure to specify the correct inflation slot +manual L) you have to make sure to specify the correct inflation slot chain such that it matches existing relationships: my $rs = $schema->resultset('Artist')->search({}, { # required to tell DBIC to collapse has_many relationships collapse => 1, - join => { cds => 'tracks'}, + join => { cds => 'tracks' }, '+columns' => { 'cds.cdid' => 'cds.cdid', 'cds.tracks.title' => 'tracks.title', diff --git a/lib/DBIx/Class/ResultSource.pm b/lib/DBIx/Class/ResultSource.pm index 4b8f1dd..c88361f 100644 --- a/lib/DBIx/Class/ResultSource.pm +++ b/lib/DBIx/Class/ResultSource.pm @@ -211,6 +211,12 @@ The length of your column, if it is a column type that can have a size restriction. This is currently only used to create tables from your schema, see L. + { size => [ 9, 6 ] } + +For decimal or float values you can specify an ArrayRef in order to +control precision, assuming your database's +L supports it. + =item is_nullable { is_nullable => 1 } diff --git a/lib/DBIx/Class/Storage/DBI.pm b/lib/DBIx/Class/Storage/DBI.pm index 9d443a1..51c72e9 100644 --- a/lib/DBIx/Class/Storage/DBI.pm +++ b/lib/DBIx/Class/Storage/DBI.pm @@ -1669,8 +1669,8 @@ sub _gen_sql_bind { ) { carp_unique 'DateTime objects passed to search() are not supported ' . 'properly (InflateColumn::DateTime formats and settings are not ' - . 'respected.) See "Formatting DateTime objects in queries" in ' - . 'DBIx::Class::Manual::Cookbook. To disable this warning for good ' + . 'respected.) See ".. format a DateTime object for searching?" in ' + . 'DBIx::Class::Manual::FAQ. To disable this warning for good ' . 'set $ENV{DBIC_DT_SEARCH_OK} to true' }