• Share
  • Email
  • Embed
  • Like
  • Save
  • Private Content
DBIx::Class beginners
 

DBIx::Class beginners

on

  • 46,148 views

Introduction to DBIx::Class.

Introduction to DBIx::Class.

Statistics

Views

Total Views
46,148
Views on SlideShare
45,665
Embed Views
483

Actions

Likes
28
Downloads
763
Comments
4

20 Embeds 483

http://www.slideshare.net 212
http://thomas-fahle.blogspot.com 133
http://irr.posterous.com 50
http://thomas-fahle.blogspot.de 41
http://pradeeppant.com 20
http://www.kt.rim.or.jp 5
http://www.linkedin.com 3
file:// 3
http://lj-toys.com 2
http://209.85.129.132 2
http://thomas-fahle.blogspot.co.at 2
http://webcache.googleusercontent.com 2
http://l.lj-toys.com 1
http://www.slashdocs.com 1
http://localhost 1
http://thomas-fahle.blogspot.ch 1
http://www.techgig.com 1
http://static.slideshare.net 1
http://www.brijj.com 1
http://www.thomas-fahle.blogspot.de 1
More...

Accessibility

Upload Details

Uploaded via as Adobe PDF

Usage Rights

© All Rights Reserved

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel

14 of 4 previous next Post a comment

  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Processing…
  • Your presentation is excellent. I have written a post of my blog http://pradeeppant.com/2011/02/28/object-relational-wrapper-in-perl/
    I will now update my presentation with 2010 or may be shall wait if you some plan to update for 2011 :-)

    Thanks
    Are you sure you want to
    Your message goes here
    Processing…
  • http://www.slideshare.net/ranguard/dbixclass-introduction-2010 for latest version of these slides
    Are you sure you want to
    Your message goes here
    Processing…
  • I've updated the slides http://www.slideshare.net/ranguard/dbixclass-introduction-2010 with the above changes and also better examples of using Schema::Loader now it is easier to use.
    Are you sure you want to
    Your message goes here
    Processing…

  • There's a disconnect between first part SQL and second part DBIC.

    Authors should have names (not titles)
    correct slides/line
    35/11
    42
    45/11-left

    Books should have titles (not names)
    correct slides
    36/11
    43
    45/11-right


    Slide 53: How does '31 DEC MON' become 2008-11-29?
    Slide 54: How does that become 'Nov'
    Are you sure you want to
    Your message goes here
    Processing…
