[Catalyst-commits] r12418 - in trunk/examples/CatalystAdvent/root/2009: . pen

ribasushi at dev.catalyst.perl.org ribasushi at dev.catalyst.perl.org
Thu Dec 17 00:37:55 GMT 2009


Author: ribasushi
Date: 2009-12-17 00:37:55 +0000 (Thu, 17 Dec 2009)
New Revision: 12418

Added:
   trunk/examples/CatalystAdvent/root/2009/17.pod
Removed:
   trunk/examples/CatalystAdvent/root/2009/pen/dbic-multicreate.pod
Modified:
   trunk/examples/CatalystAdvent/root/2009/pen/ideas.txt
Log:
off we go

Copied: trunk/examples/CatalystAdvent/root/2009/17.pod (from rev 12417, trunk/examples/CatalystAdvent/root/2009/pen/dbic-multicreate.pod)
===================================================================
--- trunk/examples/CatalystAdvent/root/2009/17.pod	                        (rev 0)
+++ trunk/examples/CatalystAdvent/root/2009/17.pod	2009-12-17 00:37:55 UTC (rev 12418)
@@ -0,0 +1,259 @@
+=head1 Efficiently create multiple related rows in DBIx::Class
+
+This article will talk about something seemingly simple and boring - 
+inserting new rows into your database. If you frequent 
+C<irc.perl.org#dbix-class> you probably have heard of the mysterious 
+MultiCreate, yet you are not entirely sure if it can bring you fame and money.
+Are you watching closely?
+
+=head2 The Pledge
+
+Let's examine the dreaded Artist/CDs example:
+
+ package MySchema::Artist;
+ 
+ use base 'DBIx::Class::Core';
+
+ __PACKAGE__->table ('artist');
+
+ __PACKAGE__->add_columns (
+   id => {
+     data_type => 'int',
+     is_auto_increment => 1,
+   },
+   name => {
+     data_type => 'varchar',
+   },
+ );
+
+ __PACKAGE__->set_primary_key ('id');
+
+ __PACKAGE__->has_many (cds => 'MySchema::CD', 'artist_id');
+
+and
+
+ package MySchema::CD;
+  
+ use base 'DBIx::Class::Core';
+ 
+ __PACKAGE__->table ('cd');
+
+ __PACKAGE__->add_columns (   
+   id => {
+     data_type => 'int',
+     is_auto_increment => 1,
+   },
+   title => {
+     data_type => 'varchar',
+   },
+   artist_id => {
+     data_type => 'int',
+   },
+ );
+
+ __PACKAGE__->set_primary_key ('id');
+
+ __PACKAGE__->belongs_to (artist => 'MySchema::Artist', 'artist_id');
+
+=head2 The Turn
+
+Say some XML describing a modest CD collection needs to be parssed and stuffed
+in the above tables, while preserving relational integrity. A seasoned
+programmer will sigh, drink some coffee and write the following code:
+
+ for my $chunk (@data) {
+
+   my $artist = $schema->resultset ('Artist')->create ({
+     name => $chunk->artist_name
+   });
+
+   for my $cd_title (@{$chunk->{cds}}) {
+     $schema->resultset ('CD')->create ({
+       title => $cd_title,
+       artist_id => $artist->id,
+     });
+   }
+ }
+
+After the coffee kicks in, perhaps the programmer will remember that he
+already has declared relationships between Artist and CD. So he will reduce
+the second inner loop to:
+
+ ...
+   for my $cd_title (@{$chunk->{cds}}) {
+     $artist->create_related ('cds', { title => $cd_title });
+   }
+ ...
+
+This is better, but it suffers from a number of deficiencies:
+
+=over
+
+=item
+
+If the XML contained Tracks of individual CDs we would need a 2nd nested
+loop, which may result in another loop and so on.
+
+=item
+
+The entire operation probably needs to be wrapped in a transaction - that's
+yet more boilerplate to cargo cult from code you've already written elsewhere.
+
+=item
+
+B<It just doesn't feel like DRY>
+
+=back
+
+So the depressed developer sets out to write even more boring dull code.
+
+=head2 The Prestige
+
+An enlightened coworker arrives, looks at the code and changes it to:
+
+ for my $chunk (@data) {
+   my $artist_with_cds = $schema->resultset ('Artist')->create ($chunk);
+ }
+
+Then looks closer at @data, makes sure it doesn't contain anything 
+L<DBIx::Class> wouldn't know how to handle and changes the entire loop to 
+just:
+
+ $schema->resultset ('Artist')->populate (\@data);
+
+=head1 The Trick Revealed
+
+Just like being able to L<turn ResultSets into nested 
+structures|DBIx::Class::ResultClass::HashRefInflator>, L<DBIx::Class> can 
+turn such structures back to related rows. All you have to do is augment the 
+hashref you normally pass to L<DBIx::Class::ResultSet/create> with a 
+structure representing the related row(s), keyed off the B<relationship 
+name>. The structure will be either another hashref (for single-type 
+relationships i.e. L<belongs_to|DBIx::Class::Relationship/belongs_to>, 
+L<has_one|DBIx::Class::Relationship/has_one>, 
+L<might_have|DBIx::Class::Relationship/might_have>) or an arrayref of 
+hashrefs for L<has_many|DBIx::Class::Relationship/has_many> (relationship of 
+type multi).
+
+When the enlightened coworker did C<< ->create ($chunk) >> he effectively 
+supplied:
+
+ {
+   name => 'somename',
+   cds => [
+     {
+       title => 'cd1',
+     },     
+     {
+       title => 'cd2',
+     },
+     ...
+   ],
+ }
+
+which was properly taken apart and inserted in the appropriate tables using 
+the appropriate foreign keys as one would expect. In addition since 
+L<create()|DBIx::Class::ResultSet/create> is a single call, L<DBIx::Class> 
+takes care of making it atomic by dutifully wrapping the entire operation in 
+a L<transaction|DBIx::Class::Manual::Cookbook/TRANSACTIONS>.
+
+The same logic applies to L<DBIx::Class::ResultSet/populate> which happily 
+accepts an arrayref of hashrefs as the creation argument. In fact you can 
+bootstrap a poor man's replication by simply dumping a 
+L<ResultSet|DBIx::Class::ResultSet> via 
+L<DBIx::Class::ResultClass::HashRefInflator>, and feeding the result straight 
+back to L<create()|DBIx::Class::ResultSet/create>, using a 
+L<ResultSet|DBIx::Class::ResultSet> derived from the target 
+L<Schema|DBIx::Class::Schema/resultset>.
+
+=head2 But does it scale?
+
+The example above is rather simple, but what if we have a complex chain of 
+relationships? For example:
+
+ Artist->has_many ('cds', ...)
+ CD->has_many ('cd2producer', ...)
+ CD2Producer->belongs_to ('producer', ...)
+ ...and so on
+
+Won't L<DBIx::Class> attempt to create the same producer several times? 
+Fortunately no: any creation attempt over a 
+L<belongs_to|DBIx::Class::Relationship/belongs_to> relationship will be 
+executed via a L<DBIx::Class::ResultSet/find_or_create>, thus allowing 
+everything to just work. For the brave there is an L<example pushing all 
+conceivable limits|http://cpansearch.perl.org/src/FREW/DBIx-Class-0.08115/t/multi_create/torture.t>.
+
+=head2 But there got to be *some* caveats...?
+
+=over
+
+=item
+
+Currently this works for L<create()|DBIx::Class::ResultSet/create> only. 
+While L<find_or_create|DBIx::Class::ResultSet/find_or_create> will be 
+executed on L<belongs_to|DBIx::Class::Relationship/belongs_to> relationships, 
+the final goal is nothing more than new row creation. This means that e.g. 
+passing a nested structure to L<DBIx::Class::ResultSet/update_or_create> will 
+not do anything close to what you would expect.
+
+=item
+
+The returned object does not always have the proper related objects inserted 
+at the expected slots (similar to the effect of 
+L<prefetch|DBIx::Class::ResultSet/prefetch>). We need a good amount of tests 
+before we attempt to make this work as expected - patches welcome!
+
+=item
+
+The L<find_or_create|DBIx::Class::ResultSet/find_or_create> use mentioned 
+above can result in some strange behaviour. Consider:
+
+ my $cd_data = {
+   artist_id => $some_artist->id,
+   title => 'my cd',
+   genre => {
+     name => 'vague genre',
+     cds => [
+       {
+         title => 'oddball cd',
+         artist_id => $some_other_artist->id,
+       },
+     ],
+   }
+ };
+
+If the I<vague genre> genre already exists, L<DBIx::Class> will B<not> 
+descend to check if the I<oddball cd> is in fact created. This has not yet 
+caused anyone grief, thus there is no motivation for the (non trivial) fix.
+
+=back
+
+=head1 What The Future Holds
+
+While the new row creation is mostly done and over with (in fact it delayed 
+the release of the 0.081xx series by several months), there are more ways to 
+expand the existing codebase.
+
+=over
+
+=item
+
+Port L<DBIx::Class::ResultSet::RecursiveUpdate> to leverage the (substantial) 
+infrastructure behind MultiCreate. While some conceptual differences 
+exist between both modules, they can certainly be resolved during the 
+test-writing phase.
+
+=item
+
+Merge create and update into the logical multi_update_or_create !
+
+=back
+
+If you have tuits or simply ideas - you are always welcome to hang out on 
+IRC, and start a discussion (or snatch a commit-bit and start hacking)
+
+Happy search()ing!
+
+=head1 Author
+
+Peter Rabbitson <ribasushi at cpan.org>


