X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=blobdiff_plain;f=lib%2FDBIx%2FClass%2FManual%2FCookbook.pod;h=081a4d0dea2d2f6a1c93ff9548dd0437c6f9af7d;hb=4c24816137de09c629fcd3da41b7626d50cc13f6;hp=715c8f85e61a61f6878f271e1fc9f6bd5e0aac14;hpb=5e8b1b2adbc7b67dc7948a41354e36ce0c7998c6;p=dbsrgits%2FDBIx-Class.git diff --git a/lib/DBIx/Class/Manual/Cookbook.pod b/lib/DBIx/Class/Manual/Cookbook.pod index 715c8f8..081a4d0 100644 --- a/lib/DBIx/Class/Manual/Cookbook.pod +++ b/lib/DBIx/Class/Manual/Cookbook.pod @@ -138,6 +138,8 @@ any of your aliases using either of these: } ); + my $count = $rs->next->get_column('count'); + =head3 SELECT COUNT(DISTINCT colname) my $rs = $schema->resultset('Foo')->search( @@ -169,6 +171,57 @@ L supports C as follows: # LEFT JOIN cd cds ON ( cds.artist = me.artistid ) # GROUP BY name +=head3 Predefined searches + +You can write your own DBIx::Class::ResultSet class by inheriting from it +and define often used searches as methods: + + 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: + + __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 Predefined searches without writing a ResultSet class + +Alternatively you can automatically generate a DBIx::Class::ResultSet +class by using the ResultSetManager component and tagging your method +as 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 Using joins and prefetch You can use the C attribute to allow searching on, or sorting your @@ -362,7 +415,7 @@ example of the recommended way to use it: $genus->add_to_species({ name => 'troglodyte' }); $genus->wings(2); $genus->update; - $schema->txn_do($code, $genus); # Can have a nested transation + $schema->txn_do($code, $genus); # Can have a nested transaction return $genus->species; }; @@ -438,6 +491,13 @@ C. $class->next::method($attrs); } +For more information about C, look in the L +documentation. See also L for more +ways to write your own base classes to do this. + +People looking for ways to do "triggers" with DBIx::Class are probably +just looking for this. + =head2 Stringification Employ the standard stringification technique by using the C @@ -502,8 +562,8 @@ instead: }, ); - $translator->parser('DBIx::Class'); - $translator->producer('DBIx::Class::File'); + $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; @@ -511,7 +571,7 @@ instead: print $output; You could use L to search for all subclasses in the MyDB::* -namespace, which is currently left as an excercise for the reader. +namespace, which is currently left as an exercise for the reader. =head2 Schema versioning @@ -626,8 +686,164 @@ The first sets the quotesymbols. If the quote i "symmetric" as " or ' __PACKAGE__->storage->sql_maker->quote_char('"'); -is enough. If the left qoute differs form the right quote, the first +is enough. If the left quote differs form the right quote, the first notation should be used. name_sep needs to be set to allow the SQL generator to put the quotes the correct place. +=head2 Overloading methods + +L uses the L package, which provides for redispatch of +method calls. You have to use calls to C to overload methods. +More information on using L with L can be found in +L. + +=head3 Changing one field whenever another changes + +For example, say that you have three columns, C, C, and +C. You would like to make changes to C and have +C be automagically set to the value of C squared. +You can accomplish this by overriding C: + + 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, which +redispatches your call to store_column to the superclass(es). + +=head3 Automatically creating related objects + +You might have a class C which has many Cs. Further, you +want to create a C object every time you insert an C object. +You can accomplish this by overriding C: + + sub insert { + my ( $class, $args_ref ) = @_; + my $self = $class->next::method($args_ref); + $self->cds->new({})->fill_from_artist($self)->insert; + return $self; + } + +where C is a method you specify in C which sets +values in C based on the data in the C object you pass in. + +=head2 Debugging DBIx::Class objects with Data::Dumper + +L 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 outputs +it. Simply define a hook that L 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; + + $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 +will automagically clean up your data before printing it. See +L 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; + my $artist_rs = $schema->resultset('Artist'); + # for example + +This can be useful when you don't want to pass around a Schema object to every +method. + +=head2 Profiling + +When you enable L'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 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. + =cut