Kato Atsushi
ktats****@users*****
2007年 6月 19日 (火) 09:23:36 JST
Index: docs/modules/DBIx-Class-0.07006/lib/DBIx/Class/Manual/Cookbook.pod diff -u /dev/null docs/modules/DBIx-Class-0.07006/lib/DBIx/Class/Manual/Cookbook.pod:1.1 --- /dev/null Tue Jun 19 09:23:36 2007 +++ docs/modules/DBIx-Class-0.07006/lib/DBIx/Class/Manual/Cookbook.pod Tue Jun 19 09:23:36 2007 @@ -0,0 +1,1428 @@ +=head1 åå + +DBIx::Class::Manual::Cookbook - ã¬ã·ããããã + +=head1 ã¬ã·ã + +=head2 æ¤ç´¢ + +=head3 ãã¼ã¸å¦çãããçµæã»ãã + +When you expect a large number of results, you can ask L<DBIx::Class> for a +paged resultset, which will fetch only a small number of records at a time: + +çµæã»ãããè¨å¤§ã«ãªããããªãããã¼ã¸å¦çãããçµæãL<DBIx::Class>ã§åå¾ã§ãã¾ãã +ä¸åã«ãå°ãã®ã¬ã³ã¼ãããã¨ã£ã¦ãã¾ãã: + + my $rs = $schema->resultset('Artist')->search( + undef, + { + page => 1, # page to return (defaults to 1) + rows => 10, # number of results per page + }, + ); + + return $rs->all(); # all records for page 1 + +The C<page> attribute does not have to be specified in your search: + +C<page>å±æ§ã¯æ¤ç´¢ã«æå®ããå¿ è¦ã¯ããã¾ãã: + + my $rs = $schema->resultset('Artist')->search( + undef, + { + rows => 10, + } + ); + + return $rs->page(1); # DBIx::Class::ResultSet containing first 10 records + +In either of the above cases, you can return a L<Data::Page> object for the +resultset (suitable for use in e.g. a template) using the C<pager> method: + +ä¸ã®ã±ã¼ã¹ã§ããçµæã»ããã«ãL<Data::Page>ãªãã¸ã§ã¯ã(ãã³ãã¬ã¼ãã«ä½¿ãã®ã«é©ãã) +ãå¾ããã¨ãã§ãã¾ããC<pager>ã¡ã½ããã使ã£ã¦: + + return $rs->pager(); + +=head3 è¤é㪠WHEREç¯ + +Sometimes you need to formulate a query using specific operators: + +ç¹å®ã®æ¼ç®åã使ã£ãã¯ã¨ãªãå®å¼åããå¿ è¦ãããæãããã§ããã: + + my @albums = $schema->resultset('Album')->search({ + artist => { 'like', '%Lamb%' }, + title => { 'like', '%Fear of Fours%' }, + }); + +This results in something like the following C<WHERE> clause: + +çµæã¯ä¸è¨ã®C<WHERE>ç¯ã®ãããªãã®ã«ãªãã¾ã: + + WHERE artist LIKE '%Lamb%' AND title LIKE '%Fear of Fours%' + +Other queries might require slightly more complex logic: + +ããã¡ãã£ã¨è¤éãªãã¸ãã¯ãå¿ è¦ãªä»ã®ã¯ã¨ãªãããã§ããã: + + my @albums = $schema->resultset('Album')->search({ + -or => [ + -and => [ + artist => { 'like', '%Smashing Pumpkins%' }, + title => 'Siamese Dream', + ], + artist => 'Starchildren', + ], + }); + +This results in the following C<WHERE> clause: + +çµæã¯ä¸è¨ã®C<WHERE>ç¯ã«ãªãã¾ã: + + WHERE ( artist LIKE '%Smashing Pumpkins%' AND title = 'Siamese Dream' ) + OR artist = 'Starchildren' + +For more information on generating complex queries, see +L<SQL::Abstract/WHERE CLAUSES>. + +è¤éãªã¯ã¨ãªãã¤ããããã®ãã詳ãã説æã¯ãL<SQL::Abstract/WHERE CLAUSES>ãè¦ã¦ãã ããã + +=head3 ç¹å®ã®ã«ã©ã ã使ã + +When you only want specific columns from a table, you can use +C<columns> to specify which ones you need. This is useful to avoid +loading columns with large amounts of data that you aren't about to +use anyway: + +ãã¼ãã«ããç¹å®ã®ã«ã©ã ã欲ããã ãã®ã¨ãã«ã¯ãC<columns>ã使ã£ã¦ã +å¿ è¦ãªãã®ãæå®ã§ãã¾ããä½ã«ã使ããªã大éã®ãã¼ã¿ãåãè¾¼ãã®ã +é¿ãããã¨ãã§ãã¾ãã + + my $rs = $schema->resultset('Artist')->search( + undef, + { + columns => [qw/ name /] + } + ); + + # Equivalent SQL: + # SELECT artist.name FROM artist + +This is a shortcut for C<select> and C<as>, see below. C<columns> +cannot be used together with C<select> and C<as>. + +å¾ã§ãè¦ã¾ãããããã¯ãC<select>ã¨C<as>ã®ã·ã§ã¼ãã«ããã§ãã +C<columns>ã¯C<select>ã¨C<as>ã¨ä¸ç·ã«ã¯ä½¿ãã¾ããã + +=head3 ãã¼ã¿ãã¼ã¹ã®é¢æ°ãã¹ãã¢ãããã·ã¼ã¸ã£ã使ã + +The combination of C<select> and C<as> can be used to return the result of a +database function or stored procedure as a column value. You use C<select> to +specify the source for your column value (e.g. a column name, function, or +stored procedure name). You then use C<as> to set the column name you will use +to access the returned value: + +C<select>ã¨C<as>ã®çµã¿åããã§ãã«ã©ã ã®å¤ã¨ãã¦ãã¼ã¿ãã¼ã¹é¢æ°ãã¹ãã¢ã +ããã·ã¼ã¸ã£ã®çµæãè¿ãã®ã«ä½¿ããã¨ãåºæ¥ã¾ããC<select>ã使ã£ã¦ãã«ã©ã ã® +å¤ã®ããã®ã½ã¼ã¹ãæå®ã§ãã¾ã(ä¾ãã°ãã«ã©ã åãé¢æ°ãã¹ãã¢ãããã·ã¼ã¸ã£å)ã +ãããããC<as>ããè¿ãããå¤ã«ã¢ã¯ã»ã¹ããã®ã«ä½¿ãã«ã©ã åãã»ããããã®ã« +使ãã¾ã: + + my $rs = $schema->resultset('Artist')->search( + {}, + { + select => [ 'name', { LENGTH => 'name' } ], + as => [qw/ name name_length /], + } + ); + + # Equivalent SQL: + # SELECT name name, LENGTH( name ) + # FROM artist + +Note that the C< as > attribute has absolutely nothing to with the sql +syntax C< SELECT foo AS bar > (see the documentation in +L<DBIx::Class::ResultSet/ATTRIBUTES>). If your alias exists as a +column in your base class (i.e. it was added with C<add_columns>), you +just access it as normal. Our C<Artist> class has a C<name> column, so +we just use the C<name> accessor: + +C< as >å±æ§ã¯ãSQLã®ã·ã³ã¿ãã¯ã¹C< SELECT foo AS bar>ã¨ã¾ã£ããé¢ä¿ãªããã¨ã« +æ°ãã¤ãã¦ãã ãã(L<DBIx::Class::ResultSet/ATTRIBUTES>ã®ããã¥ã¡ã³ããè¦ã¦ãã ãã)ã +ãã¼ã¹ã¯ã©ã¹ã«ã«ã©ã ã¨ãã¦ã¨ã¤ãªã¢ã¹ããã(ããªãã¡ãC<add_columns>ã§è¿½å ããã¦ãã)ã®ãªãã +æ®éã«ããã«ã¢ã¯ã»ã¹ã§ãã¾ããC<Artist>ã¯ã©ã¹ã«ã¯ãC<name>ã«ã©ã ãããã®ã§ã +C<name>ã¢ã¯ã»ãµã使ãã¾ã: + + my $artist = $rs->first(); + my $name = $artist->name(); + +If on the other hand the alias does not correspond to an existing column, you +have to fetch the value using the C<get_column> accessor: + +ä¸æ¹ã§ãã¨ã¤ãªã¢ã¹ãæ¢åã®ã«ã©ã ã«ä¸è´ããªããªããC<get_column>ã¢ã¯ã»ãµã使ã£ã¦ã +å¤ãåå¾ããå¿ è¦ãããã¾ã: + + my $name_length = $artist->get_column('name_length'); + +If you don't like using C<get_column>, you can always create an accessor for +any of your aliases using either of these: + +C<get_column>ãæ°ã«å ¥ããªããã°ããã¤ã§ããä¸è¨ã®ããããã使ã£ã¦ã©ã㪠+ã¨ã¤ãªã¢ã¹ã«ãã¢ã¯ã»ãµãä½ãã¾ã: + + # Define accessor manually: + sub name_length { shift->get_column('name_length'); } + + # Or use DBIx::Class::AccessorGroup: + __PACKAGE__->mk_group_accessors('column' => 'name_length'); + +=head3 SELECT DISTINCT with multiple columns + + my $rs = $schema->resultset('Foo')->search( + {}, + { + select => [ + { distinct => [ $source->columns ] } + ], + as => [ $source->columns ] # remember 'as' is not the same as SQL AS :-) + } + ); + + my $count = $rs->next->get_column('count'); + +=head3 SELECT COUNT(DISTINCT colname) + + my $rs = $schema->resultset('Foo')->search( + {}, + { + select => [ + { count => { distinct => 'colname' } } + ], + as => [ 'count' ] + } + ); + +=head3 çµæã®ã°ã«ã¼ãã³ã° + +L<DBIx::Class> supports C<GROUP BY> as follows: + +L<DBIx::Class>ã¯C<GROUP BY>ããµãã¼ããã¾ã: + + my $rs = $schema->resultset('Artist')->search( + {}, + { + join => [qw/ cds /], + select => [ 'name', { count => 'cds.cdid' } ], + as => [qw/ name cd_count /], + group_by => [qw/ name /] + } + ); + + # Equivalent SQL: + # SELECT name, COUNT( cds.cdid ) FROM artist me + # LEFT JOIN cd cds ON ( cds.artist = me.artistid ) + # GROUP BY name + +Please see L<DBIx::Class::ResultSet/ATTRIBUTES> documentation if you +are in any way unsure about the use of the attributes above (C< join +>, C< select >, C< as > and C< group_by >). + +ä¸è¨ã®(C< join >ã C< select >ã C< as >ã C< group_by>)å±æ§ã®ä½¿ãæ¹ãããããªããã°ã +L<DBIx::Class::ResultSet/ATTRIBUTES>ããã¥ã¡ã³ããã¿ã¦ãã ããã + +=head3 å ã«å®ç¾©ãããæ¤ç´¢ + +You can write your own L<DBIx::Class::ResultSet> class by inheriting from it +and define often used searches as methods: + +L<DBIx::Class::ResultSet>ã¯ã©ã¹ãç¶æ¿ãã¦ãèªåèªèº«ã®ã¯ã©ã¹ãæ¸ãããã使ã +æ¤ç´¢ãã¡ã½ããã¨ãã¦å®ç¾©ã§ãã¾ã: + + package My::DBIC::ResultSet::CD; + use strict; + use warnings; + use base 'DBIx::Class::ResultSet'; + + sub search_cds_ordered { + my ($self) = @_; + + return $self->search( + {}, + { order_by => 'name DESC' }, + ); + } + + 1; + +To use your resultset, first tell DBIx::Class to create an instance of it +for you, in your My::DBIC::Schema::CD class: + +èªåã®çµæã»ãã使ãã«ã¯ãæåã«ãèªåã®My::DBIC::Schema::CDã¯ã©ã¹ã®ä¸ã§ã +DBIx::Classã«ãã®ã¤ã³ã¹ã¿ã³ã¹ãä½ãããã«æãã¾ãã + + __PACKAGE__->resultset_class('My::DBIC::ResultSet::CD'); + +Then call your new method in your code: + +ãããããã³ã¼ãã®ä¸ã§ãæ°ããå£ç½ã£åº¦ãå¼ã³ã¾ã: + + my $ordered_cds = $schema->resultset('CD')->search_cds_ordered(); + + +=head3 ResultSetã¯ã©ã¹ãæ¸ãããªããå ã«å®ç¾©ãããæ¤ç´¢ + +Alternatively you can automatically generate a DBIx::Class::ResultSet +class by using the ResultSetManager component and tagging your method +as ResultSet: + +å¥ã®ããããã¨ãã¦ãResultSetManagerã³ã³ãã¼ãã³ãã使ããResultSetã¨ã㦠+èªåã®ã¡ã½ãããã¿ã®ã³ã°ãããã¨ã§ãDBIx::Class::ResultSetã¯ã©ã¹ãèªåçã«ä½ãã¾ãã + + __PACKAGE__->load_components(qw/ ResultSetManager Core /); + + sub search_cds_ordered : ResultSet { + my ($self) = @_; + return $self->search( + {}, + { order_by => 'name DESC' }, + ); + } + +Then call your method in the same way from your code: + +ããã§ãã³ã¼ãããåãæ¹æ³ã§å¼ã¹ã¾ã: + + my $ordered_cds = $schema->resultset('CD')->search_cds_ordered(); + +=head2 joins 㨠prefetch ã使ã + +You can use the C<join> attribute to allow searching on, or sorting your +results by, one or more columns in a related table. To return all CDs matching +a particular artist name: + +C<join>å±æ§ã使ã£ã¦ãé¢é£ãããã¼ãã«ã®1ã¤ä»¥ä¸ã®ã«ã©ã ã使ã£ã¦ã +æ¤ç´¢ããçµæã®ã½ã¼ããã§ãã¾ããç¹å®ã®ã¢ã¼ãã£ã¹ãåã®å ¨ã¦ã®CDã +è¿ãããã«ã¯: + + my $rs = $schema->resultset('CD')->search( + { + 'artist.name' => 'Bob Marley' + }, + { + join => [qw/artist/], # join the artist table + } + ); + + # Equivalent SQL: + # SELECT cd.* FROM cd + # JOIN artist ON cd.artist = artist.id + # WHERE artist.name = 'Bob Marley' + +If required, you can now sort on any column in the related tables by including +it in your C<order_by> attribute: + +å¿ è¦ãªããC<order_by>å±æ§ã«ãããå«ãã¦ãé¢é£ãããã¼ãã«ã®ããããã®ã«ã©ã 㧠+ã½ã¼ãæºããã¨ãåºæ¥ã¾ãã + + my $rs = $schema->resultset('CD')->search( + { + 'artist.name' => 'Bob Marley' + }, + { + join => [qw/ artist /], + order_by => [qw/ artist.name /] + } + }; + + # Equivalent SQL: + # SELECT cd.* FROM cd + # JOIN artist ON cd.artist = artist.id + # WHERE artist.name = 'Bob Marley' + # ORDER BY artist.name + +Note that the C<join> attribute should only be used when you need to search or +sort using columns in a related table. Joining related tables when you only +need columns from the main table will make performance worse! + +C<join>å±æ§ã¯é¢é£ãããã¼ãã«ã®ã«ã©ã ã使ã£ã¦æ¤ç´¢ãã½ã¼ããããå¿ è¦ãããã¨ãã«ã®ã¿ +使ãããã¹ãã ã¨ãããã¨ã«æ³¨æãã¦ãã ããã +ã¡ã¤ã³ã®ãã¼ãã«ããã«ã©ã ãå¿ è¦ãªã¨ãã«ãé¢é£ãããã¼ãã«ãçµåããã®ã¯ã +ããã©ã¼ãã³ã¹ãæªãã§ã! + +Now let's say you want to display a list of CDs, each with the name of the +artist. The following will work fine: + +ã§ããCDã®ãªã¹ãããããããã®ã¢ã¼ãã£ã¹ãã®ååã¨ä¸ç·ã«è¡¨ç¤ºãããã¨ãã¾ãããã +ä¸è¨ã®ããããã§ãã¾ãããã¾ã: + + while (my $cd = $rs->next) { + print "CD: " . $cd->title . ", Artist: " . $cd->artist->name; + } + +There is a problem however. We have searched both the C<cd> and C<artist> tables +in our main query, but we have only returned data from the C<cd> table. To get +the artist name for any of the CD objects returned, L<DBIx::Class> will go back +to the database: + +ã§ãããä¸ã¤åé¡ãããã¾ãããã®ã¡ã¤ã³ã¯ã¨ãªã§ãC<cd>ã¨C<artist>ãã¼ãã«ã®ä¸¡æ¹ã +æ¤ç´¢ãã¦ãã¾ãããC<cd>ããã®ã¿ãã¼ã¿ãè¿ããã¦ãã¾ããè¿ãããCDãªãã¸ã§ã¯ãã®ä¸é¨ã§ã +ã¢ã¼ãã£ã¹ãåãå¾ãããã«ãL<DBIx::Class>ã¯ããã¼ã¿ãã¼ã¹ã«æ»ãã¾ã: + + SELECT artist.* FROM artist WHERE artist.id = ? + +A statement like the one above will run for each and every CD returned by our +main query. Five CDs, five extra queries. A hundred CDs, one hundred extra +queries! + +ä¸è¨ã®ãããªã¹ãã¼ãã¡ã³ãããã¡ã¤ã³ã¯ã¨ãªã«ãã£ã¦è¿ããããããããã®ãå ¨ã¦ã® +CDã§èµ°ãã¾ãã5ã¤ã®CDã§ããã°ã5ã¤ã®å¥ã®ã¯ã¨ãªã§ãã100ã®CDã§ããã°ã100ã®å¥ã® +ã¯ã¨ãª! + +Thankfully, L<DBIx::Class> has a C<prefetch> attribute to solve this problem. +This allows you to fetch results from related tables in advance: + +ããããããã¨ã«ãL<DBIx::Class>ã¯ãC<prefetch>å±æ§ãããããã®åé¡ã解決ã§ãã¾ãã +ãã®å±æ§ã使ãã¨ãå ã«é¢é£ãããã¼ãã«ããçµæãã¨ã£ã¦ããã¾ã: + + + my $rs = $schema->resultset('CD')->search( + { + 'artist.name' => 'Bob Marley' + }, + { + join => [qw/ artist /], + order_by => [qw/ artist.name /], + prefetch => [qw/ artist /] # return artist data too! + } + ); + + # Equivalent SQL (note SELECT from both "cd" and "artist"): + # SELECT cd.*, artist.* FROM cd + # JOIN artist ON cd.artist = artist.id + # WHERE artist.name = 'Bob Marley' + # ORDER BY artist.name + +The code to print the CD list remains the same: + +CDã®ãªã¹ãã表示ããã³ã¼ãã¯åãç©ã使ãã¾ã: + + while (my $cd = $rs->next) { + print "CD: " . $cd->title . ", Artist: " . $cd->artist->name; + } + +L<DBIx::Class> has now prefetched all matching data from the C<artist> table, +so no additional SQL statements are executed. You now have a much more +efficient query. + +L<DBIx::Class>ã¯C<artist>ãã¼ãã«ãããã¹ã¦ã®ããããããã¼ã¿ãå ã«ã¨ã£ã¦ãã¦ãã¾ãã +ãã®ãããä½åãªSQLã¹ãã¼ãã¡ã³ãã¯å®è¡ããã¾ãããããå¹ççãªã¯ã¨ãªã«ãªãã¾ããã + +Note that as of L<DBIx::Class> 0.05999_01, C<prefetch> I<can> be used with +C<has_many> relationships. + +L<DBIx::Class> 0.05999_01ã®è¾å ¸ã§ã C<has_many>ã®ãªã¬ã¼ã·ã§ã³ã·ããã§ã +C<prefetch>ãI<使ã>ãã®ã«æ³¨æãã¦ãã ããã + +Also note that C<prefetch> should only be used when you know you will +definitely use data from a related table. Pre-fetching related tables when you +only need columns from the main table will make performance worse! + + +ã¾ããC<prefetch>ã¯ãé¢é£ãããã¼ãã«ãããã¼ã¿ãå¿ ã使ãã¨ããã£ã¦ããã¨ãã®ã¿ã«ã +使ãã¹ãã§ããã¡ã¤ã³ãã¼ãã«ããã®ã«ã©ã ããå¿ è¦ã¨ããªããªãã +é¢é£ãããã¼ãã«ããå ã«åå¾ããã®ã¯ãããã©ã¼ãã³ã¹ãæªããã¾ã! + +=head3 ãã«ãã¹ãããã®çµå(join) + +Sometimes you want to join more than one relationship deep. In this example, +we want to find all C<Artist> objects who have C<CD>s whose C<LinerNotes> +contain a specific string: + +ã²ã¨ã¤ä»¥ä¸ã®æ·±ããªã¬ã¼ã·ã§ã³ã·ããã§joinãããã¨ããããã§ãããã +ãã®ä¾ã§ã¯ãC<LinerNotes>ã«ç¹å®ã®æåãå«ã¾ããC<CD>ãæã£ã¦ããã +C<Artist>ãªãã¸ã§ã¯ããæ¢ãããã¨ãã¾ã: + + # Relationships defined elsewhere: + # Artist->has_many('cds' => 'CD', 'artist'); + # CD->has_one('liner_notes' => 'LinerNotes', 'cd'); + + my $rs = $schema->resultset('Artist')->search( + { + 'liner_notes.notes' => { 'like', '%some text%' }, + }, + { + join => { + 'cds' => 'liner_notes' + } + } + ); + + # Equivalent SQL: + # SELECT artist.* FROM artist + # JOIN ( cd ON artist.id = cd.artist ) + # JOIN ( liner_notes ON cd.id = liner_notes.cd ) + # WHERE liner_notes.notes LIKE '%some text%' + +Joins can be nested to an arbitrary level. So if we decide later that we +want to reduce the number of Artists returned based on who wrote the liner +notes: + +çµåã¯ä»»æã®ã¬ãã«ã§ãã¹ãã§ãã¾ããã§ãã®ã§ãå¾ãããã©ã¤ãã¼ãã¼ãã +誰ãæ¸ããããå ã«ãè¿ãããã¢ã¼ãã£ã¹ãã®æ°ãæ¸ããããã¨æ±ºããã¨ããã: + + # Relationship defined elsewhere: + # LinerNotes->belongs_to('author' => 'Person'); + + my $rs = $schema->resultset('Artist')->search( + { + 'liner_notes.notes' => { 'like', '%some text%' }, + 'author.name' => 'A. Writer' + }, + { + join => { + 'cds' => { + 'liner_notes' => 'author' + } + } + } + ); + + # Equivalent SQL: + # SELECT artist.* FROM artist + # JOIN ( cd ON artist.id = cd.artist ) + # JOIN ( liner_notes ON cd.id = liner_notes.cd ) + # JOIN ( author ON author.id = liner_notes.author ) + # WHERE liner_notes.notes LIKE '%some text%' + # AND author.name = 'A. Writer' + +=head2 ãã«ãã¹ãããã®prefetch + +From 0.04999_05 onwards, C<prefetch> can be nested more than one relationship +deep using the same syntax as a multi-step join: + +0.04999_05以åãããC<prefetch>ã¯ããã«ãã¹ãããã®çµåã¨åãã·ã³ã¿ãã¯ã¹ã§ã +ä¸ã¤ä»¥ä¸ã®æ·±ããªã¬ã¼ã·ã§ã³ã·ããã§ãã¹ãã§ãã¾ãã: + + my $rs = $schema->resultset('Tag')->search( + {}, + { + prefetch => { + cd => 'artist' + } + } + ); + + # Equivalent SQL: + # SELECT tag.*, cd.*, artist.* FROM tag + # JOIN cd ON tag.cd = cd.cdid + # JOIN artist ON cd.artist = artist.artistid + +Now accessing our C<cd> and C<artist> relationships does not need additional +SQL statements: + + +ããã§ãC<cd>ã¨C<artist>ã®ãªã¬ã¼ã·ã§ã³ã·ããã«ã¢ã¯ã»ã¹ããã®ã«ã +追å ã®SQLã¹ãã¼ãã¡ã³ãã¯å¿ è¦ããã¾ãã: + + my $tag = $rs->first; + print $tag->cd->artist->name; + +=head2 Columns of data + +If you want to find the sum of a particular column there are several +ways, the obvious one is to use search: + +ç¹å®ã®ã«ã©ã ã®åè¨ãæ¢ããããã°ãããã¤ãã®æ¹æ³ãããã¾ããèªæã®ãã®ã¨ãã¦ã¯ã +searchã使ããã®ã§ã: + + my $rs = $schema->resultset('Items')->search( + {}, + { + select => [ { sum => 'Cost' } ], + as => [ 'total_cost' ], # remember this 'as' is for DBIx::Class::ResultSet not SQL + } + ); + my $tc = $rs->first->get_column('total_cost'); + +Or, you can use the L<DBIx::Class::ResultSetColumn>, which gets +returned when you ask the C<ResultSet> for a column using +C<get_column>: + +ãããã¯ãL<DBIx::Class::ResultSetColumn>ã使ããã¨ãåºæ¥ã¾ãã +ããã¯ãC<ResultSet>ã§C<get_column>ã使ã£ã¦ã«ã©ã ãåãã¨ãã« +è¿ããããã®ãåãã¾ãã + + my $cost = $schema->resultset('Items')->get_column('Cost'); + my $tc = $cost->sum; + +With this you can also do: + +ãããã次ã®ããã«ã§ãã¾ã: + + my $minvalue = $cost->min; + my $maxvalue = $cost->max; + +Or just iterate through the values of this column only: + +ã¾ãã¯ããã®ã«ã©ã ã®å¤ã®ã¿ãéãã¦ã¤ãã¬ã¼ããã¾ã: + + while ( my $c = $cost->next ) { + print $c; + } + + foreach my $c ($cost->all) { + print $c; + } + +C<ResultSetColumn> only has a limited number of built-in functions, if +you need one that it doesn't have, then you can use the C<func> method +instead: + +C<ResultSetColumn>ã¯å°ãã ããã«ãã¤ã³ã®é¢æ°ãããã¾ãã +ããã«ãªããã®ãå¿ è¦ãªããC<func>ã¡ã½ãããå¤ããã«ä½¿ããã¨ãã§ãã¾ã: + + my $avg = $cost->func('AVERAGE'); + +This will cause the following SQL statement to be run: + +ããããã¨ãä¸è¨ã®SQLã¹ãã¼ãã¡ã³ããèµ°ãã¾ã: + + SELECT AVERAGE(Cost) FROM Items me + +Which will of course only work if your database supports this function. +See L<DBIx::Class::ResultSetColumn> for more documentation. + +ãã¡ããã使ã£ã¦ãããã¼ã¿ãã¼ã¹ããã®é¢æ°ããµãã¼ããã¦ããªããã°ããã¾ããã +ãã詳ããã¯ãL<DBIx::Class::ResultSetColumn>ãã¿ã¦ãã ããã + +=head2 ãªã¬ã¼ã·ã§ã³ã·ããã使ã + +=head3 é¢é£ãããã¼ãã«ã«æ°ããåãä½ã + + my $book->create_related('author', { name => 'Fred'}); + +=head3 é¢é£ãããã¼ãã«ãæ¤ç´¢ãã + +Only searches for books named 'Titanic' by the author in $author. + +$autorã®èè ã§ã'Titanic'ã¨ããååã®æ¬ã ããæ¤ç´¢ãããã + + my $author->search_related('books', { name => 'Titanic' }); + +=head3 é¢é£ãããã¼ãã«ã®ãã¼ã¿ãåé¤ãã + +Deletes only the book named Titanic by the author in $author. + +$autorã®èè ã§ãTitanicã¨ããååã®æ¬ã ããåé¤ãããã + + my $author->delete_related('books', { name => 'Titanic' }); + +=head3 é¢ä¿ããçµæã»ããã®é åºä»ã + +If you always want a relation to be ordered, you can specify this when you +create the relationship. + +é åºä»ããããé¢ä¿ã常ã«ã»ãããªãããªã¬ã¼ã·ã§ã³ã·ãããä½ãã¨ãã«ã次ã®æå®ãã§ãã¾ãã + +To order C<< $book->pages >> by descending page_number. + +page_numberãéé ã§ãC<< $book->pages >>ã並ã³å¤ããããªãã + + Book->has_many('pages' => 'Page', 'book', { order_by => \'page_number DESC'} ); + +=head2 ãã©ã³ã¶ã¯ã·ã§ã³ + +As of version 0.04001, there is improved transaction support in +L<DBIx::Class::Storage::DBI> and L<DBIx::Class::Schema>. Here is an +example of the recommended way to use it: + +0.04001以åã«ã L<DBIx::Class::Storage::DBI>ã¨L<DBIx::Class::Schema>ã«ãæ¹è¯ããã +ãã©ã³ã¶ã¯ã·ã§ã³ãµãã¼ããããã¾ããããã使ãæ¨å¥¨ãããæ¹æ³ã®ä¸ä¾ã§ã: + + my $genus = $schema->resultset('Genus')->find(12); + + my $coderef2 = sub { + $genus->extinct(1); + $genus->update; + }; + + my $coderef1 = sub { + $genus->add_to_species({ name => 'troglodyte' }); + $genus->wings(2); + $genus->update; + $schema->txn_do($coderef2); # Can have a nested transaction + return $genus->species; + }; + + my $rs; + eval { + $rs = $schema->txn_do($coderef1); + }; + + if ($@) { # Transaction failed + die "the sky is falling!" # + if ($@ =~ /Rollback failed/); # Rollback failed + + deal_with_failed_transaction(); + } + +Nested transactions will work as expected. That is, only the outermost +transaction will actually issue a commit to the $dbh, and a rollback +at any level of any transaction will cause the entire nested +transaction to fail. Support for savepoints and for true nested +transactions (for databases that support them) will hopefully be added +in the future. + +ãã¹ãããããã©ã³ã¶ã¯ã·ã§ã³ã¯æå¾ ã©ããã«åãã¾ãã +ä¸çªå¤å´ã®ãã©ã³ã¶ã¯ã·ã§ã³ã ããå®éã«$dbhã«ã³ããããçºè¡ãã¾ãã +ã©ã®ã¬ãã«ã®ã©ã®ãã©ã³ã¶ã¯ã·ã§ã³ã§ãããã¼ã«ããã¯ãããã +å ¨ã¦ã®ãã¹ãããããã©ã³ã¶ã¯ã·ã§ã³ã失æãã¾ãã +ã»ã¼ããã¤ã³ãã¨ãæ¬å½ã«ãã¹ãããããã©ã³ã¶ã¯ã·ã§ã³ +(ããããµãã¼ããã¦ãããã¼ã¿ãã¼ã¹ç¨ã«)ã¯ãå°æ¥ããã¾ãããã°ã追å ãããã§ãããã + +=head2 Many-to-many ã®ãªã¬ã¼ã·ã§ã³ã·ãã + +This is straightforward using L<ManyToMany|DBIx::Class::Relationship/many_to_many>: +ããã¯ãåç´ã«L<ManyToMany|DBIx::Class::Relationship/many_to_many>ã使ãã¾ã: + + package My::DB; + # ... set up connection ... + + package My::User; + use base 'My::DB'; + __PACKAGE__->table('user'); + __PACKAGE__->add_columns(qw/id name/); + __PACKAGE__->set_primary_key('id'); + __PACKAGE__->has_many('user_address' => 'My::UserAddress', 'user'); + __PACKAGE__->many_to_many('addresses' => 'user_address', 'address'); + + package My::UserAddress; + use base 'My::DB'; + __PACKAGE__->table('user_address'); + __PACKAGE__->add_columns(qw/user address/); + __PACKAGE__->set_primary_key(qw/user address/); + __PACKAGE__->belongs_to('user' => 'My::User'); + __PACKAGE__->belongs_to('address' => 'My::Address'); + + package My::Address; + use base 'My::DB'; + __PACKAGE__->table('address'); + __PACKAGE__->add_columns(qw/id street town area_code country/); + __PACKAGE__->set_primary_key('id'); + __PACKAGE__->has_many('user_address' => 'My::UserAddress', 'address'); + __PACKAGE__->many_to_many('users' => 'user_address', 'user'); + + $rs = $user->addresses(); # get all addresses for a user + $rs = $address->users(); # get all users for an address + +=head2 åã®ããã©ã«ãã®å¤ãç¨æãã + +It's as simple as overriding the C<new> method. Note the use of +C<next::method>. + +åç´ã«ãC<new>ã¡ã½ããããªã¼ãã¼ã©ã¤ããã¾ãã +C<next::method>ã®ä½¿ãããã«æ³¨æãã¦ãã ããã + + sub new { + my ( $self, $attrs ) = @_; + + $attrs->{foo} = 'bar' unless defined $attrs->{foo}; + + $self->next::method($attrs); + + return $self; + } + +For more information about C<next::method>, look in the L<Class::C3> +documentation. See also L<DBIx::Class::Manual::Component> for more +ways to write your own base classes to do this. + +C<next::method>ã«ã¤ãã¦ãã詳ããã¯ãL<Class::C3>ã®ããã¥ã¡ã³ããåç §ãã¦ãã ããã +ãããããããã®ãèªåèªèº«ã®ãã¼ã¹ã¯ã©ã¹ãæ¸ãããã®ãããå¤ãã®æ¹æ³ã«ã¤ãã¦ã¯ã +L<DBIx::CLass::Manual::Component>ãè¦ã¦ãã ããã + +People looking for ways to do "triggers" with DBIx::Class are probably +just looking for this. + +DBIx::Classã§"triggers"ãããæ¹æ³ãæ¢ãã¦ãã人ãããããæ¢ãã¦ããã§ãããã + +=head2 Stringification + +Employ the standard stringification technique by using the C<overload> +module. + +C<overload> ã¢ã¸ã¥ã¼ã«ã§æ¨æºçãªæåååã®ãã¯ããã¯ã使ãã¾ãã + +To make an object stringify itself as a single column, use something +like this (replace C<foo> with the column/method of your choice): + +ã²ã¨ã¤ã®ã«ã©ã ã«ã¤ãã¦ããªãã¸ã§ã¯ãèªèº«ãæåååããã«ã¯ã +次ã®ããã«ãã¾ãã(ã«ã©ã /ã¡ã½ããã§C<foo>ãç½®ãæãã¦ãã ãã) + + use overload '""' => sub { shift->name}, fallback => 1; + +For more complex stringification, you can use an anonymous subroutine: + +ããè¤éãªæåååã§ã¯ãç¡åãµãã«ã¼ãã³ã使ãã¾ã: + + use overload '""' => sub { $_[0]->name . ", " . + $_[0]->address }, fallback => 1; + +=head3 æåååã®ä¾ + +Suppose we have two tables: C<Product> and C<Category>. The table +specifications are: + +äºã¤ã®ãã¼ãã«ãããã¨ãã¾ã:C<Product>ã¨C<Cateogry>ã +ãã¼ãã«ã®å®ç¾©ã¯æ¬¡ã®éã: + + Product(id, Description, category) + Category(id, Description) + +C<category> is a foreign key into the Category table. + +C<category>ã¯Categoryãã¼ãã«ã®å¤é¨ãã¼ã§ãã + +If you have a Product object C<$obj> and write something like + +Productãªãã¸ã§ã¯ãC<$obj>ãããã次ã®ããã«æ¸ããã¨ããã¨ã + + print $obj->category + +things will not work as expected. + +æå¾ ã©ããã«ã¯åãã¾ããã + +To obtain, for example, the category description, you should add this +method to the class defining the Category table: + +ã«ãã´ãªã®å 容ãå¾ãããªããä¾ãã°ãCategoryãã¼ãã«ã®ã¯ã©ã¹å®ç¾©ã«æ¬¡ã® +ã¡ã½ããã追å ãã¹ãã§ã: + + use overload "" => sub { + my $self = shift; + + return $self->Description; + }, fallback => 1; + +=head2 ãããã«åæ + +If you find yourself quitting an app with Control-C a lot during +development, you might like to put the following signal handler in +your main database class to make sure it disconnects cleanly: + +éçºä¸ã«ãCotrol-Cãå¤ç¨ãã¦ãã¢ããªã±ã¼ã·ã§ã³ãçµäºããããã¨ã +å¤ããªããä¸è¨ã®ãããªã·ã°ãã«ãã³ãã©ã¼ããã¼ã¿ãã¼ã¹ã¯ã©ã¹ã«ç½®ãã¦ã +確å®ã«ãããã«åæãããããããã¾ãã: + + $SIG{INT} = sub { + __PACKAGE__->storage->disconnect; + }; + +=head2 ã¹ãã¼ãã®ã¤ã³ãã¼ã/ã¨ã¯ã¹ãã¼ã + +This functionality requires you to have L<SQL::Translator> (also known as +"SQL Fairy") installed. + +ãã®æ©è½ã使ãã«ã¯ãL<SQL::Translator>("SQL Fairy"ã¨ãç¥ããã)ã +ã¤ã³ã¹ãã¼ã«ããå¿ è¦ãããã¾ãã + +To create a DBIx::Class schema from an existing database: + +DBIx::Classã¹ãã¼ããæ¢åã®ãã¼ã¿ãã¼ã¹ããä½ãã¾ã: + + sqlt --from DBI + --to DBIx::Class::File + --prefix "MySchema" > MySchema.pm + +To create a MySQL database from an existing L<DBIx::Class> schema, convert the +schema to MySQL's dialect of SQL: + +MySQLãã¼ã¿ãã¼ã¹ãæ¢åã®L<DBIx::Class>ã¹ãã¼ãããä½ãã¾ãã +ã¹ãã¼ããMySQLã®SQLæ¾è¨ã«å¤æãã¾ã: + + sqlt --from SQL::Translator::Parser::DBIx::Class + --to MySQL + --DBIx::Class "MySchema.pm" > Schema1.sql + +And import using the mysql client: + +mysqlã¯ã©ã¤ã¢ã³ãã§importãã¾ã: + + mysql -h "host" -D "database" -u "user" -p < Schema1.sql + +=head2 ã¯ã©ã¹ãã¼ã¹ããã¹ãã¼ããã¼ã¹ã®ã»ããã¢ããã¸ã®ç°¡åãªç§»è¡ + +You want to start using the schema-based approach to L<DBIx::Class> +(see L<SchemaIntro.pod>), but have an established class-based setup with lots +of existing classes that you don't want to move by hand. Try this nifty script +instead: + +L<DBIx::Class>ã¸ã®ã¹ãã¼ããã¼ã¹ã®ã¢ããã¼ãã使ããã(L<SchemaIntro.pod>ãã¿ã¦ãã ãã)ã +ã§ããæ¢åã®å¤§éã®ã¯ã©ã¹ã§ãå¾æ¥ã®ã¯ã©ã¹ãã¼ã¹ã®ã»ããã¢ãããããã +æã§ããããåããããã¯ãªãã¨ãã¾ããæã§åããå¤ããã«ãä¸è¨ã®æ°ã®å©ããã¹ã¯ãªããã +試ãã¦ã¿ã¦ä¸ãã: + + + + use MyDB; + use SQL::Translator; + + my $schema = MyDB->schema_instance; + + my $translator = SQL::Translator->new( + debug => $debug || 0, + trace => $trace || 0, + no_comments => $no_comments || 0, + show_warnings => $show_warnings || 0, + add_drop_table => $add_drop_table || 0, + validate => $validate || 0, + parser_args => { + 'DBIx::Schema' => $schema, + }, + producer_args => { + 'prefix' => 'My::Schema', + }, + ); + + $translator->parser('SQL::Translator::Parser::DBIx::Class'); + $translator->producer('SQL::Translator::Producer::DBIx::Class::File'); + + my $output = $translator->translate(@args) or die + "Error: " . $translator->error; + + print $output; + +You could use L<Module::Find> to search for all subclasses in the MyDB::* +namespace, which is currently left as an exercise for the reader. + +L<Module::Find>ã使ã£ã¦ãMyDB::*åå空éã«ããå ¨ã¦ã®ãµãã¯ã©ã¹ãæ¢ããã¨ãåºæ¥ã¾ããã +ããã¯ãä»ã®ã¨ãããèªè ã¸ã®èª²é¡ã¨ãã¦ããã¾ãã + + +=head2 ã¹ãã¼ãã®ãã¼ã¸ã§ãã³ã° + +The following example shows simplistically how you might use DBIx::Class to +deploy versioned schemas to your customers. The basic process is as follows: + +ä¸è¨ã®ä¾ã§ã¯ãDBIx::Classã使ã£ã¦ã顧客åãã«ãã¼ã¸ã§ã³ä»ãã®ã¹ãã¼ããã©ããã£ã¦ +ãããã¤ãããããè¦ããã¾ãã + +=over 4 + +=item 1. + +Create a DBIx::Class schema + +DBIx::Classã¹ãã¼ããä½ãã¾ã + +=item 2. + +Save the schema + +ã¹ãã¼ããä¿åãã¾ã + +=item 3. + +Deploy to customers + +顧客ã«ãããã¤ãã¾ã + +=item 4. + +Modify schema to change functionality + +ã¹ãã¼ããå¤æ´ãã¦ã functionality ãå¤æ´ãã¾ã + +=item 5. + +Deploy update to customers + +顧客ã«æ´æ°ããããã¤ãã¾ã + +=back + +=head3 DBIx::Calssã¹ãã¼ããä½ã + +This can either be done manually, or generated from an existing database as +described under C<Schema import/export>. + +ããã¯ãæã§è¡ããã¨ããC<Schema import/export>ã§èª¬æãã¾ããã +æ¢åã®ãã¼ã¿ãã¼ã¹ããçæãããã¨ãã§ãã¾ãã + +=head3 ã¹ãã¼ããä¿åãã + +Use C<sqlt> to transform your schema into an SQL script suitable for your +customer's database. E.g. for MySQL: + +C<sqlt>ã使ã£ã¦ãã¹ãã¼ããå¤æãã¦ã顧客ã®ãã¼ã¿ãã¼ã¹(ä¾ãã°ãMySQL)ã«åã£ãã +SQLã¹ã¯ãªããã«ãã¾ãã + + 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: + +è¤æ°ã®ãã³ããããã¼ãã¼ã¹ãã¿ã¼ã²ããã«ããªããã°ãªããªããªãã +ããããã«åã£ãSQLã¹ã¯ãªãããçæããã ãã§ããPostgreSQLããµãã¼ããã¦ãã¾ã: + + sqlt --from SQL::Translator::DBIx::Class + --to PostgreSQL + --DBIx::Class "MySchema.pm" > Schema1.pgsql.sql + +=head3 顧客ã«ãããã¤ãã + +There are several ways you could deploy your schema. These are probably +beyond the scope of this recipe, but might include: + +ã¹ãã¼ãããããã¤ããã®ã«ã¯è¤æ°ã®æ¹æ³ãããã¾ãã +ãã®ã¬ã·ãã®ã¹ã³ã¼ããé¸è±ãã¦ãããã§ãããå«ãã¦ããã¾ã: + +=over 4 + +=item 1. + +Require customer to apply manually using their RDBMS. + +=item 2. + +Package along with your app, making database dump/schema update/tests +all part of your install. + +=back + +=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 + +=head3 Deploy update to customers + +The schema update can be deployed to customers using the same method as before. + +=head2 Setting limit dialect for SQL::Abstract::Limit + +In some cases, SQL::Abstract::Limit cannot determine the dialect of +the remote SQL server by looking at the database handle. This is a +common problem when using the DBD::JDBC, since the DBD-driver only +know that in has a Java-driver available, not which JDBC driver the +Java component has loaded. This specifically sets the limit_dialect +to Microsoft SQL-server (See more names in SQL::Abstract::Limit +-documentation. + + __PACKAGE__->storage->sql_maker->limit_dialect('mssql'); + +The JDBC bridge is one way of getting access to a MSSQL server from a platform +that Microsoft doesn't deliver native client libraries for. (e.g. Linux) + +=head2 Setting quoting for the generated SQL. + +If the database contains column names with spaces and/or reserved words, they +need to be quoted in the SQL queries. This is done using: +n + __PACKAGE__->storage->sql_maker->quote_char([ qw/[ ]/] ); + __PACKAGE__->storage->sql_maker->name_sep('.'); + +The first sets the quote characters. Either a pair of matching +brackets, or a C<"> or C<'>: + + __PACKAGE__->storage->sql_maker->quote_char('"'); + +Check the documentation of your database for the correct quote +characters to use. C<name_sep> needs to be set to allow the SQL +generator to put the quotes the correct place. + +=head2 Overloading methods + +L<DBIx::Class> uses the L<Class::C3> package, which provides for redispatch of +method calls. You have to use calls to C<next::method> to overload methods. +More information on using L<Class::C3> with L<DBIx::Class> can be found in +L<DBIx::Class::Manual::Component>. + +=head3 Changing one field whenever another changes + +For example, say that you have three columns, C<id>, C<number>, and +C<squared>. You would like to make changes to C<number> and have +C<squared> be automagically set to the value of C<number> squared. +You can accomplish this by overriding C<store_column>: + + sub store_column { + my ( $self, $name, $value ) = @_; + if ($name eq 'number') { + $self->squared($value * $value); + } + $self->next::method($name, $value); + } + +Note that the hard work is done by the call to C<next::method>, which +redispatches your call to store_column in the superclass(es). + +=head3 Automatically creating related objects + +You might have a class C<Artist> which has many C<CD>s. Further, if you +want to create a C<CD> object every time you insert an C<Artist> object. +You can accomplish this by overriding C<insert> on your objects: + + sub insert { + my ( $self, @args ) = @_; + $self->next::method(@args); + $self->cds->new({})->fill_from_artist($self)->insert; + return $self; + } + +where C<fill_from_artist> is a method you specify in C<CD> which sets +values in C<CD> based on the data in the C<Artist> object you pass in. + +=head2 Debugging DBIx::Class objects with Data::Dumper + +L<Data::Dumper> can be a very useful tool for debugging, but sometimes it can +be hard to find the pertinent data in all the data it can generate. +Specifically, if one naively tries to use it like so, + + use Data::Dumper; + + my $cd = $schema->resultset('CD')->find(1); + print Dumper($cd); + +several pages worth of data from the CD object's schema and result source will +be dumped to the screen. Since usually one is only interested in a few column +values of the object, this is not very helpful. + +Luckily, it is possible to modify the data before L<Data::Dumper> outputs +it. Simply define a hook that L<Data::Dumper> will call on the object before +dumping it. For example, + + package My::DB::CD; + + sub _dumper_hook { + $_[0] = bless { + %{ $_[0] }, + result_source => undef, + }, ref($_[0]); + } + + [...] + + use Data::Dumper; + + local $Data::Dumper::Freezer = '_dumper_hook'; + + my $cd = $schema->resultset('CD')->find(1); + print Dumper($cd); + # dumps $cd without its ResultSource + +If the structure of your schema is such that there is a common base class for +all your table classes, simply put a method similar to C<_dumper_hook> in the +base class and set C<$Data::Dumper::Freezer> to its name and L<Data::Dumper> +will automagically clean up your data before printing it. See +L<Data::Dumper/EXAMPLES> for more information. + +=head2 Retrieving a row object's Schema + +It is possible to get a Schema object from a row object like so: + + my $schema = $cd->result_source->schema; + # use the schema as normal: + my $artist_rs = $schema->resultset('Artist'); + +This can be useful when you don't want to pass around a Schema object to every +method. + +=head2 Profiling + +When you enable L<DBIx::Class::Storage::DBI>'s debugging it prints the SQL +executed as well as notifications of query completion and transaction +begin/commit. If you'd like to profile the SQL you can subclass the +L<DBIx::Class::Storage::Statistics> class and write your own profiling +mechanism: + + package My::Profiler; + use strict; + + use base 'DBIx::Class::Storage::Statistics'; + + use Time::HiRes qw(time); + + my $start; + + sub query_start { + my $self = shift(); + my $sql = shift(); + my $params = @_; + + print "Executing $sql: ".join(', ', @params)."\n"; + $start = time(); + } + + sub query_end { + my $self = shift(); + my $sql = shift(); + my @params = @_; + + printf("Execution took %0.4f seconds.\n", time() - $start); + $start = undef; + } + + 1; + +You can then install that class as the debugging object: + + __PACKAGE__->storage()->debugobj(new My::Profiler()); + __PACKAGE__->storage()->debug(1); + +A more complicated example might involve storing each execution of SQL in an +array: + + sub query_end { + my $self = shift(); + my $sql = shift(); + my @params = @_; + + my $elapsed = time() - $start; + push(@{ $calls{$sql} }, { + params => \@params, + elapsed => $elapsed + }); + } + +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. + +=head2 Getting the value of the primary key for the last database insert + +AKA getting last_insert_id + +If you are using PK::Auto, this is straightforward: + + my $foo = $rs->create(\%blah); + # do more stuff + my $id = $foo->id; # foo->my_primary_key_field will also work. + +If you are not using autoincrementing primary keys, this will probably +not work, but then you already know the value of the last primary key anyway. + +=head2 Dynamic Sub-classing DBIx::Class proxy classes +(AKA multi-class object inflation from one table) + +L<DBIx::Class> classes are proxy classes, therefore some different +techniques need to be employed for more than basic subclassing. In +this example we have a single user table that carries a boolean bit +for admin. We would like like to give the admin users +objects(L<DBIx::Class::Row>) the same methods as a regular user but +also special admin only methods. It doesn't make sense to create two +seperate proxy-class files for this. We would be copying all the user +methods into the Admin class. There is a cleaner way to accomplish +this. + +Overriding the C<inflate_result> method within the User proxy-class +gives us the effect we want. This method is called by +L<DBIx::Class::ResultSet> when inflating a result from storage. So we +grab the object being returned, inspect the values we are looking for, +bless it if it's an admin object, and then return it. See the example +below: + +B<Schema Definition> + + package DB::Schema; + + use base qw/DBIx::Class::Schema/; + + __PACKAGE__->load_classes(qw/User/); + + +B<Proxy-Class definitions> + + package DB::Schema::User; + + use strict; + use warnings; + use base qw/DBIx::Class/; + + ### Defined what our admin class is for ensure_class_loaded + my $admin_class = __PACKAGE__ . '::Admin'; + + __PACKAGE__->load_components(qw/Core/); + + __PACKAGE__->table('users'); + + __PACKAGE__->add_columns(qw/user_id email password + firstname lastname active + admin/); + + __PACKAGE__->set_primary_key('user_id'); + + sub inflate_result { + my $self = shift; + my $ret = $self->next::method(@_); + if( $ret->admin ) {### If this is an admin rebless for extra functions + $self->ensure_class_loaded( $admin_class ); + bless $ret, $admin_class; + } + return $ret; + } + + sub hello { + print "I am a regular user.\n"; + return ; + } + + + package DB::Schema::User::Admin; + + use strict; + use warnings; + use base qw/DB::Schema::User/; + + sub hello + { + print "I am an admin.\n"; + return; + } + + sub do_admin_stuff + { + print "I am doing admin stuff\n"; + return ; + } + +B<Test File> test.pl + + use warnings; + use strict; + use DB::Schema; + + my $user_data = { email => 'someg****@place*****', + password => 'pass1', + admin => 0 }; + + my $admin_data = { email => 'somea****@admin*****', + password => 'pass2', + admin => 1 }; + + my $schema = DB::Schema->connection('dbi:Pg:dbname=test'); + + $schema->resultset('User')->create( $user_data ); + $schema->resultset('User')->create( $admin_data ); + + ### Now we search for them + my $user = $schema->resultset('User')->single( $user_data ); + my $admin = $schema->resultset('User')->single( $admin_data ); + + print ref $user, "\n"; + print ref $admin, "\n"; + + print $user->password , "\n"; # pass1 + print $admin->password , "\n";# pass2; inherited from User + print $user->hello , "\n";# I am a regular user. + print $admin->hello, "\n";# I am an admin. + + ### The statement below will NOT print + print "I can do admin stuff\n" if $user->can('do_admin_stuff'); + ### The statement below will print + print "I can do admin stuff\n" if $admin->can('do_admin_stuff'); + +=head2 Skip object creation for faster results + +DBIx::Class is not built for speed, it's built for convenience and +ease of use, but sometimes you just need to get the data, and skip the +fancy objects. Luckily this is also fairly easy using +C<inflate_result>: + + # Define a class which just returns the results as a hashref: + package My::HashRefInflator; + + ## $me is the hashref of cols/data from the immediate resultsource + ## $prefetch is a deep hashref of all the data from the prefetched + ## related sources. + + sub mk_hash { + my ($me, $rest) = @_; + + return { %$me, + map { ($_ => mk_hash(@{$rest->{$_}})) } keys %$rest + }; + } + + sub inflate_result { + my ($self, $source, $me, $prefetch) = @_; + return mk_hash($me, $prefetch); + } + + # Change the object inflation to a hashref for just this resultset: + $rs->result_class('My::HashRefInflator'); + + my $datahashref = $rs->next; + foreach my $col (keys %$datahashref) { + if(!ref($datahashref->{$col})) { + # It's a plain value + } + elsif(ref($datahashref->{$col} eq 'HASH')) { + # It's a related value in a hashref + } + } + +=head2 Want to know if find_or_create found or created a row? + +Just use C<find_or_new> instead, then check C<in_storage>: + + my $obj = $rs->find_or_new({ blah => 'blarg' }); + unless ($obj->in_storage) { + $obj->insert; + # do whatever else you wanted if it was a new row + } + +=head3 Wrapping/overloading a column accessor + +Problem: Say you have a table "Camera" and want to associate a description +with each camera. For most cameras, you'll be able to generate the description from +the other columns. However, in a few special cases you may want to associate a +custom description with a camera. + +Solution: + +In your database schema, define a description field in the "Camera" table that +can contain text and null values. + +In DBIC, we'll overload the column accessor to provide a sane default if no +custom description is defined. The accessor will either return or generate the +description, depending on whether the field is null or not. + +First, in your "Camera" schema class, define the description field as follows: + + __PACKAGE__->add_columns(description => { accessor => '_description' }); + +Next, we'll define the accessor-wrapper subroutine: + + sub description { + my $self = shift; + + # If there is an update to the column, we'll let the original accessor + # deal with it. + return $self->_description(@_) if @_; + + # Fetch the column value. + my $description = $self->_description; + + # If there's something in the description field, then just return that. + return $description if defined $description && length $descripton; + + # Otherwise, generate a description. + return $self->generate_description; + } + + +=cut +