[Catalyst-commits] r7323 - trunk/examples/CatalystAdvent/root/2007
castaway at dev.catalyst.perl.org
castaway at dev.catalyst.perl.org
Sun Dec 23 10:25:40 GMT 2007
Author: castaway
Date: 2007-12-23 10:25:39 +0000 (Sun, 23 Dec 2007)
New Revision: 7323
Added:
trunk/examples/CatalystAdvent/root/2007/22.pod
Log:
Day 22
Added: trunk/examples/CatalystAdvent/root/2007/22.pod
===================================================================
--- trunk/examples/CatalystAdvent/root/2007/22.pod (rev 0)
+++ trunk/examples/CatalystAdvent/root/2007/22.pod 2007-12-23 10:25:39 UTC (rev 7323)
@@ -0,0 +1,321 @@
+=head1 NAME
+
+DBIx::Class::Tutorial::Part1
+
+=head1 DESCRIPTION
+
+This is part one of a DBIx::Class tutorial I started writing for the
+folks at work, I hope to turn it into a multi-part releasable
+document.
+
+=head1 GLOSSARY
+
+See L<DBIx::Class::Manual::Glossary>.
+
+Some terms needed for this doc:
+
+=over
+
+=item Schema
+
+A L<DBIx::Class::Schema> object is created by calling C<< ->connect >>
+on our Schema subclass, and passing it the DSN (and other possible
+values, see L<DBIx::Class::Schema>). Eg: C<< my $schema =
+Breadcrumbs::Schema->connect('dbi:mysql;dbname=Breadcrumbs',
+'user', 'passwd'); >> Creating this object does not attempt to connect
+to the database yet.
+
+NB: To make use of an existing mechanism to store credentials for
+databases, we can do: C<< my $schema = Breadcrumbs::Schema->connect(
+sub { My::Util('Breadcrumbs') } ); >>.
+
+=item ResultSet
+
+A L<DBIx::Class::ResultSet>, an object which represents a database
+query, including all conditions/clauses. Creating one does not run the
+associated query, it is stored and used when actual data objects are
+requested. The simplest form of ResultSet represents an entire table,
+and can be retrieved from the schema object like this: C<< my $rs =
+$schema->resultset('Path') >>.
+
+=item ResultSource
+
+A class that describes a table, it's columns and it's relations with
+other tables.
+
+=item Row
+
+A L<DBIx::Class::Row> representing an actual row of data resulting
+from a database query. This could be and entire row, or just certain
+fields as restricted by a ResultSet.
+
+=head1 SETUP
+
+To create a new set of DBIx::Class L<DBIx::Class::ResultSource>
+classes, you can either write them by hand, or create from an existing
+database.
+
+=head2 Create ResultSource classes from existing database
+
+L<DBIx::Class::Schema::Loader>'s C<make_schema_at> can extract schema
+definitions from existing databases and create a complete
+L<DBIx::Class> schema for you, example:
+
+ perl -MDBIx::Class::Schema::Loader=make_schema_at,dump_to_dir:./lib -e 'make_schema_at("Breadcrumbs::Schema", { debug => 1 }, [ "dbi:mysql:dbname=Breadcrumbs","user", "passwd" ])'
+
+This will create a file for each database table (in
+F<lib/Breadcrumbs/Schema/>), plus the file F<Breadcrumbs/Schema.pm> which is
+the L<DBIx::Class::Schema> file.
+
+The table files will contain information about the columns and their
+types. If the database is innodb, it will also extract relationships
+based on foreign key references etc. An md5 sum of the contents is
+added, so that the user can add more relations or other methods to the
+file, and a later update will not overwrite them.
+
+=head2 If the database layout changes
+
+Re-run the C<make_schema_at> command shown above.
+
+=head2 Create ResultSource classes from scratch
+
+See L<DBIx::Class::Manual::Intro/SETTING UP DBIx::Class>.
+
+=head2 Setting up relationships
+
+If your databases is mysql and not using innodb, you will have
+to add the table relationships ourselves. These are the most useful
+part of DBIx::Class, otherwise we might as well just use DBI..
+
+There are 3 main/useful relationship types, for the rest and a more wordy
+description, see L<DBIx::Class::Relationship>.
+
+The C<name> of each relationship (the first argument), is important,
+it is used both as an accessor and as a key to joni across tables.
+
+=head2 belongs_to (foreign keys)
+
+=begin notes
+
+Create duplicate blueprint db to play with and show how this works.
+
+Also figure out SQL created and add to doc.
+
+=end notes
+
+Breadcrumbs's C<Name> table has a C<PathID> column which contains
+an ID from the C<Path> table. To make the Path object simple to
+retrieve and update, we add the following to the Name.pm file,
+after the md5 sum from Loader:
+
+ __PACKAGE__->belongs_to('path', 'Breadcrumbs::Schema::Path', 'PathID');
+
+Read as: The value in the C<PathID> column belongs_to the table
+represented by C<Breadcrumbs::Schema::Path>.
+
+This creates an accessor C<path>, which we can call on a Name
+L<DBIx::Class::Row> which will retrieve the appropriate
+L<DBIx::Class::Row> in the C<Path> table.
+
+ ## Print the path of the current name entry
+ print $name->path->path;
+
+We can also set a PathID for a new Name row by calling:
+
+ $name->path($pathobj);
+ $name->update;
+
+=head2 has_many (reverse foreign key)
+
+Going in the opposite direction, each C<Path> row has 0 to many
+C<Name> entries associated with it, indicated by the C<PathID> column
+in the C<Name> table. We can make it simple to retrieve all the name
+values for a particular C<Path> row:
+
+ __PACKAGE__->has_many('names', 'Breadcrumbs::Schema::Name', 'PathID');
+
+Read as: This class has many C<names> in C<Breadcrumbs::Schema::Name>
+linked via the C<PathID> column.
+
+Creates us an accessor C<names> which can give us a
+L<DBIx::Class::ResultSet> (of which more later), or an array of
+C<Name> objects.
+
+ ## find all names for current path entry
+ foreach my $name ($path->names)
+ {
+ print $name->name;
+ }
+
+Add a new name for the current path, assuming we have a language
+object as well:
+
+ $path->create_related('names',
+ { name => 'Products',
+ lang => $lang }
+ );
+
+
+
+=head1 GETTING DATA
+
+Whether fetching complete rows, searching for rows, or fetching data
+from multiple tables, the methods are much the same, all return
+L<DBIx::Class::Row> objects eventually.
+
+=head2 find (single row by unique key)
+
+To fetch a full row using it's Primary Key (they all have PKs,
+right?), we can retrieve a Row object directly from a ResultSet
+representing the table:
+
+ ## Just PK:
+ my $name1 = $schema->resultset('Name')->find(1);
+
+ ## More meaningful:
+ my $name1 = $schema->resultset('Name')->find({ id => 1 });
+
+The Row object contains all the values of all the fields defined in
+the ResultSource. Not necessarily in the table. Each column has an
+accessor which is by default the name of the column lower-cased (When
+using Loader).
+
+ my $namename = $name1->name; ## Japan
+ my $langid = $name1->langid; ## 1
+
+=head2 search (multiple objects, less coluns, conditions)
+
+To fetch a particular Name row and it's Path at the
+same time, using only one database query:
+
+ my $name_rs = $schema->resultset('Name')->search(
+ { 'me.id' => 1 }, ## WHERE
+ { prefetch => [ 'path' ] } ## JOIN AND SELECT. NB: Rel name!
+
+This creates a L<DBIx::Class::ResultSet>, to retrieve the actual Row object:
+
+ my $namerow = $name_rs->next;
+
+The following SQL is executed:
+
+ SELECT me.ID, me.Name, me.LangID, me.PathID, path.ID, path.Path, path.Parent, path.Position, path.MenuID FROM Name me JOIN Path path ON ( path.ID = me.PathID ) WHERE ( me.id = ? ): '1'
+
+
+As with the find, the data is retrieved using the column-name
+accessors. To retrieve a Row object representing the path:
+
+ my $pathrow = $namerow->path;
+
+ my $actualpath = $pathrow->path; ## companyinfo/careers/jp
+ my $pathparent = $pathrow->parent; ## 36
+
+To fetch just a few columns:
+
+ my $name_rs = $schema->resultset('Name')->search(
+ { 'me.id' => 1 }, ## WHERE
+ {
+ select => [ qw/id name/ ], ## SELECT
+ as => [ qw/id name/ ],
+ }
+
+This will only select the ID and Name columns. The C<as> attribute names the columns.
+
+To add more complex conditions:
+
+ ## All names where the patch matches '/support%'
+ my $name_search = $schema->resultset('Name')->search(
+ {
+ 'path.path' => { 'like' => '/support/%' }, ## WHERE path.path LIKE '/support/%'
+ },
+ {
+ 'join' => [ 'path' ], ## JOIN
+ }
+
+=head1 INSERTING DATA
+
+To insert new rows, call C<create> on a ResultSet object. This will
+first instantiate a new Row object, then call C<insert> on it. This
+can be done individually if needed.
+
+ ## INSERT INTO ..
+ my $newname = $schema->resultset('Name')->create(
+ {
+ name => 'Support',
+ pathid => $pathid,
+ langid => $langid,
+ });
+ print $newname->in_storage(); ## 1 (TRUE)
+
+ my $newpath = $schema->resultset('Path')->new(
+ {
+ path => 'support',
+ });
+ print $newpath->in_storage(); ## 0 (FALSE)
+ $newpath->insert();
+ print $newpath->in_storage(); ## 1 (TRUE)
+
+=head1 CHANGING DATA
+
+=head2 Updating one row
+
+The column-name accessors used to fetch data from a row can also be
+used to set new values, eg:
+
+ ## Nonsensically change the language of a given Name row
+ my $namerow = $schema->resultset('Name')->find({ name => 'Japan' });
+ $namerow->langid(2);
+ $namerow->update; ## UPDATE
+
+=head2 Updating many rows with a resultset
+
+Create a resultset containing the condition to select all the rows to
+update. Calling C<update> on the resultset will run the C<UPDATE>
+command using the condition in the resultset.
+
+ ## All name rows with value 'Evaluation'
+ my $name_rs = $schema->resultset('Name')->search(
+ {
+ 'me.name' => 'Evaluation',
+ });
+ $name_rs->update({ name => 'Free Trial' }); ## UPDATE .. WHERE
+
+=head3 Deleting a row
+
+Just call C<delete> on a C<Row> object.
+
+ ## No more products/es !
+ my $pathrow = $schema->resultset('Path')->find({ path => 'products/es' });
+ $pathrow->delete;
+
+This will also delete related rows that would otherwise be
+inconsistent. It will remove them *after* the main row. This cascading
+can be turned off on a relationship if necessary.
+
+=head3 Delete multiple rows
+
+Call C<delete> on a ResultSet object instead. This will run a
+C<DELETE> statement with a C<WHERE> clause, and will not cascade the
+deletes.
+
+ my $path_rs = $schema->resultset('Path')->search(
+ {
+ 'me.path' => 'products/es',
+ });
+ $path_rs->delete; ## DELETE .. WHERE
+
+To delete multiple rows with cascading, call C<delete_all> on the
+ResultSet, which will delete each row and it's related rows
+individually.
+
+ $path_rs->delete_all; ## many DELETE statements
+
+=head1 TODO
+
+Add some sorta table descriptions to this doc.
+
+Patches and suggestions welcome.
+
+=head1 AUTHOR
+
+Jess Robinson <castaway at desert-island.me.uk>
+
More information about the Catalyst-commits
mailing list