Post Comment
Edit your comment

    DBIx::Class beginners DBIx::Class beginners Presentation Transcript

    • DBIx::Class (aka DBIC) for (advanced) beginners Leo Lapworth @ LPW 2008 http://leo.cuckoo.org/projects/
    • assumptions You know a little about Perl and using objects You know a little bit about databases and using foreign keys
    • DBIx::Class? • ORM (object relational mapper) • SQL <-> OO (using objects instead of SQL) • Simple, powerful, complex, fab and confusing • There are many ORMs, DBIx::Class just happens to be the best in Perl (personal opinion)
    • why this talk? • Help avoid mistakes I made! • Help learn DBIx::Class faster • Make your coding easier
    • point of note quot;Debugging is twice as hard as writing the code in the first place. Therefore, if you write the code as cleverly as possible, you are, by definition, not smart enough to debug it.quot; - Brian W. Kernighan This talk is about making it easy so we you are less likely to get confused
    • table setup
    • example... Books Authors
    • authors table CREATE TABLE authors( id int(8) primary key auto_increment, name varchar(255) ) engine = InnoDB DEFAULT CHARSET=utf8;
    • tips Name tables as simple plurals (add an S) - makes relationships easier to understand (issue: Matt Trout quot;Tables should not be plural as gives you plurals for Result:: package names which represent a single rowquot; - talk may be rewritten in future to reflect this as this is better once you understand the relationship setup - either way, consistency is important) Use a character set (UTF8) from the start (for international characters)
    • authors table CREATE TABLE author s( id int(8) primary key auto_increment, name varchar(255) ) engine = InnoDB DEFAULT CHARSET= utf8;
    • books table CREATE TABLE books( id int(8) primary key auto_increment, title varchar(255), author int(8), foreign key (author) references authors(id) ) engine = InnoDB DEFAULT CHARSET=utf8;
    • tips Name link fields as singular Check foreign key is the same field type and size in both tables
    • books table CREATE TABLE books( id int(8) primary key auto_increment, title varchar(255), author int(8), foreign key ( author) references authors(id) ) engine = InnoDB DEFAULT CHARSET=utf8;
    • CRUD compared C - Create R - Retrieve U - Update D - Delete
    • Manual (SQL)
    • manual: create my $sth = $dbh->prepare(' INSERT INTO books (title, author) values (?,?) '); $sth->execute( 'A book title',$author_id );
    • manual: create my $sth = $dbh->prepare(' INSERT INTO books (title, author) values (?,?) '); $sth->execute( 'A book title', $author_id );
    • manual: retrieve my $sth = $dbh->prepare(' SELECT title, authors.name as author_name FROM books, authors WHERE books.author = authors.id ');
    • manual: retrieve while( my $book = $sth->fetchrow_hashref() ) { print 'Author of ' . $book->{title} . ' is ' . $book->{author_name} . quot;nquot;; }
    • manual: update my $update = $dbh->prepare(' UPDATE books SET title = ? WHERE id = ? '); $update->execute( 'New title', $book_id);
    • manual: delete my $delete = $dbh->prepare(' DELETE FROM books WHERE id = ? '); $delete->execute( $book_id);
    • DBIx::Class
    • DBIC: create my $book = $book_model->create({ title => 'A book title', author => $author_id, }); Look ma, no SQL! Tip: do not pass in primary_key field, even if its empty/undef as the object returned will have an empty id, even if your field is auto increment.
    • DBIC: create my $book = $book_model->create({ title => 'A book title', author => $author_id, });
    • DBIC: create my $pratchett = $author_model->create({ name => 'Terry Pratchett', });
    • DBIC: create my $book = $pratchett->create_related( 'books', { title => 'Another Discworld book', }); or my $book = $pratchett->add_to_books({ title => 'Another Discworld book', });
    • DBIC: create my $book = $pratchett->create_related( 'books', { title => 'Another Discworld book', }); or my $book = $pratchett->add_to_books({ title => 'Another Discworld book', });
    • DBIC: retrieve DBIx::Class - Lots of ways to do the same thing... quot;There is more than one way to do it (TIMTOWTDI, usually pronounced quot;Tim Toadyquot;) is a Perl mottoquot;
    • DBIC: retrieve my $book = $book_model->find($book_id); my $book = $book_model->search({ title => 'A book title', })->single(); my @books = $book_model->search({ author => $author_id, })->all();
    • DBIC: retrieve while( my $book = $books_rs->next() ) { print 'Author of ' . $book->title() . ' is ' . $book->author()->name() . quot;nquot;; }
    • DBIC: retrieve my $books_rs = $book_model->search({ author => $author_id, }); Search takes SQL::Abstract formatted queries > perldoc SQL::Abstract
    • DBIC: update $book->update({ title => 'New title', });
    • DBIC: delete $book->delete();
    • Creating models
    • too much typing! too much maintenance!
    • Schema::Loader Tip LPW::DBIC::Result::XX LPW::DBIC::ResultSet::XX
    • splitting logic cleanly LPW::DBIC::Result::XXX = an individual row LPW::DBIC::ResultSet::XXX = searches / results
    • using your Schema
    • DEBUGGING DBIC_TRACE=1 ./your_script.pl
    • Schema::Loader LPW::DBIC::Result::Authors->table(quot;authorsquot;); LPW::DBIC::Result::Authors->add_columns( quot;idquot;, { data_type => quot;INTquot;, default_value => undef, is_nullable => 0, size => 8 }, quot;titlequot;, { data_type => quot;VARCHARquot;, default_value => undef, is_nullable => 1, size => 255, }, ); LPW::DBIC::Result::Authors->set_primary_key(quot;idquot;);
    • Schema::Loader LPW::DBIC::Result::Books->table(quot;booksquot;); LPW::DBIC::Result::Books->add_columns( quot;idquot;, { data_type => quot;INTquot;, default_value => undef, is_nullable => 0, size => 8 }, quot;namequot;, { data_type => quot;VARCHARquot;, default_value => undef, is_nullable => 1, size => 255, }, quot;authorquot;, { data_type => quot;INTquot;, default_value => undef, is_nullable => 1, size => 8 }, ); LPW::DBIC::Result::Books->set_primary_key(quot;idquot;);
    • Schema::Loader LPW::DBIC::Result::Authors->has_many(quot;booksquot;, quot;LPW::DBIC::Booksquot;, { quot;foreign.authorquot; => quot;self.idquot; }); LPW::DBIC::Result::Books->belongs_to(quot;authorquot;, quot;LPW::DBIC::Authorsquot;, { id => quot;authorquot; });
    • Schema::Loader
    • SQL - debugging INSERT INTO authors (name) VALUES (?): 'Douglas Adams' INSERT INTO books (author, title) VALUES (?, ?): '5', '42'
    • overloading LPW::DBIC::Result::Books LPW:: DBIC::ResultSet::Books LPW:: DBIC::Result::Authors LPW:: DBIC::ResultSet::Authors
    • Result:: package LPW::DBIC::Result::Books; use base 'DBIx::Class'; use strict; use warnings; sub isbn { my $self = shift; # search amazon or something my $api = Amazon::API->book({ title => $self->title() }); return $api->isbn(); } 1;
    • Result:: package LPW::DBIC::Result::Books; use base 'DBIx::Class'; use strict; use warnings; sub isbn { my $self = shift; # search amazon or something my $api = Amazon::API->book({ title => $self->title() }); return $api->isbn(); } 1;
    • Result:: print $book->isbn();
    • Result:: (inflating) package LPW::DBIC::Result::Books; use base 'DBIx::Class'; use strict; use warnings; use DateTime::Format::MySQL; __PACKAGE__->inflate_column( 'date_published', { inflate => sub { DateTime::Format::MySQL->parse_date(shift); }, deflate => sub { shift->ymd(); }, } ); # Automatic see: DBIx::Class::InflateColumn::DateTime
    • Result:: (inflating) package LPW::DBIC::Result::Books; use base 'DBIx::Class'; use strict; use warnings; use DateTime::Format::MySQL; __PACKAGE__->inflate_column( 'date_published', { inflate => sub { DateTime::Format::MySQL->parse_date(shift); }, deflate => sub { shift->ymd(); }, } ); # Automatic see: DBIx::Class::InflateColumn::DateTime
    • Result:: (deflating) $book->date_published(DateTime->now); $book->update(); 2008-11-29
    • Result:: (inflating) my $date_published = $book->date_published() print $date_published->month_abbr(); Nov
    • ResultSets:: package LPW::DBIC::ResultSet::Books; use base 'DBIx::Class::ResultSet'; sub the_ultimate_books { my $self = shift; return $self->search( { title => { 'like', '%42%' } }); } sub by_author { my ( $self, $author ) = @_; return $self->search( { author => $author->id(), } ); } 1;
    • ResultSets:: package LPW::DBIC::ResultSet::Books; use base 'DBIx::Class::ResultSet'; sub the_ultimate_books { my $self = shift; return $self->search( { title => { 'like', '%42%' } }); } sub by_author { my ( $self, $author ) = @_; return $self->search( { author => $author->id(), } ); }
    • ResultSets:: package LPW::DBIC::ResultSet::Books; use base 'DBIx::Class::ResultSet'; sub the_ultimate_books { my $self = shift; return $self->search( { title => { 'like', '%42%' } }); } sub by_author { my ( $self, $author ) = @_; return $self->search( { author => $author->id(), } ); }
    • ResultSets:: use LPW::DBIC; my $book_model = LPW::DBIC->resultset('Books'); my $book_rs = $book_model->the_ultimate_books(); my @books = $book_rs->all();
    • ResultSets::chaining use LPW::DBIC; my $book_model = LPW::DBIC->resultset('Books'); my $author_model = LPW::DBIC->resultset('Authors'); my $author = $author_model->search({ name => 'Douglas Adams', })->single(); my $book_rs = $book_model->the_ultimate_books() ->by_author($author); my @books = $book_rs->all();
    • ResultSets::chaining my $book_rs = $book_model ->the_ultimate_books() ->by_author($author); or my $book_rs = $book_model ->the_ultimate_books(); $book_rs = $book_rs->by_author($author); # Debug (SQL): # SELECT me.id, me.title, me.date_published, me.author # FROM books me # WHERE ( ( ( author = ? ) AND ( title LIKE ? ) ) ): '5', '%42%'
    • ResultSets::chaining my $rs = $book_model ->category('childrens') ->by_author($author) ->published_after('1812') ->first_page_contains('once upon') ->rating_greater_than(4); my @books = $rs->all();
    • overloading before new record
    • overloading before new record package LPW::DBIC::Result::Authors; use base 'DBIx::Class'; sub new { my ( $class, $attrs ) = @_; # Mess with $attrs my $new = $class->next::method($attrs); return $new; } 1;
    • relationships
    • multiple authors
    • a few relationships has_many has_many Authors Authors_and_Books Books belongs_to belongs_to many_to_many
    • a few relationships !
    • new join table CREATE TABLE author_and_books( id int(8) primary key auto_increment, book int(8), author int(8), foreign key (book) references books(id), foreign key (author) references authors(id) ) engine = InnoDB DEFAULT CHARSET=utf8; ALTER TABLE `books` DROP `author`
    • new join table CREATE TABLE author_and_books( id int(8) primary key auto_increment, book int(8), author int(8), foreign key (book) references books(id), foreign key (author) references authors(id) ) engine = InnoDB DEFAULT CHARSET=utf8;
    • has_many has_many Books Authors_and_Books belongs_to
    • has_many package LPW::DBIC::Result::Books; __PACKAGE__->has_many( quot;author_and_booksquot;, quot;LPW::DBIC::Result::AuthorAndBooksquot;, { quot;foreign.bookquot; => quot;self.idquot; }, ); # This is auto generated by Schema::Loader
    • has_many package LPW::DBIC::Result::Books; __PACKAGE__->has_many( quot;author_and_booksquot;, # Name of accessor quot;LPW::DBIC::Result::AuthorAndBooksquot;, # Related class { quot;foreign.bookquot; => quot;self.idquot; }, # Relationship (magic often works if not # specified, but avoid!) );
    • belongs_to has_many Books Authors_and_Books belongs_to
    • belongs_to package LPW::DBIC::Result::AuthorAndBooks; __PACKAGE__->belongs_to( quot;bookquot;, quot;LPW::DBIC::Result::Booksquot;, { id => quot;bookquot; } ); # This is auto generated by Schema::Loader
    • belongs_to package LPW::DBIC::Result::AuthorAndBooks; __PACKAGE__->belongs_to( quot;bookquot;, # Accessor name quot;LPW::DBIC::Result::Booksquot;, # Related class { id => quot;bookquot; } # Relationship );
    • same for Authors has_many Authors Authors_and_Books belongs_to
    • with no coding... has_many has_many Authors Authors_and_Books Books belongs_to belongs_to
    • many_to_many has_many has_many Authors Authors_and_Books Books belongs_to belongs_to many_to_many
    • many_to_many package LPW::DBIC::Result::Books; use base 'DBIx::Class'; __PACKAGE__->many_to_many( quot;authorsquot; => quot;author_and_booksquot;, 'author' ); 1; # This is NOT auto generated by Schema::Loader
    • many_to_many package LPW::DBIC::Result::Books; use base 'DBIx::Class'; __PACKAGE__->many_to_many( quot;authorsquot; # Accessor Name => quot;author_and_booksquot;, # has_many accessor_name 'author' # foreign relationship name ); 1;
    • many_to_many package LPW::DBIC::Result::Authors; use base 'DBIx::Class'; __PACKAGE__->many_to_many( quot;booksquot; # Accessor Name => quot;author_and_booksquot;, # has_many accessor_name 'book' # foreign relationship name ); 1; # This is NOT auto generated by Schema::Loader
    • using many_to_many #!/usr/bin/perl use LPW::DBIC; my $author_model = LPW::DBIC->resultset('Authors'); my $author = $author_model->search({ name => 'Douglas Adams', })->single(); $author->add_to_books({ title => 'A new book', });
    • using many_to_many my $author = $author_model->search({ name => 'Douglas Adams', })->single(); $author->add_to_books({ title => 'A new book', }); # SELECT me.id, me.name FROM authors me # WHERE ( name = ? ): 'Douglas Adams'; # INSERT INTO books (title) VALUES (?): 'A new book'; # INSERT INTO author_and_books (author, book) # VALUES (?, ?): '5', '2';
    • using many_to_many $author->add_to_books($book); $book->add_to_authors($author_1); $book->add_to_authors($author_2);
    • in 16 lines of code has_many has_many Authors Authors_and_Books Books belongs_to belongs_to many_to_many
    • errors Read them closely!
    • error messages DBIx::Class::Schema::Loader::connection (): Failed to load external class definition for 'LPW::DBIC::Result::Authors': Can't locate object method quot;many_to_manyquot; via package quot;LPW::DBIC::Result::Authorquot; at lib/LPW/DBIC/Result/Authors.pm line 9. Compilation failed in require at / Library/Perl/5.8.8/DBIx/Class/Schema/ Loader/Base.pm line 292.
    • error messages DBIx::Class::Schema::Loader::connection (): Failed to load external class definition for 'LPW::DBIC::Result::Authors': Can't locate object method quot;many_to_manyquot; via package quot;LPW::DBIC::Result::Authorquot; at lib/LPW/DBIC/Result/Authors.pm line 9. Compilation failed in require at / Library/Perl/5.8.8/DBIx/Class/Schema/ Loader/Base.pm line 292.
    • errors • Turn on debugging • Read error messages (sometimes useful!) • Check field names • Check package names • Check which database you are connected to (development/test/live?) - repeat above
    • thanks http://leo.cuckoo.org/projects/ Time for bonus slides?
    • Template Toolkit • [% author.books.count %] not working? • TT all methods are called in list context • [% author.books_rs.count %] scalar context Available for all relationships
    • Catalyst package Your::App::Model::LPW; use base qw(Catalyst::Model::DBIC::Schema); use strict; use warnings; __PACKAGE__->config( schema_class => 'LPW::DBIC', ); 1;
    • Catalyst package Your::App::Model::LPW; use base qw(Catalyst::Model::DBIC::Schema); use strict; use warnings; __PACKAGE__->config( schema_class => 'LPW::DBIC', ); 1; Keep your Scheme in a separate package to your Catalyst application
    • Catalyst sub action_name : Local { my ($self, $c) = @_; my $model = $c->model('DBIC::LPW'); my $author_model = $model->resultset('Authors'); } 1;
    • thanks! http://leo.cuckoo.org/projects/