update docs for prefetch
[dbsrgits/DBIx-Class.git] / lib / DBIx / Class / Manual / Cookbook.pod
CommitLineData
3b44ccc6 1=head1 NAME
9c82c181 2
40dbc108 3DBIx::Class::Manual::Cookbook - Miscellaneous recipes
ee38fa40 4
40dbc108 5=head1 RECIPES
2913b2d3 6
bade79c4 7=head2 Searching
faf62551 8
bade79c4 9=head3 Paged results
faf62551 10
bade79c4 11When you expect a large number of results, you can ask L<DBIx::Class> for a
12paged resultset, which will fetch only a small number of records at a time:
faf62551 13
bade79c4 14 my $rs = $schema->resultset('Artist')->search(
15 {},
16 {
17 page => 1, # page to return (defaults to 1)
18 rows => 10, # number of results per page
19 },
20 );
faf62551 21
bade79c4 22 return $rs->all(); # all records for page 1
faf62551 23
bade79c4 24The C<page> attribute does not have to be specified in your search:
25
26 my $rs = $schema->resultset('Artist')->search(
27 {},
28 {
29 rows => 10,
30 }
31 );
faf62551 32
bade79c4 33 return $rs->page(1); # DBIx::Class::ResultSet containing first 10 records
faf62551 34
35In either of the above cases, you can return a L<Data::Page> object for the
bade79c4 36resultset (suitable for use in e.g. a template) using the C<pager> method:
faf62551 37
bade79c4 38 return $rs->pager();
faf62551 39
bade79c4 40=head3 Complex WHERE clauses
2913b2d3 41
40dbc108 42Sometimes you need to formulate a query using specific operators:
43
ea6309e2 44 my @albums = $schema->resultset('Album')->search({
35d4fe78 45 artist => { 'like', '%Lamb%' },
46 title => { 'like', '%Fear of Fours%' },
47 });
40dbc108 48
49This results in something like the following C<WHERE> clause:
50
35d4fe78 51 WHERE artist LIKE '%Lamb%' AND title LIKE '%Fear of Fours%'
40dbc108 52
53Other queries might require slightly more complex logic:
54
ea6309e2 55 my @albums = $schema->resultset('Album')->search({
35d4fe78 56 -or => [
57 -and => [
58 artist => { 'like', '%Smashing Pumpkins%' },
59 title => 'Siamese Dream',
60 ],
61 artist => 'Starchildren',
62 ],
63 });
40dbc108 64
65This results in the following C<WHERE> clause:
66
35d4fe78 67 WHERE ( artist LIKE '%Smashing Pumpkins%' AND title = 'Siamese Dream' )
68 OR artist = 'Starchildren'
40dbc108 69
70For more information on generating complex queries, see
71L<SQL::Abstract/WHERE CLAUSES>.
ee38fa40 72
bade79c4 73=head3 Using specific columns
faf62551 74
bade79c4 75When you only want selected columns from a table, you can use C<cols> to
76specify which ones you need:
faf62551 77
bade79c4 78 my $rs = $schema->resultset('Artist')->search(
79 {},
80 {
81 cols => [qw/ name /]
82 }
83 );
faf62551 84
bade79c4 85 # Equivalent SQL:
86 # SELECT artist.name FROM artist
faf62551 87
bade79c4 88=head3 Using database functions or stored procedures
faf62551 89
bade79c4 90The combination of C<select> and C<as> can be used to return the result of a
91database function or stored procedure as a column value. You use C<select> to
92specify the source for your column value (e.g. a column name, function, or
93stored procedure name). You then use C<as> to set the column name you will use
94to access the returned value:
faf62551 95
bade79c4 96 my $rs = $schema->resultset('Artist')->search(
97 {},
98 {
99 select => [ 'name', { LENGTH => 'name' } ],
100 as => [qw/ name name_length /],
101 }
102 );
faf62551 103
bade79c4 104 # Equivalent SQL:
105 # SELECT name name, LENGTH( name ) name_length
106 # FROM artist
faf62551 107
108If your alias exists as a column in your base class (i.e. it was added with
bade79c4 109C<add_columns>), you just access it as normal. Our C<Artist> class has a C<name>
110column, so we just use the C<name> accessor:
faf62551 111
bade79c4 112 my $artist = $rs->first();
113 my $name = $artist->name();
faf62551 114
115If on the other hand the alias does not correspond to an existing column, you
bade79c4 116can get the value using the C<get_column> accessor:
faf62551 117
bade79c4 118 my $name_length = $artist->get_column('name_length');
faf62551 119
bade79c4 120If you don't like using C<get_column>, you can always create an accessor for
faf62551 121any of your aliases using either of these:
122
bade79c4 123 # Define accessor manually:
124 sub name_length { shift->get_column('name_length'); }
faf62551 125
bade79c4 126 # Or use DBIx::Class::AccessorGroup:
127 __PACKAGE__->mk_group_accessors('column' => 'name_length');
faf62551 128
bade79c4 129=head3 SELECT DISTINCT with multiple columns
faf62551 130
bade79c4 131 my $rs = $schema->resultset('Foo')->search(
132 {},
133 {
134 select => [
135 { distinct => [ $source->columns ] }
136 ],
137 as => [ $source->columns ]
138 }
139 );
faf62551 140
bade79c4 141=head3 SELECT COUNT(DISTINCT colname)
6607ee1b 142
bade79c4 143 my $rs = $schema->resultset('Foo')->search(
144 {},
145 {
146 select => [
147 { count => { distinct => 'colname' } }
148 ],
149 as => [ 'count' ]
150 }
151 );
6607ee1b 152
bade79c4 153=head3 Grouping results
154
155L<DBIx::Class> supports C<GROUP BY> as follows:
156
157 my $rs = $schema->resultset('Artist')->search(
158 {},
159 {
160 join => [qw/ cds /],
161 select => [ 'name', { count => 'cds.cdid' } ],
162 as => [qw/ name cd_count /],
163 group_by => [qw/ name /]
164 }
165 );
6607ee1b 166
bade79c4 167 # Equivalent SQL:
168 # SELECT name, COUNT( cds.cdid ) FROM artist me
169 # LEFT JOIN cd cds ON ( cds.artist = me.artistid )
170 # GROUP BY name
6607ee1b 171
87980de7 172=head2 Using joins and prefetch
173
bade79c4 174You can use the C<join> attribute to allow searching on, or sorting your
175results by, one or more columns in a related table. To return all CDs matching
176a particular artist name:
ea6309e2 177
bade79c4 178 my $rs = $schema->resultset('CD')->search(
179 {
180 'artist.name' => 'Bob Marley'
181 },
182 {
183 join => [qw/artist/], # join the artist table
184 }
185 );
186
187 # Equivalent SQL:
188 # SELECT cd.* FROM cd
189 # JOIN artist ON cd.artist = artist.id
190 # WHERE artist.name = 'Bob Marley'
191
192If required, you can now sort on any column in the related tables by including
193it in your C<order_by> attribute:
194
195 my $rs = $schema->resultset('CD')->search(
196 {
197 'artist.name' => 'Bob Marley'
198 },
199 {
200 join => [qw/ artist /],
201 order_by => [qw/ artist.name /]
202 }
203 };
ea6309e2 204
bade79c4 205 # Equivalent SQL:
206 # SELECT cd.* FROM cd
207 # JOIN artist ON cd.artist = artist.id
208 # WHERE artist.name = 'Bob Marley'
209 # ORDER BY artist.name
ea6309e2 210
bade79c4 211Note that the C<join> attribute should only be used when you need to search or
212sort using columns in a related table. Joining related tables when you only
213need columns from the main table will make performance worse!
ea6309e2 214
bade79c4 215Now let's say you want to display a list of CDs, each with the name of the
216artist. The following will work fine:
ea6309e2 217
bade79c4 218 while (my $cd = $rs->next) {
219 print "CD: " . $cd->title . ", Artist: " . $cd->artist->name;
220 }
ea6309e2 221
bade79c4 222There is a problem however. We have searched both the C<cd> and C<artist> tables
223in our main query, but we have only returned data from the C<cd> table. To get
224the artist name for any of the CD objects returned, L<DBIx::Class> will go back
225to the database:
ea6309e2 226
bade79c4 227 SELECT artist.* FROM artist WHERE artist.id = ?
ea6309e2 228
229A statement like the one above will run for each and every CD returned by our
230main query. Five CDs, five extra queries. A hundred CDs, one hundred extra
231queries!
232
bade79c4 233Thankfully, L<DBIx::Class> has a C<prefetch> attribute to solve this problem.
234This allows you to fetch results from a related table as well as the main table
ea6309e2 235for your class:
236
bade79c4 237 my $rs = $schema->resultset('CD')->search(
238 {
239 'artist.name' => 'Bob Marley'
240 },
241 {
242 join => [qw/ artist /],
243 order_by => [qw/ artist.name /],
244 prefetch => [qw/ artist /] # return artist data too!
245 }
246 );
ea6309e2 247
bade79c4 248 # Equivalent SQL (note SELECT from both "cd" and "artist"):
249 # SELECT cd.*, artist.* FROM cd
250 # JOIN artist ON cd.artist = artist.id
251 # WHERE artist.name = 'Bob Marley'
252 # ORDER BY artist.name
ea6309e2 253
254The code to print the CD list remains the same:
255
bade79c4 256 while (my $cd = $rs->next) {
257 print "CD: " . $cd->title . ", Artist: " . $cd->artist->name;
258 }
ea6309e2 259
bade79c4 260L<DBIx::Class> has now prefetched all matching data from the C<artist> table,
ea6309e2 261so no additional SQL statements are executed. You now have a much more
262efficient query.
263
bade79c4 264Note that as of L<DBIx::Class> 0.04, C<prefetch> cannot be used with
265C<has_many> relationships. You will get an error along the lines of "No
266accessor for prefetched ..." if you try.
ea6309e2 267
bade79c4 268Also note that C<prefetch> should only be used when you know you will
ea6309e2 269definitely use data from a related table. Pre-fetching related tables when you
270only need columns from the main table will make performance worse!
271
bade79c4 272=head3 Multi-step joins
ea6309e2 273
274Sometimes you want to join more than one relationship deep. In this example,
bade79c4 275we want to find all C<Artist> objects who have C<CD>s whose C<LinerNotes>
276contain a specific string:
277
278 # Relationships defined elsewhere:
279 # Artist->has_many('cds' => 'CD', 'artist');
280 # CD->has_one('liner_notes' => 'LinerNotes', 'cd');
281
282 my $rs = $schema->resultset('Artist')->search(
283 {
284 'liner_notes.notes' => { 'like', '%some text%' },
285 },
286 {
287 join => {
288 'cds' => 'liner_notes'
289 }
290 }
291 );
ea6309e2 292
bade79c4 293 # Equivalent SQL:
294 # SELECT artist.* FROM artist
295 # JOIN ( cd ON artist.id = cd.artist )
296 # JOIN ( liner_notes ON cd.id = liner_notes.cd )
297 # WHERE liner_notes.notes LIKE '%some text%'
ea6309e2 298
299Joins can be nested to an arbitrary level. So if we decide later that we
300want to reduce the number of Artists returned based on who wrote the liner
301notes:
302
bade79c4 303 # Relationship defined elsewhere:
304 # LinerNotes->belongs_to('author' => 'Person');
305
306 my $rs = $schema->resultset('Artist')->search(
307 {
308 'liner_notes.notes' => { 'like', '%some text%' },
309 'author.name' => 'A. Writer'
310 },
311 {
312 join => {
313 'cds' => {
314 'liner_notes' => 'author'
ea6309e2 315 }
bade79c4 316 }
317 }
318 );
ea6309e2 319
bade79c4 320 # Equivalent SQL:
321 # SELECT artist.* FROM artist
322 # JOIN ( cd ON artist.id = cd.artist )
323 # JOIN ( liner_notes ON cd.id = liner_notes.cd )
324 # JOIN ( author ON author.id = liner_notes.author )
325 # WHERE liner_notes.notes LIKE '%some text%'
326 # AND author.name = 'A. Writer'
87980de7 327
328=head2 Transactions
329
330As of version 0.04001, there is improved transaction support in
35d4fe78 331L<DBIx::Class::Storage::DBI>. Here is an example of the recommended
40dbc108 332way to use it:
87980de7 333
35d4fe78 334 my $genus = Genus->find(12);
335 eval {
336 MyDB->txn_begin;
337 $genus->add_to_species({ name => 'troglodyte' });
338 $genus->wings(2);
339 $genus->update;
340 cromulate($genus); # Can have a nested transation
341 MyDB->txn_commit;
342 };
343 if ($@) {
344 # Rollback might fail, too
87980de7 345 eval {
35d4fe78 346 MyDB->txn_rollback
87980de7 347 };
35d4fe78 348 }
87980de7 349
40dbc108 350Currently, a nested commit will do nothing and a nested rollback will
35d4fe78 351die. The code at each level must be sure to call rollback in the case
40dbc108 352of an error, to ensure that the rollback will propagate to the top
35d4fe78 353level and be issued. Support for savepoints and for true nested
40dbc108 354transactions (for databases that support them) will hopefully be added
355in the future.
ee38fa40 356
130c6439 357=head2 Many-to-many relationships
ee38fa40 358
ea6309e2 359This is straightforward using L<DBIx::Class::Relationship::ManyToMany>:
360
bade79c4 361 package My::DB;
362 # ... set up connection ...
363
364 package My::User;
365 use base 'My::DB';
366 __PACKAGE__->table('user');
367 __PACKAGE__->add_columns(qw/id name/);
368 __PACKAGE__->set_primary_key('id');
369 __PACKAGE__->has_many('user_address' => 'My::UserAddress', 'user');
370 __PACKAGE__->many_to_many('addresses' => 'user_address', 'address');
371
372 package My::UserAddress;
373 use base 'My::DB';
374 __PACKAGE__->table('user_address');
375 __PACKAGE__->add_columns(qw/user address/);
376 __PACKAGE__->set_primary_key(qw/user address/);
377 __PACKAGE__->belongs_to('user' => 'My::User');
378 __PACKAGE__->belongs_to('address' => 'My::Address');
379
380 package My::Address;
381 use base 'My::DB';
382 __PACKAGE__->table('address');
383 __PACKAGE__->add_columns(qw/id street town area_code country/);
384 __PACKAGE__->set_primary_key('id');
385 __PACKAGE__->has_many('user_address' => 'My::UserAddress', 'address');
386 __PACKAGE__->many_to_many('users' => 'user_address', 'user');
387
388 $rs = $user->addresses(); # get all addresses for a user
389 $rs = $address->users(); # get all users for an address
390
391=head2 Setting default values for a row
a00e1684 392
35d4fe78 393It's as simple as overriding the C<new> method. Note the use of
40dbc108 394C<next::method>.
a00e1684 395
35d4fe78 396 sub new {
397 my ( $class, $attrs ) = @_;
40dbc108 398
35d4fe78 399 $attrs->{foo} = 'bar' unless defined $attrs->{foo};
40dbc108 400
35d4fe78 401 $class->next::method($attrs);
402 }
a00e1684 403
25af00d7 404=head2 Stringification
405
40dbc108 406Employ the standard stringification technique by using the C<overload>
35d4fe78 407module. Replace C<foo> with the column/method of your choice.
25af00d7 408
35d4fe78 409 use overload '""' => 'foo', fallback => 1;
25af00d7 410
bade79c4 411=head2 Disconnecting cleanly
412
413If you find yourself quitting an app with Control-C a lot during
414development, you might like to put the following signal handler in
415your main database class to make sure it disconnects cleanly:
416
417 $SIG{INT} = sub {
418 __PACKAGE__->storage->dbh->disconnect;
419 };
420
362500af 421=head2 Schema import/export
422
423This functionality requires you to have L<SQL::Translator> (also known as
424"SQL Fairy") installed.
425
426To create a DBIx::Class schema from an existing database:
427
428 sqlt --from DBI
429 --to DBIx::Class::File
430 --prefix "MySchema" > MySchema.pm
431
432To create a MySQL database from an existing L<DBIx::Class> schema, convert the
433schema to MySQL's dialect of SQL:
434
435 sqlt --from DBIx::Class --to MySQL --DBIx::Class "MySchema.pm" > Schema1.sql
436
437And import using the mysql client:
438
439 mysql -h "host" -D "database" -u "user" -p < Schema1.sql
440
b0a20454 441=head2 Easy migration from class-based to schema-based setup
442
443You want to start using the schema-based approach to L<DBIx::Class>
444(see L<SchemaIntro.pod>), but have an established class-based setup with lots
445of existing classes that you don't want to move by hand. Try this nifty script
446instead:
447
448 use MyDB;
449 use SQL::Translator;
450
451 my $schema = MyDB->schema_instance;
452
453 my $translator = SQL::Translator->new(
454 debug => $debug || 0,
455 trace => $trace || 0,
456 no_comments => $no_comments || 0,
457 show_warnings => $show_warnings || 0,
458 add_drop_table => $add_drop_table || 0,
459 validate => $validate || 0,
460 parser_args => {
461 'DBIx::Schema' => $schema,
462 }
463 producer_args => {
464 'prefix' => 'My::Schema',
465 }
466 );
467
468 $translator->parser('DBIx::Class');
469 $translator->producer('DBIx::Class::File');
470
471 my $output = $translator->translate(@args) or die
472 "Error: " . $translator->error;
473
474 print $output;
475
476You could use L<Module::Find> to search for all subclasses in the MyDB::*
477namespace, which is currently left as an excercise for the reader.
478
362500af 479=head2 Schema versioning
480
481The following example shows simplistically how you might use DBIx::Class to
482deploy versioned schemas to your customers. The basic process is as follows:
483
4841) Create a DBIx::Class schema
4852) Save the schema
4863) Deploy to customers
4874) Modify schema to change functionality
4885) Deploy update to customers
489
490=head3 Create a DBIx::Class schema
491
492This can either be done manually, or generated from an existing database as
493described under C<Schema import/export>.
494
495=head3 Save the schema
496
497Use C<sqlt> to transform your schema into an SQL script suitable for your
498customer's database. E.g. for MySQL:
499
500 sqlt --from DBIx::Class
501 --to MySQL
502 --DBIx::Class "MySchema.pm" > Schema1.mysql.sql
503
504If you need to target databases from multiple vendors, just generate an SQL
505script suitable for each. To support PostgreSQL too:
506
507 sqlt --from DBIx::Class
508 --to PostgreSQL
509 --DBIx::Class "MySchema.pm" > Schema1.pgsql.sql
510
511=head3 Deploy to customers
512
513There are several ways you could deploy your schema. These are probably
514beyond the scope of this recipe, but might include:
515
5161) Require customer to apply manually using their RDBMS.
5172) Package along with your app, making database dump/schema update/tests
518all part of your install.
519
520=head3 Modify the schema to change functionality
521
522As your application evolves, it may be necessary to modify your schema to
523change functionality. Once the changes are made to your schema in DBIx::Class,
524export the modified schema as before, taking care not to overwrite the original:
525
526 sqlt --from DBIx::Class
527 --to MySQL
528 --DBIx::Class "Anything.pm" > Schema2.mysql.sql
529
530Next, use sqlt-diff to create an SQL script that will update the customer's
531database schema:
532
533 sqlt-diff --to MySQL Schema1=MySQL Schema2=MySQL > SchemaUpdate.mysql.sql
534
535=head3 Deploy update to customers
536
537The schema update can be deployed to customers using the same method as before.
538
40dbc108 539=cut