Property changes on: trunk/examples/CatalystAdvent/root/2009/17.pod
___________________________________________________________________
Name: svn:mergeinfo
   + 

Deleted: trunk/examples/CatalystAdvent/root/2009/pen/dbic-multicreate.pod
===================================================================
--- trunk/examples/CatalystAdvent/root/2009/pen/dbic-multicreate.pod	2009-12-17 00:36:31 UTC (rev 12417)
+++ trunk/examples/CatalystAdvent/root/2009/pen/dbic-multicreate.pod	2009-12-17 00:37:55 UTC (rev 12418)
@@ -1,259 +0,0 @@
-=head1 Efficiently create multiple related rows in DBIx::Class
-
-This article will talk about something seemingly simple and boring - 
-inserting new rows into your database. If you frequent 
-C<irc.perl.org#dbix-class> you probably have heard of the mysterious 
-MultiCreate, yet you are not entirely sure if it can bring you fame and money.
-Are you watching closely?
-
-=head2 The Pledge
-
-Let's examine the dreaded Artist/CDs example:
-
- package MySchema::Artist;
- 
- use base 'DBIx::Class::Core';
-
- __PACKAGE__->table ('artist');
-
- __PACKAGE__->add_columns (
-   id => {
-     data_type => 'int',
-     is_auto_increment => 1,
-   },
-   name => {
-     data_type => 'varchar',
-   },
- );
-
- __PACKAGE__->set_primary_key ('id');
-
- __PACKAGE__->has_many (cds => 'MySchema::CD', 'artist_id');
-
-and
-
- package MySchema::CD;
-  
- use base 'DBIx::Class::Core';
- 
- __PACKAGE__->table ('cd');
-
- __PACKAGE__->add_columns (   
-   id => {
-     data_type => 'int',
-     is_auto_increment => 1,
-   },
-   title => {
-     data_type => 'varchar',
-   },
-   artist_id => {
-     data_type => 'int',
-   },
- );
-
- __PACKAGE__->set_primary_key ('id');
-
- __PACKAGE__->belongs_to (artist => 'MySchema::Artist', 'artist_id');
-
-=head2 The Turn
-
-Say some XML describing a modest CD collection needs to be parssed and stuffed
-in the above tables, while preserving relational integrity. A seasoned
-programmer will sigh, drink some coffee and write the following code:
-
- for my $chunk (@data) {
-
-   my $artist = $schema->resultset ('Artist')->create ({
-     name => $chunk->artist_name
-   });
-
-   for my $cd_title (@{$chunk->{cds}}) {
-     $schema->resultset ('CD')->create ({
-       title => $cd_title,
-       artist_id => $artist->id,
-     });
-   }
- }
-
-After the coffee kicks in, perhaps the programmer will remember that he
-already has declared relationships between Artist and CD. So he will reduce
-the second inner loop to:
-
- ...
-   for my $cd_title (@{$chunk->{cds}}) {
-     $artist->create_related ('cds', { title => $cd_title });
-   }
- ...
-
-This is better, but it suffers from a number of deficiencies:
-
-=over
-
-=item
-
-If the XML contained Tracks of individual CDs we would need a 2nd nested
-loop, which may result in another loop and so on.
-
-=item
-
-The entire operation probably needs to be wrapped in a transaction - that's
-yet more boilerplate to cargo cult from code you've already written elsewhere.
-
-=item
-
-B<It just doesn't feel like DRY>
-
-=back
-
-So the depressed developer sets out to write even more boring dull code.
-
-=head2 The Prestige
-
-An enlightened coworker arrives, looks at the code and changes it to:
-
- for my $chunk (@data) {
-   my $artist_with_cds = $schema->resultset ('Artist')->create ($chunk);
- }
-
-Then looks closer at @data, makes sure it doesn't contain anything 
-L<DBIx::Class> wouldn't know how to handle and changes the entire loop to 
-just:
-
- $schema->resultset ('Artist')->populate (\@data);
-
-=head1 The Trick Revealed
-
-Just like being able to L<turn ResultSets into nested 
-structures|DBIx::Class::ResultClass::HashRefInflator>, L<DBIx::Class> can 
-turn such structures back to related rows. All you have to do is augment the 
-hashref you normally pass to L<DBIx::Class::ResultSet/create> with a 
-structure representing the related row(s), keyed off the B<relationship 
-name>. The structure will be either another hashref (for single-type 
-relationships i.e. L<belongs_to|DBIx::Class::Relationship/belongs_to>, 
-L<has_one|DBIx::Class::Relationship/has_one>, 
-L<might_have|DBIx::Class::Relationship/might_have>) or an arrayref of 
-hashrefs for L<has_many|DBIx::Class::Relationship/has_many> (relationship of 
-type multi).
-
-When the enlightened coworker did C<< ->create ($chunk) >> he effectively 
-supplied:
-
- {
-   name => 'somename',
-   cds => [
-     {
-       title => 'cd1',
-     },     
-     {
-       title => 'cd2',
-     },
-     ...
-   ],
- }
-
-which was properly taken apart and inserted in the appropriate tables using 
-the appropriate foreign keys as one would expect. In addition since 
-L<create()|DBIx::Class::ResultSet/create> is a single call, L<DBIx::Class> 
-takes care of making it atomic by dutifully wrapping the entire operation in 
-a L<transaction|DBIx::Class::Manual::Cookbook/TRANSACTIONS>.
-
-The same logic applies to L<DBIx::Class::ResultSet/populate> which happily 
-accepts an arrayref of hashrefs as the creation argument. In fact you can 
-bootstrap a poor man's replication by simply dumping a 
-L<ResultSet|DBIx::Class::ResultSet> via 
-L<DBIx::Class::ResultClass::HashRefInflator>, and feeding the result straight 
-back to L<create()|DBIx::Class::ResultSet/create>, using a 
-L<ResultSet|DBIx::Class::ResultSet> derived from the target 
-L<Schema|DBIx::Class::Schema/resultset>.
-
-=head2 But does it scale?
-
-The example above is rather simple, but what if we have a complex chain of 
-relationships? For example:
-
- Artist->has_many ('cds', ...)
- CD->has_many ('cd2producer', ...)
- CD2Producer->belongs_to ('producer', ...)
- ...and so on
-
-Won't L<DBIx::Class> attempt to create the same producer several times? 
-Fortunately no: any creation attempt over a 
-L<belongs_to|DBIx::Class::Relationship/belongs_to> relationship will be 
-executed via a L<DBIx::Class::ResultSet/find_or_create>, thus allowing 
-everything to just work. For the brave there is an L<example pushing all 
-conceivable limits|http://cpansearch.perl.org/src/FREW/DBIx-Class-0.08115/t/multi_create/torture.t>.
-
-=head2 But there got to be *some* caveats...?
-
-=over
-
-=item
-
-Currently this works for L<create()|DBIx::Class::ResultSet/create> only. 
-While L<find_or_create|DBIx::Class::ResultSet/find_or_create> will be 
-executed on L<belongs_to|DBIx::Class::Relationship/belongs_to> relationships, 
-the final goal is nothing more than new row creation. This means that e.g. 
-passing a nested structure to L<DBIx::Class::ResultSet/update_or_create> will 
-not do anything close to what you would expect.
-
-=item
-
-The returned object does not always have the proper related objects inserted 
-at the expected slots (similar to the effect of 
-L<prefetch|DBIx::Class::ResultSet/prefetch>). We need a good amount of tests 
-before we attempt to make this work as expected - patches welcome!
-
-=item
-
-The L<find_or_create|DBIx::Class::ResultSet/find_or_create> use mentioned 
-above can result in some strange behaviour. Consider:
-
- my $cd_data = {
-   artist_id => $some_artist->id,
-   title => 'my cd',
-   genre => {
-     name => 'vague genre',
-     cds => [
-       {
-         title => 'oddball cd',
-         artist_id => $some_other_artist->id,
-       },
-     ],
-   }
- };
-
-If the I<vague genre> genre already exists, L<DBIx::Class> will B<not> 
-descend to check if the I<oddball cd> is in fact created. This has not yet 
-caused anyone grief, thus there is no motivation for the (non trivial) fix.
-
-=back
-
-=head1 What The Future Holds
-
-While the new row creation is mostly done and over with (in fact it delayed 
-the release of the 0.081xx series by several months), there are more ways to 
-expand the existing codebase.
-
-=over
-
-=item
-
-Port L<DBIx::Class::ResultSet::RecursiveUpdate> to leverage the (substantial) 
-infrastructure behind MultiCreate. While some conceptual differences 
-exist between both modules, they can certainly be resolved during the 
-test-writing phase.
-
-=item
-
-Merge create and update into the logical multi_update_or_create !
-
-=back
-
-If you have tuits or simply ideas - you are always welcome to hang out on 
-IRC, and start a discussion (or snatch a commit-bit and start hacking)
-
-Happy search()ing!
-
-=head1 Author
-
-Peter Rabbitson <ribasushi at cpan.org>

Modified: trunk/examples/CatalystAdvent/root/2009/pen/ideas.txt
===================================================================
--- trunk/examples/CatalystAdvent/root/2009/pen/ideas.txt	2009-12-17 00:36:31 UTC (rev 12417)
+++ trunk/examples/CatalystAdvent/root/2009/pen/ideas.txt	2009-12-17 00:37:55 UTC (rev 12418)
@@ -9,7 +9,6 @@
  - DBIx::Class::InstancePerContext & DBIx::Class::Schema::RestrictWithObject   - zamolxes and dhoss? groditi?
  - DBIx::Class::Helpers - frew 
  - gitalist - zts
- - ribasushi multicreate
  - config for beginners (t0m)
 
 Articles structure
@@ -60,3 +59,4 @@
  - DBIx::Class::ResultSet::WithMetaData - lukes (done before 11th Dec)  (dec 12) 
  - CatalystX::Dispatcher::AsGraph / Template::AsGraph / SQL::Translator::Producer::Diagram  - garu + zamolxes ( dec 14th?)
  - dbic txns - caelum, dec 15 
+ - dbic multicreate - ribasushi, dec 17




More information about the Catalyst-commits mailing list