From: Brendan Byrd Date: Thu, 14 Mar 2013 22:58:15 +0000 (-0400) Subject: Rework Manual::Intro X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=commitdiff_plain;h=refs%2Fheads%2Ftopic%2Frework_intro;p=dbsrgits%2FDBIx-Class.git Rework Manual::Intro --- diff --git a/lib/DBIx/Class/Manual/Intro.pod b/lib/DBIx/Class/Manual/Intro.pod index 382f72d..898e6bc 100644 --- a/lib/DBIx/Class/Manual/Intro.pod +++ b/lib/DBIx/Class/Manual/Intro.pod @@ -8,121 +8,186 @@ 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 +=head1 THE DBIx::Class WAY (CLIFF NOTES) Here are a few simple tips that will help you get your bearings with -DBIx::Class. +L. =head2 Tables become Result classes -DBIx::Class needs to know what your Table structure looks like. You -do that by defining Result classes. Result classes are defined by -calling methods proxied to L. Each Result -class defines one Table, 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: +L needs to know what your Table structure looks like. You +do that by defining L. Each +Result class defines one Table, which defines the Columns it has, any +L it has to other tables, and much more. - A Result class == Table +The important thing to understand: + + A Result class ~~ 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! - -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 L (called +C) for an C table with three columns: +C, C, and C. Any time we want to query this table, we'll +be creating a L<ResultSet|DBIx::Class::ResultSet> from its +L<Schema|DBIx::Class::Schema>. For example, the results of: 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 represented like so: + + my $rs = $schema->resultset('Album')->search(undef, { + columns => [qw{ albumid artist title }] + }); -DBIx::Class doesn't limit you to creating only simple ResultSets -- if you +L<DBIC|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; -You could easily achieve it. +You could easily achieve it, like this: + + my $rs = $schema->resultset('Album')->search(undef, { + columns => ['title'], + group_by => ['title'], + }); The important thing to understand: - Any time you would reach for a SQL query in DBI, you are - creating a DBIx::Class::ResultSet. + Instead of writing SQL queries manually, you ask a ResultSet object to + generate them. -=head2 Search is like "prepare" +=head2 Search results are returned as "Rows" -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") +Rows of the search from the database are blessed into +L<Result objects|DBIx::Class::Manual::ResultClass>. This might seem conflicting, +but this is because a Result class is supposed to be customizable "toolset" for +both result management and table definition. The important thing to understand: - Setting up a ResultSet does not execute the query; retrieving - the data does. + When -defining- a Result class, the purpose of the class is to: + * Define the table, columns, and constraints + * Define relationships to other tables -=head2 Search results are returned as Rows + When -using- a Result object, the purpose of the object is to: + * Read data from a result (like a row) + * Perform CRUD operations, based on that data + * "Inflate" columns + * Use custom methods defined in the class by you -Rows of the search from the database are blessed into -L<Result|DBIx::Class::Manual::ResultClass> objects. +=head2 Search is like "prepare" + +L<DBIC|DBIx::Class> tends to wait until it absolutely must fetch information from the +database. If you are returning a L<ResultSet|DBIx::Class::ResultSet>, the +query won't execute until you use a method that wants to access the data, such +as C<next> or C<first>. + + # Does not run any SQL statements yet + my $rs = $schema->resultset('Album')->search(undef, { + columns => [qw{ albumid artist title }] + }); + + # Will trigger the SQL query once and loop through the results + while (my $result = $rs->next) { + my $artist = $result->artist; + ... + } =head1 SETTING UP DBIx::Class -Let's look at how you can set and use your first native L<DBIx::Class> tree. +Let's look at how you can set and use your first native L<DBIC|DBIx::Class> tree. + +=head2 Which deployment method to take + +There are a few different ways to create your L<DBIC|DBIx::Class> tree. Which +recommended method to take depends on how your database currently exists: + + * If you are creating a brand new database, set up the tree manually, and then + create the tables via $schema->deploy (or another deployment tool). + * If the database already exists, use DBIx::Class::Schema::Loader or another + schema builder. + +Since L<DBIC|DBIx::Class> first came into the scene, there have been a number of +useful deployment tools (schema builders) that ease the process (from oldest to +newest): -First we'll see how you can set up your classes yourself. If you want them to -be auto-discovered, just skip to the L<next section|/Using -DBIx::Class::Schema::Loader>, which shows you how to use -L<DBIx::Class::Schema::Loader>. +=over + +=item * + +L<DBIx::Class::Schema::Loader> (existing DB only) + +=item * + +L<DBIx::Class::DeploymentHandler> (new or existing DB) + +=item * + +L<DBIx::Class::Migration> (new or existing DB) + +=back + +This document only covers the manual method and L<DBICSL|/Using +DBIx::Class::Schema::Loader>. However, users with complex schemas might want +to check out the latest deployment tech to make life easier. =head2 Setting it up manually +=head3 Schema + First, you should create your base schema class, which inherits from L<DBIx::Class::Schema>: 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_namespaces() method: +In this class, you load your C<result_source> ("table", "model") classes, which +we will define later, using the L<load_namespaces|DBIx::Class::Schema/load_namespaces> +method: # load My::Schema::Result::* and their resultset classes __PACKAGE__->load_namespaces(); -By default this loads all the Result (Row) classes in the -My::Schema::Result:: namespace, and also any resultset classes in the -My::Schema::ResultSet:: namespace (if missing, the resultsets are -defaulted to be DBIx::Class::ResultSet objects). You can change the -result and resultset namespaces by using options to the -L<DBIx::Class::Schema/load_namespaces> call. +By default, this loads all the L<Result classes|DBIx::Class::Manual::ResultClass> +in the C<My::Schema::Result::> namespace, and also any ResultSet classes in the +C<My::Schema::ResultSet::> namespace. (If missing, the ResultSets are +defaulted to be L<DBIx::Class::ResultSet> objects.) You can change the Result +and ResultSet namespaces by using options to the +L<load_namespaces|DBIx::Class::Schema/load_namespaces> call. It is also possible to do the same things manually by calling -C<load_classes> for the Row classes and defining in those classes any -required resultset classes. +L<load_classes|DBIx::Class::Schema/load_classes> for the +L<Result classes|DBIx::Class::Manual::ResultClass> and +defining in those classes any required ResultSet classes. + +=head3 Result class + +=head4 Header Next, create each of the classes you want to load as specified above: package My::Schema::Result::Album; use base qw/DBIx::Class::Core/; -Load any additional components you may need with the load_components() method, -and provide component configuration if required. For example, if you want +Load any additional components you may need with the +L<load_components|Class::C3::Componentised/load_components( @comps )> method, +and provide component configuration if required. For example, if you want automatic row ordering: __PACKAGE__->load_components(qw/ Ordered /); __PACKAGE__->position_column('rank'); -Ordered will refer to a field called 'position' unless otherwise directed. Here you are defining -the ordering field to be named 'rank'. (NOTE: Insert errors may occur if you use the Ordered -component, but have not defined a position column or have a 'position' field in your row.) +(See L<DBIx::Class::Ordered> for more information.) Set the table for your class: __PACKAGE__->table('album'); +=head4 Columns + Add columns to your class: __PACKAGE__->add_columns(qw/ albumid artist title rank /); @@ -130,43 +195,42 @@ Add columns to your class: 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<add_columns> a hash: - __PACKAGE__->add_columns(albumid => - { accessor => 'album', - data_type => 'integer', - size => 16, - is_nullable => 0, - is_auto_increment => 1, - }, - artist => - { data_type => 'integer', - size => 16, - is_nullable => 0, - }, - title => - { data_type => 'varchar', - size => 256, - is_nullable => 0, - }, - rank => - { data_type => 'integer', - size => 16, - is_nullable => 0, - default_value => 0, - } - ); - -DBIx::Class doesn't directly use most of this data yet, but various related -modules such as L<HTML::FormHandler::Model::DBIC> make use of it. -Also it allows you to create your database tables from your Schema, -instead of the other way around. -See L<DBIx::Class::Schema/deploy> for details. - -See L<DBIx::Class::ResultSource> for more details of the possible column + __PACKAGE__->add_columns( + albumid => { + accessor => 'album', + data_type => 'integer', + size => 16, + is_auto_increment => 1, + }, + artist => { + data_type => 'integer', + size => 16, + }, + title => { + data_type => 'varchar', + size => 256, + is_nullable => 1, + }, + rank => { + data_type => 'integer', + size => 16, + default_value => 0, + }, + ); + +L<DBIC|DBIx::Class> doesn't directly use most of this data, but various +related modules, such as L<HTML::FormHandler::Model::DBIC>, make use of it. Also, +it allows you to create your database tables from your Schema, instead of the +other way around. See L<DBIx::Class::Schema/deploy> for details. + +See L<DBIx::Class::ResultSource/add_columns> for more details of the possible column attributes. -Accessors are created for each column automatically, so My::Schema::Result::Album will -have albumid() (or album(), when using the accessor), artist() and title() -methods. +Accessors are created for each column automatically, so C<My::Schema::Result::Album> +will have C<albumid> (or C<album>, when using the accessor shown above), C<artist> +and C<title> methods. + +=head4 Primary Key Define a primary key for your class: @@ -176,10 +240,16 @@ If you have a multi-column primary key, just pass a list instead: __PACKAGE__->set_primary_key( qw/ albumid artistid / ); -Define this class' relationships with other classes using either C<belongs_to> -to describe a column which contains an ID of another Table, or C<has_many> to -make a predefined accessor for fetching objects that contain this Table's -foreign key: +See also L</The Significance and Importance of Primary Keys>. + +=head4 Relationships + +Define this class' relationships with other classes to make predefined accessors +for your L<Result class|DBIx::Class::Manual::ResultClass>. Use either +L<belongs_to|DBIx::Class::Relationship/belongs_to> to describe columns which +contain an ID of another Table, or +L<has_many|DBIx::Class::Relationship/has_many> to fetch objects that contain this +Table's foreign key: # in My::Schema::Result::Artist __PACKAGE__->has_many('albums', 'My::Schema::Result::Album', 'artist'); @@ -189,9 +259,9 @@ available relationships and how you can design your own. =head2 Using DBIx::Class::Schema::Loader -This module (L<DBIx::Class::Schema::Loader>) is an external module, and not part -of the L<DBIx::Class> distribution. It inspects your database, and automatically -creates classes for all the tables in your schema. +L<DBICSL|DBIx::Class::Schema::Loader> is an external module, and not part +of the L<DBIC|DBIx::Class> distribution. It inspects your database, and +automatically creates classes for all the tables in your schema. The simplest way to use it is via the L<dbicdump> script from the L<DBIx::Class::Schema::Loader> distribution. For example: @@ -329,7 +399,7 @@ directly on a ResultSet object. =head2 Finding your objects -L<DBIx::Class> provides a few different ways to retrieve data from your +L<DBIC|DBIx::Class> provides a few different ways to retrieve data from your database. Here's one example: # Find all of Santana's albums @@ -413,29 +483,29 @@ L<DBIx::Class::ResultSet/ATTRIBUTES>. =head2 The Significance and Importance of Primary Keys The concept of a L<primary key|DBIx::Class::ResultSource/set_primary_key> in -DBIx::Class warrants special discussion. The formal definition (which somewhat +L<DBIC|DBIx::Class> warrants special discussion. The formal definition (which somewhat resembles that of a classic RDBMS) is I<a unique constraint that is least -likely to change after initial row creation>. However this is where the -similarity ends. Any time you call a CRUD operation on a row (e.g. +likely to change after initial row creation>. However, this is where the +similarity ends. Any time you call a CRUD operation on a row (e.g. L<delete|DBIx::Class::Row/delete>, L<update|DBIx::Class::Row/update>, L<discard_changes|DBIx::Class::Row/discard_changes>, -etc.) DBIx::Class will use the values of of the +etc.), L<DBIC|DBIx::Class> will use the values of of the L<primary key|DBIx::Class::ResultSource/set_primary_key> columns to populate -the C<WHERE> clause necessary to accomplish the operation. This is why it is +the C<WHERE> clause necessary to accomplish the operation. This is why it is important to declare a L<primary key|DBIx::Class::ResultSource/set_primary_key> on all your result sources B<even if the underlying RDBMS does not have one>. -In a pinch one can always declare each row identifiable by all its columns: +In a pinch, one can always declare each row identifiable by all its columns: __PACKAGE__->set_primary_key(__PACKAGE__->columns); -Note that DBIx::Class is smart enough to store a copy of the PK values before +Note that L<DBIC|DBIx::Class> is smart enough to store a copy of the PK values before any row-object changes take place, so even if you change the values of PK -columns the C<WHERE> clause will remain correct. +columns, the C<WHERE> clause will remain correct. -If you elect not to declare a C<primary key>, DBIx::Class will behave correctly +If you elect not to declare a C<primary key>, L<DBIC|DBIx::Class> will behave correctly by throwing exceptions on any row operation that relies on unique identifiable -rows. If you inherited datasets with multiple identical rows in them, you can +rows. If you inherited datasets with multiple identical rows in them, you can still operate with such sets provided you only utilize L<DBIx::Class::ResultSet> CRUD methods: L<search|DBIx::Class::ResultSet/search>, @@ -451,7 +521,7 @@ a declared PK): $row->update({ children => 2 }); # <-- exception thrown because $row isn't # necessarily unique -So instead the following should be done: +So, instead the following should be done: $schema->resultset('People') ->search({ last_name => 'Dantes' }) @@ -469,6 +539,8 @@ information on this can be found in L<DBIx::Class::Manual::Troubleshooting> =item * L<DBIx::Class::Manual::Cookbook> +=item * L<DBIx::Class::Manual::ResultClass> + =back =cut