Commit | Line | Data |
52d980cb |
1 | <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" |
2 | "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> |
3 | |
4 | <html xmlns="http://www.w3.org/1999/xhtml"> |
5 | |
6 | <head> |
7 | <title>DBIx::Class (aka DBIC)</title> |
8 | <!-- metadata --> |
9 | <meta name="contributor" content="youcan[64]netzgesta[46]de" /> |
10 | <meta name="publisher" content="s5.netzgesta.de" /> |
11 | <meta name="description" content="S5 1.3 is a very flexible and lightweight slide show system available for anyone to use (including transitions and scalable fonts and images)" /> |
12 | <meta name="keywords" content="S5, version 1.3, slide show, presentation-mode, projection-mode, powerpoint-like, scala-like, keynote-like, incremental display, scalable fonts, scalable images, transitions, notes, osf, xoxo, css, javascript, xhtml, public domain" /> |
13 | |
14 | <meta name="generator" content="S5" /> |
15 | <meta name="version" content="1" /> |
16 | <meta name="subject" content="DBIx::Class" /> |
17 | <meta name="author" content="See first slide" /> |
18 | <meta name="company" content="N/A" /> |
19 | <meta name="robots" content="index, follow" /> |
20 | <meta name="revisit-after" content="7 days" /> |
21 | <!-- meta additionally --> |
22 | <meta http-equiv="content-type" content="text/html; charset=utf-8" /> |
23 | <meta http-equiv="Content-Script-Type" content="text/javascript" /> |
24 | <meta http-equiv="Content-Style-Type" content="text/css" /><!-- configuration parameters --> |
25 | <meta name="defaultView" content="slideshow" /> |
26 | <meta name="controlVis" content="hidden" /> |
27 | <!-- configuration transition extension --> |
28 | <meta name="tranSitions" content="true" /> |
29 | <meta name="fadeDuration" content="500" /> |
30 | <meta name="incrDuration" content="250" /> |
31 | <!-- configuration autoplay extension --> |
32 | <meta name="autoMatic" content="false" /> |
33 | <meta name="playLoop" content="true" /> |
34 | <meta name="playDelay" content="10" /> |
35 | <!-- configuration audio extension --> |
36 | <meta name="audioSupport" content="false" /> |
37 | <meta name="audioVolume" content="0" /> |
38 | <meta name="audioError" content="false" /> |
39 | <!-- configuration audio debug --> |
40 | <meta name="audioDebug" content="false" /> |
41 | <!-- style sheet links --> |
42 | <link rel="stylesheet" href="ui/scala_utf/slides.css" type="text/css" media="projection" id="slideProj" /> |
43 | <link rel="stylesheet" href="ui/scala_utf/outline.css" type="text/css" media="screen" id="outlineStyle" /> |
44 | <link rel="stylesheet" href="ui/scala_utf/print.css" type="text/css" media="print" id="slidePrint" /> |
45 | <link rel="stylesheet" href="ui/scala_utf/opera.css" type="text/css" media="projection" id="operaFix" /> |
46 | <!-- embedded styles --> |
47 | <style type="text/css" media="all"> |
48 | .imgcon {width: 100%; margin: 0 auto; padding: 0; text-align: center;} |
49 | #anim {width: 33%; height: 320px; position: relative;} |
50 | #anim img {position: absolute; top: 0px; left: 0px;} |
51 | .red {color: red;} |
52 | .grey {color: gray;} |
53 | </style> |
54 | <!-- S5 JS --> |
55 | <script src="ui/scala_utf/slides.js" type="text/javascript"></script> |
56 | </head> |
57 | <body> |
58 | |
59 | <div class="layout"> |
60 | <div id="controls"><!-- DO NOT EDIT --></div> |
61 | <div id="currentSlide"><!-- DO NOT EDIT --></div> |
62 | <div id="header"></div> |
63 | <div id="footer"> |
64 | <h1>DBIx::Class Introduction</h1> |
65 | <h2>YAPC::NA 2010</h2> |
66 | </div> |
67 | </div> |
68 | |
69 | <div class="presentation"> |
70 | |
71 | <div class="slide"> |
72 | <h1>DBIX::Class (aka DBIC)</h1> |
73 | <h3>for (advanced) beginners</h3> |
0cffcad7 |
74 | </div> |
75 | |
76 | <div class="slide"> |
77 | <h1>Authors</h1> |
78 | <h4>Originally Leo Lapworth @ LPW 2009</h4> |
79 | <h4>Matthew S. Trout</h4> |
0cffcad7 |
80 | <h4>Justin D. Hunter</h4> |
5cac74c7 |
81 | <h4>Arthur Axel "fREW" Schmidt</h4> |
52d980cb |
82 | </div> |
83 | |
84 | <div class="slide"> |
85 | <h1>assumptions</h1> |
86 | <p>You know a little about Perl and using objects</p> |
87 | <p>You know a little bit about databases and using foreign keys</p> |
0cffcad7 |
88 | <div class="notes"> |
89 | <ul> |
90 | <li>How many people have designed a database with Foreign Keys?</li> |
91 | <li>How many people have used any ORM?<ul> |
92 | <li>In Perl?<ul> |
93 | <li>DBIC?</li> |
94 | <li> Class::DBI? </li> |
95 | <li> Rose::DB? </li> |
96 | <li> Fey? </li> |
97 | <li> Others? </li> |
98 | </ul></li> |
99 | <li>AR? </li> |
100 | <li> DataMapper? </li> |
101 | <li>(N)Hibernate?</li> |
102 | </ul></li> |
103 | </ul> |
104 | </div> |
52d980cb |
105 | </div> |
106 | |
107 | <div class="slide"> |
108 | <h1>DBIx::Class?</h1> |
109 | <ul> |
110 | <li>ORM (object relational mapper)</li> |
111 | <li>SQL <-> OO (using objects instead of SQL)</li> |
112 | <li>Simple, powerful, complex, fab and confusing</li> |
113 | <li>There are many ORMs, DBIx::Class just happens to be the best in Perl (personal opinion)</li> |
114 | </ul> |
115 | </div> |
116 | |
117 | <div class="slide"> |
52d980cb |
118 | <h1>point of note</h1> |
5cac74c7 |
119 | <p><em>"Debugging is twice as hard as writing the code in the first |
120 | place. Therefore, if you write the code as cleverly as possible, |
121 | you are, by definition, not smart enough to debug it." - Brian |
122 | W. Kernighan</em></p> |
52d980cb |
123 | |
5cac74c7 |
124 | <p>This talk is about making it easy so we are less likely to get |
125 | confused</p> |
52d980cb |
126 | </div> |
127 | |
128 | <div class="slide"> |
5cac74c7 |
129 | <h1>Examples Table Setup</h1> |
52d980cb |
130 | <ul> |
131 | <li>Authors</li> |
132 | <li>Books</li> |
133 | </ul> |
5cac74c7 |
134 | <em>MySQL not recommended</em> |
52d980cb |
135 | </div> |
136 | |
137 | <div class="slide"> |
138 | <h1>authors table</h1> |
139 | <pre>CREATE TABLE authors( |
140 | id int(8) primary key auto_increment, |
141 | name varchar(255) |
142 | ) engine = InnoDB DEFAULT CHARSET=utf8;</pre> |
143 | </div> |
144 | |
145 | <div class="slide"> |
146 | <h1>tips</h1> |
147 | <p>Name tables as simple plurals (<strong>add an S</strong>) - makes relationships easier to understand</p> |
52d980cb |
148 | <p>Use a character set (<strong>UTF8</strong>) from the start (for international characters)</p> |
149 | </div> |
150 | |
151 | <div class="slide"> |
152 | <h1>authors table</h1> |
153 | <pre>CREATE TABLE author<strong>s</strong>( |
154 | id int(8) primary key auto_increment, |
155 | name varchar(255) |
156 | ) engine = <strong>InnoDB</strong> DEFAULT CHARSET=<strong>utf8</strong>;</pre> |
157 | </div> |
158 | |
159 | <div class="slide"> |
160 | <h1>books table</h1> |
161 | <pre>CREATE TABLE books( |
5cac74c7 |
162 | id int(8) primary key auto_increment, |
163 | title varchar(255), |
164 | author_id int(8),foreign key (author) |
52d980cb |
165 | references authors(id) |
166 | ) engine = InnoDB DEFAULT CHARSET=utf8;</pre> |
167 | </div> |
168 | |
169 | |
170 | <div class="slide"> |
171 | <h1>tips</h1> |
172 | <p>Name link fields as singular</p> |
5cac74c7 |
173 | <p>Ensure foreign key is the same type and size in both tables</p> |
52d980cb |
174 | </div> |
175 | |
176 | <div class="slide"> |
177 | <h1>books table</h1> |
178 | <pre>CREATE TABLE books( |
5cac74c7 |
179 | id int(8) primary key auto_increment, |
180 | title varchar(255), |
181 | author_id <strong>int(8)</strong>,<strong>foreign key (<em>author</em>)</strong> |
52d980cb |
182 | <strong>references <em>authors(id)</em></strong> |
183 | ) engine = InnoDB DEFAULT CHARSET=utf8;</pre> |
184 | </div> |
185 | |
186 | <div class="slide"> |
187 | <h1>CRUD compared</h1> |
188 | <ul> |
189 | <li><strong>C</strong> - Create</li> |
190 | <li><strong>R</strong> - Read</li> |
191 | <li><strong>U</strong> - Update</li> |
192 | <li><strong>D</strong> - Delete</li> |
193 | </ul> |
194 | </div> |
195 | |
196 | <div class="slide"> |
197 | <h1>Manual (SQL)</h1> |
198 | </div> |
199 | |
200 | <div class="slide"> |
5cac74c7 |
201 | <h1>SQL: Create</h1> |
52d980cb |
202 | <pre>my $sth = $dbh->prepare(' |
203 | INSERT INTO books |
204 | (title, author) |
205 | values (?,?) |
206 | '); |
207 | |
208 | $sth->execute( |
209 | 'A book title', $author_id |
210 | );</pre> |
211 | </div> |
212 | |
213 | <div class="slide"> |
5cac74c7 |
214 | <h1>SQL: Read</h1> |
52d980cb |
215 | <pre>my $sth = $dbh->prepare(' |
216 | SELECT title, |
217 | authors.name as author_name |
218 | FROM books, authors |
219 | WHERE books.author = authors.id |
220 | ');</pre> |
221 | </div> |
222 | |
223 | <div class="slide"> |
5cac74c7 |
224 | <h1>SQL: Read</h1> |
52d980cb |
225 | <pre>while( my $book = $sth->fetchrow_hashref() ) { |
226 | print 'Author of ' |
227 | . $book->{title} |
228 | . ' is ' |
229 | . $book->{author_name} |
230 | . "\n"; |
231 | }</pre> |
232 | </div> |
233 | |
234 | <div class="slide"> |
5cac74c7 |
235 | <h1>SQL: Update</h1> |
52d980cb |
236 | <pre>my $update = $dbh->prepare(' |
237 | UPDATE books |
238 | SET title = ? |
239 | WHERE id = ? |
240 | '); |
241 | |
242 | $update->execute( |
243 | 'New title',<strong>$book_id</strong> |
244 | );</pre> |
245 | </div> |
246 | |
247 | <div class="slide"> |
5cac74c7 |
248 | <h1>SQL: Delete</h1> |
52d980cb |
249 | <pre>my $delete = $dbh->prepare(' |
250 | DELETE FROM books |
251 | WHERE id = ? |
252 | '); |
253 | |
254 | $delete->execute(<strong>$book_id</strong>);</pre> |
255 | </div> |
256 | |
257 | <div class="slide"> |
258 | <h1>DBIx::Class</h1> |
259 | </div> |
260 | |
261 | <div class="slide"> |
5cac74c7 |
262 | <h1>DBIC: Create</h1> |
52d980cb |
263 | <pre>my $book = $book_model->create({ |
264 | title => 'A book title', |
265 | author => $author_id, |
266 | });</pre> |
267 | <p>Look ma, no SQL!</p> |
52d980cb |
268 | </div> |
269 | |
270 | <div class="slide"> |
5cac74c7 |
271 | <h1>DBIC: Create</h1> |
52d980cb |
272 | <pre>my $pratchett = $author_model->create({ |
273 | name => 'Terry Pratchett', |
274 | });</pre> |
275 | </div> |
276 | |
277 | <div class="slide"> |
5cac74c7 |
278 | <h1>DBIC: Create</h1> |
52d980cb |
279 | <pre>my $book = $pratchett->create_related( |
280 | <strong>books</strong> => { |
281 | title => 'Another Discworld book', |
282 | });</pre> |
283 | <strong>or</strong> |
284 | <pre>my $book = $pratchett->add_to_<strong>books</strong>({ |
285 | title => 'Another Discworld book', |
286 | });</pre> |
287 | </div> |
288 | |
289 | <div class="slide"> |
5cac74c7 |
290 | <h1>DBIC: Read</h1> |
52d980cb |
291 | <p>DBIx::Class - Lots of ways to do the same thing...</p> |
292 | <p><em>"There is more than one way to do it (TIMTOWTDI, usually pronounced "Tim Toady") is a Perl motto"</em></p> |
293 | </div> |
294 | |
295 | <div class="slide"> |
5cac74c7 |
296 | <h1>DBIC: Read</h1> |
52d980cb |
297 | <pre>my $book = $book_model->find($book_id); |
298 | |
299 | my $book = $book_model->search({ |
300 | title => 'A book title', |
5cac74c7 |
301 | }, { rows => 1 })->single; |
52d980cb |
302 | |
303 | my @books = $book_model->search({ |
304 | author => $author_id, |
305 | })->all;</pre> |
306 | </div> |
307 | |
308 | <div class="slide"> |
5cac74c7 |
309 | <h1>DBIC: Read</h1> |
310 | <pre>while( my $book = $books_rs->next ) { |
52d980cb |
311 | print 'Author of ' |
312 | . $book->title |
313 | . ' is ' |
314 | . $book->author->name |
315 | . "\n"; |
316 | }</pre> |
317 | </div> |
318 | |
319 | <div class="slide"> |
5cac74c7 |
320 | <h1>DBIC: Read</h1> |
321 | <pre>my $books_rs = $book_rs->search({ |
52d980cb |
322 | author => $author_id, |
323 | });</pre> |
324 | <p>Search takes SQL::Abstract formatted queries</p> |
325 | <pre>> perldoc SQL::Abstract</p> |
326 | </div> |
327 | |
328 | <div class="slide"> |
5cac74c7 |
329 | <h1>DBIC: Update</h1> |
52d980cb |
330 | <pre>$book->update({ |
331 | title => 'New title', |
332 | });</pre> |
333 | </div> |
334 | |
335 | <div class="slide"> |
5cac74c7 |
336 | <h1>DBIC: Delete</h1> |
52d980cb |
337 | <pre>$book->delete;</pre> |
338 | </div> |
339 | |
340 | <div class="slide"> |
341 | <h1>Creating models</h1> |
342 | </div> |
343 | |
344 | <div class="slide"> |
492be2ae |
345 | <pre>package MyApp::Schema::Result::Author; |
346 | use strict; use warnings; |
347 | __PACKAGE__->table('authors'); |
348 | __PACKAGE__->add_columns( |
349 | id => { |
350 | data_type => 'int', |
351 | size => 8, |
352 | }, |
353 | title => { |
354 | data_type => 'varchar', |
355 | is_nullable => 1, |
356 | size => 255, |
357 | }, |
358 | ); |
359 | __PACKAGE__->set_primary_key('id'); |
360 | __PACKAGE__->has_many( books => |
361 | 'MyApp::Schema::Result::Book', 'author_id' |
362 | ); |
363 | 1; |
364 | </pre> |
365 | </div> |
366 | |
367 | <div class="slide"> |
368 | <pre>package MyApp::Schema::Result::Book; |
369 | use strict; use warnings; |
370 | __PACKAGE__->table('books'); |
371 | __PACKAGE__->add_columns( |
372 | id => { |
373 | data_type => 'int', |
374 | size => 8, |
375 | }, |
376 | name => { |
377 | data_type => 'varchar', |
378 | is_nullable => 1, |
379 | size => 255, |
380 | }, |
381 | author_id => { |
382 | data_type => 'int', |
383 | size => 8, |
384 | is_nullable => 1, # <-- probably should be 0 |
385 | }, |
386 | ); |
387 | __PACKAGE__->set_primary_key('id'); |
388 | __PACKAGE__->belongs_to( author => |
389 | 'MyApp::Schema::Result::Author', 'author_id' |
390 | ); |
391 | 1; |
392 | </pre> |
52d980cb |
393 | </div> |
394 | |
395 | <div class="slide"> |
52d980cb |
396 | <h1>Schema::Loader</h1> |
c7321880 |
397 | <p>DB -> Perl vs Perl -> DB</p> |
398 | <pre>package MyApp::Schema; |
399 | use strict; use warnings; |
400 | use base 'DBIx::Class::Schema::Loader'; |
401 | __PACKAGE__->loader_options({ |
402 | naming => 'v7', |
403 | debug => $ENV{DBIC_TRACE}, |
404 | }); |
405 | 1; |
406 | |
407 | # elsewhere... |
408 | |
409 | my $schema = MyApp::Schema->connect($dsn, $user, $pass); |
410 | </pre> |
52d980cb |
411 | </div> |
412 | |
413 | <div class="slide"> |
414 | <h1>splitting logic cleanly</h1> |
41a0eb8e |
415 | <p>Foo::Schema::Result::Foo = an individual row</p> |
416 | <p>Foo::Schema::ResultSet::Foo = searches / results</p> |
52d980cb |
417 | </div> |
418 | |
419 | <div class="slide"> |
420 | <h1>using your Schema</h1> |
421 | <pre>example usage code goes here</pre> |
422 | </div> |
423 | |
424 | <div class="slide"> |
425 | <h1>DEBUGGING</h1> |
426 | <pre>DBIC_TRACE=1 ./your_script.pl</pre> |
427 | </div> |
428 | |
429 | <div class="slide"> |
430 | <h1>Schema::Loader</h1> |
41a0eb8e |
431 | <pre>Foo::Schema::Result::Authors->table("authors"); |
432 | Foo::Schema::Result::Authors->add_columns( |
52d980cb |
433 | id => { |
434 | data_type => "INT", |
435 | default_value => undef, |
436 | is_nullable => 0, |
437 | size => 8 |
438 | }, |
439 | title => { |
440 | data_type => "VARCHAR", |
441 | default_value => undef, |
442 | is_nullable => 1, |
443 | size => 255, |
444 | }, |
445 | ); |
41a0eb8e |
446 | Foo::Schema::Result::Authors->set_primary_key("id");</pre> |
52d980cb |
447 | </div> |
448 | |
449 | <div class="slide"> |
450 | <h1>Schema::Loader</h1> |
41a0eb8e |
451 | <pre>Foo::Schema::Result::Books->table("books"); |
452 | Foo::Schema::Result::Books->add_columns( |
52d980cb |
453 | id => { |
454 | data_type => "INT", |
455 | default_value => undef, |
456 | is_nullable => 0, |
457 | size => 8 |
458 | }, |
459 | name => { |
460 | data_type => "VARCHAR", |
461 | default_value => undef, |
462 | is_nullable => 1, |
463 | size => 255, |
464 | }, |
465 | author => { |
466 | data_type => "INT", |
467 | default_value => undef, |
468 | is_nullable => 1, |
469 | size => 8 |
470 | }, |
471 | ); |
41a0eb8e |
472 | Foo::Schema::Result::Books->set_primary_key("id");</pre> |
52d980cb |
473 | </div> |
474 | |
475 | <div class="slide"> |
476 | <h1>Schema::Loader</h1> |
41a0eb8e |
477 | <pre>Foo::Schema::Result::Authors->has_many(books => "Foo::Schema::Books", |
52d980cb |
478 | { "foreign.author" => "self.id" }); |
479 | |
41a0eb8e |
480 | Foo::Schema::Result::Books->belongs_to(author => "Foo::Schema::Authors", |
52d980cb |
481 | { id => "author" });</pre> |
482 | </div> |
483 | |
484 | <div class="slide"> |
485 | <h1>SQL - debugging</h1> |
486 | <pre>INSERT INTO authors (name) |
487 | VALUES (?): 'Douglas Adams' |
488 | |
489 | INSERT INTO books (author, title) |
490 | VALUES (?, ?): '5', '42'</pre> |
491 | </div> |
492 | |
493 | <div class="slide"> |
494 | <h1>overloading</h1> |
41a0eb8e |
495 | <pre>Foo::Schema::Result::Books |
496 | Foo::Schema::ResultSet::Books |
497 | Foo::Schema::Result::Authors |
498 | Foo::Schema::ResultSet::Books</pre> |
52d980cb |
499 | </div> |
500 | |
501 | <div class="slide"> |
502 | <h1>Result::</h1> |
41a0eb8e |
503 | <pre>package Foo::Schema::Result::Books; |
52d980cb |
504 | use base 'DBIx::Class'; |
505 | use strict; |
506 | use warnings; |
507 | |
508 | sub isbn { |
509 | my $self = shift; |
510 | |
511 | # search amazon or something |
512 | my $api = Amazon::API->book({ |
513 | title => $self->title |
514 | }); |
515 | |
516 | return $api->isbn; |
517 | } |
518 | |
519 | 1;</pre> |
520 | </div> |
521 | |
522 | <div class="slide"> |
523 | <h1>Result::</h1> |
524 | <pre>print $book->isbn;</pre> |
525 | </div> |
526 | |
527 | <div class="slide"> |
528 | <h1>Result:: (inflating)</h1> |
41a0eb8e |
529 | <pre>package Foo::Schema::Result::Books; |
52d980cb |
530 | use base 'DBIx::Class'; |
531 | use strict; |
532 | use warnings; |
533 | |
534 | use DateTime::Format::MySQL; |
535 | |
536 | __PACKAGE__-><strong>inflate_column</strong>( |
537 | <strong>date_published</strong> => { |
538 | inflate => sub { DateTime::Format::MySQL->parse_date(shift) }, |
539 | deflate => sub { shift->ymd}, |
540 | } |
541 | ); |
542 | # Automatic see: DBIx::Class::InflateColumn::DateTime</pre> |
543 | </div> |
544 | |
545 | <div class="slide"> |
546 | <h1>Result:: (inflating)</h1> |
41a0eb8e |
547 | <pre>package Foo::Schema::Result::Books; |
52d980cb |
548 | use base 'DBIx::Class'; |
549 | use strict; |
550 | use warnings; |
551 | |
552 | use DateTime::Format::MySQL; |
553 | |
554 | __PACKAGE__->inflate_column( |
555 | date_published => { |
556 | <strong>inflate => sub { DateTime::Format::MySQL->parse_date(shift) }, |
557 | deflate => sub { shift->ymd},</strong> |
558 | } |
559 | ); |
560 | # Automatic see: DBIx::Class::InflateColumn::DateTime |
561 | # Automatic see: DBIx::Class::InflateColumn::DateTime |
562 | # Automatic see: DBIx::Class::InflateColumn::DateTime</pre> |
563 | </div> |
564 | |
565 | <div class="slide"> |
566 | <h1>Result:: (deflating)</h1> |
567 | <pre>$book->date_published(DateTime->now); |
568 | $book->update;</pre> |
569 | </div> |
570 | |
571 | <div class="slide"> |
572 | <h1>Result:: (inflating)</h1> |
573 | <pre>my $date_published = $book->date_published; |
574 | print $date_published->month_abbr;</pre> |
575 | |
576 | <strong><em>Nov</em></strong> |
577 | </div> |
578 | |
579 | <div class="slide"> |
580 | <h1>ResultSets::</h1> |
41a0eb8e |
581 | <pre>package Foo::Schema::ResultSet::Books; |
52d980cb |
582 | use base 'DBIx::Class::ResultSet'; |
583 | sub the_ultimate_books { |
584 | my $self = shift; |
585 | return $self->search({ title => { -like => '%42%' } }); |
586 | } |
587 | sub by_author { |
588 | my ( $self, $author ) = @_; |
589 | return $self->search({ author => $author->id }) |
590 | } |
591 | |
592 | 1;</pre> |
593 | </div> |
594 | |
595 | <div class="slide"> |
596 | <h1>ResultSets::</h1> |
41a0eb8e |
597 | <pre>package Foo::Schema::<strong>ResultSet::Books</strong>; |
52d980cb |
598 | use base '<strong>DBIx::Class::ResultSet</strong>'; |
599 | sub the_ultimate_books { |
600 | my $self = shift; |
601 | <strong>return $self->search({ title => { -like => '%42%' } })</strong> |
602 | } |
603 | sub by_author { |
604 | my ( $self, $author ) = @_; |
605 | return $self->search({ author => $author->id }) |
606 | } |
607 | |
608 | 1;</pre> |
609 | </div> |
610 | |
611 | <div class="slide"> |
612 | <h1>ResultSets::</h1> |
41a0eb8e |
613 | <pre>package Foo::Schema::ResultSet::Books; |
52d980cb |
614 | use base 'DBIx::Class::ResultSet'; |
615 | sub the_ultimate_books { |
616 | my $self = shift; |
617 | return $self->search({ title => { -like => '%42%' } }); |
618 | } |
619 | sub by_author { |
620 | my ( $self, $author ) = @_; |
621 | <strong>return $self->search({ author => $author->id })</strong> |
622 | } |
623 | |
624 | 1;</pre> |
625 | </div> |
626 | |
627 | <div class="slide"> |
628 | <h1>ResultSets::</h1> |
41a0eb8e |
629 | <pre>use Foo::Schema; |
630 | my $book_model = Foo::Schema->resultset('Books'); |
52d980cb |
631 | my $book_rs = $book_model->the_ultimate_books; |
632 | my @books = $book_rs->all;</pre> |
633 | </div> |
634 | |
635 | <div class="slide"> |
636 | <h1>ResultSets::chaining</h1> |
41a0eb8e |
637 | <pre>use Foo::Schema; |
638 | my $book_model = Foo::Schema->resultset('Books'); |
639 | my $author_model = Foo::Schema->resultset('Authors'); |
52d980cb |
640 | my $author = $author_model->search({ name => 'Douglas Adams' })->single; |
641 | my $book_rs = $book_model->the_ultimate_books->by_author($author); |
642 | my @books = $book_rs->all;</pre> |
643 | </div> |
644 | |
645 | <div class="slide"> |
646 | <h1>ResultSets::chaining</h1> |
647 | <pre>my $book_rs = $book_model |
648 | ->the_ultimate_books |
649 | ->by_author($author);</pre> |
650 | or |
651 | |
652 | <pre>my $book_rs = $book_model |
653 | ->the_ultimate_books(); |
654 | $book_rs = $book_rs->by_author($author);</pre> |
655 | <pre># Debug (SQL): |
656 | |
657 | # SELECT me.id, me.title, me.date_published, me.author |
658 | # FROM books me |
659 | # WHERE ( ( ( author = ? ) AND ( title LIKE ? ) ) ): '5', '%42%' |
660 | # WHERE ( ( ( author = ? ) AND ( title LIKE ? ) ) ): '5', '%42%'</pre> |
661 | </div> |
662 | |
663 | <div class="slide"> |
664 | <h1>ResultSets::chaining</h1> |
665 | <pre>my $rs = $book_model |
666 | ->category('childrens') |
667 | ->by_author($author) |
668 | ->published_after('1812') |
669 | ->first_page_contains('once upon') |
670 | ->rating_greater_than(4); |
671 | |
672 | my @books = $rs->all;</pre> |
673 | </div> |
674 | |
675 | <div class="slide"> |
676 | <h1>overloading before new record</h1> |
677 | </div> |
678 | |
679 | <div class="slide"> |
680 | <h1>overloading before new record</h1> |
41a0eb8e |
681 | <pre>package Foo::Schema::Result::Authors; |
52d980cb |
682 | use base 'DBIx::Class'; |
683 | |
684 | sub new { |
685 | my ( $class, $attrs ) = @_; |
686 | # Mess with $attrs |
687 | my $new = $class->next::method($attrs); |
688 | return $new |
689 | } |
690 | |
691 | 1;</pre> |
692 | |
693 | <div class="slide"> |
694 | <h1>relationships</h1> |
695 | </div> |
696 | |
697 | <div class="slide"> |
698 | <h1>multiple authors</h1> |
699 | </div> |
700 | |
701 | <div class="slide"> |
702 | <h1>a few relationships</h1> |
703 | (authors -- author_link_to_book -- books) |
704 | </div> |
705 | |
706 | <div class="slide"> |
707 | <h1>a few relationships</h1> |
708 | ! |
709 | </div> |
710 | |
711 | <div class="slide"> |
712 | <h1>new join table</h1> |
713 | <pre>CREATE TABLE author_and_books( |
714 | id int(8) primary key auto_increment, |
715 | book int(8), |
716 | author int(8), |
717 | foreign key (book) references books(id), |
718 | foreign key (author) references authors(id) |
719 | ) engine = InnoDB DEFAULT CHARSET=utf8; |
720 | |
721 | ALTER TABLE `books` DROP `author`</pre> |
722 | </div> |
723 | |
724 | <div class="slide"> |
725 | <h1>new join table</h1> |
726 | <pre>CREATE TABLE author_and_books( |
727 | id int(8) primary key auto_increment, |
728 | book int(8), |
729 | author int(8), |
730 | <strong>foreign key (book) references books(id), |
731 | foreign key (author) references authors(id)</strong> |
732 | ) engine = InnoDB DEFAULT CHARSET=utf8; |
733 | |
734 | ALTER TABLE `books` DROP `author`</pre> |
735 | </div> |
736 | |
737 | <div class="slide"> |
738 | <h1>has_many</h1> |
739 | </div> |
740 | |
741 | <div class="slide"> |
742 | <h1>has_many</h1> |
41a0eb8e |
743 | <pre>package Foo::Schema::<strong>Result::Books</strong>; |
52d980cb |
744 | |
41a0eb8e |
745 | __PACKAGE__->has_many( author_and_books => "Foo::Schema::Result::AuthorAndBooks", |
52d980cb |
746 | { "foreign.book" => "self.id" }, |
747 | ); |
748 | |
749 | <strong># This is auto generated by Schema::Loader</strong></pre> |
750 | </div> |
751 | |
752 | <div class="slide"> |
753 | <h1>has_many</h1> |
41a0eb8e |
754 | <pre>package Foo::Schema::<strong>Result::Books</strong>; |
52d980cb |
755 | |
756 | __PACKAGE__->has_many( |
757 | author_and_books => <strong># name of accessor</strong> |
41a0eb8e |
758 | "Foo::Schema::Result::AuthorAndBooks", <strong># related class</strong> |
52d980cb |
759 | { "foreign.book" => "self.id" } <strong># Relationship (magic often works if not |
760 | # specified, but avoid!)</strong> |
761 | ); |
762 | </pre> |
763 | </div> |
764 | |
765 | <div class="slide"> |
766 | <h1>belongs_to</h1> |
767 | </div> |
768 | |
769 | <div class="slide"> |
770 | <h1>belongs_to</h1> |
41a0eb8e |
771 | <pre>package Foo::Schema::<strong>Result::AuthorAndBooks</strong>; |
52d980cb |
772 | |
773 | __PACKAGE__->belongs_to( |
774 | book => <strong># Accessor name</strong> |
41a0eb8e |
775 | "Foo::Schema::Result::Books", <strong># Related class</strong> |
52d980cb |
776 | { id => "book" } <strong># relationship</strong> |
777 | ); |
778 | </pre> |
779 | </div> |
780 | |
781 | <div class="slide"> |
782 | <h1>same for Authors</h1> |
783 | </div> |
784 | |
785 | <div class="slide"> |
786 | <h1>with no coding...</h1> |
787 | </div> |
788 | |
789 | <div class="slide"> |
790 | <h1>many_to_many</h1> |
791 | </div> |
792 | |
793 | <div class="slide"> |
794 | <h1>many_to_many</h1> |
41a0eb8e |
795 | <pre>package Foo::Schema::<strong>Result::Books</strong>; |
52d980cb |
796 | use base 'DBIx::Class'; |
797 | |
798 | __PACKAGE__->many_to_many( |
799 | authors => "author_and_books", 'author' |
800 | ); |
801 | |
802 | 1; |
803 | |
804 | <strong> # This is <em>NOT</em> auto generated by Schema::Loader </strong></pre> |
805 | </div> |
806 | |
807 | <div class="slide"> |
808 | <h1>many_to_many</h1> |
41a0eb8e |
809 | <pre>package Foo::Schema::<strong>Result::Books</strong>; |
52d980cb |
810 | use base 'DBIx::Class'; |
811 | |
812 | __PACKAGE__->many_to_many( |
813 | authors <strong># Accessor name</strong> |
814 | => "author_and_books", <strong># has_many</strong> |
815 | 'author' <strong># foreign relationship name</strong> |
816 | ); |
817 | |
818 | 1;</pre> |
819 | </div> |
820 | |
821 | <div class="slide"> |
822 | <h1>many_to_many</h1> |
41a0eb8e |
823 | <pre>package Foo::Schema::Result::Authors; |
52d980cb |
824 | use base 'DBIx::Class'; |
825 | |
826 | __PACKAGE__->many_to_many( |
827 | "books" <strong># Accessor Name</strong> |
828 | => "author_and_books", <strong># has_many accessor_name</strong> |
829 | 'book' <strong># foreign relationship name</strong> |
830 | ); |
831 | |
832 | 1; |
833 | |
834 | <strong># This is <em>NOT</em> auto generated by Schema::Loader</strong></pre> |
835 | </div> |
836 | |
837 | <div class="slide"> |
838 | <h1>using many_to_many</h1> |
839 | <pre>#!/usr/bin/perl |
840 | |
41a0eb8e |
841 | use Foo::Schema; |
52d980cb |
842 | |
41a0eb8e |
843 | my $author_model = Foo::Schema->resultset('Authors'); |
52d980cb |
844 | my $author = $author_model->search({ |
845 | name => 'Douglas Adams', |
846 | })->single; |
847 | $author->add_to_books({ |
848 | title => 'A new book', |
849 | });</pre> |
850 | </div> |
851 | |
852 | <div class="slide"> |
853 | <h1>using many_to_many</h1> |
854 | <pre>my $author = $author_model->search({ |
855 | name => 'Douglas Adams', |
856 | })->single; |
857 | <strong>$author->add_to_books({ |
858 | title => 'A new book', |
859 | });</strong> |
860 | |
861 | # SELECT me.id, me.name FROM authors me |
862 | # WHERE ( name = ? ): 'Douglas Adams'; |
863 | # INSERT INTO books (title) VALUES (?): 'A new book'; |
864 | # INSERT INTO author_and_books (author, book) |
865 | # VALUES (?, ?): '5', '2';</pre> |
866 | </div> |
867 | |
868 | <div class="slide"> |
869 | <h1>using many_to_many</h1> |
870 | <pre>$author->add_to_books($book); |
871 | |
872 | $book->add_to_authors($author_1); |
873 | $book->add_to_authors($author_2);</pre> |
874 | </div> |
875 | |
876 | <div class="slide"> |
877 | <h1>in 16 lines of code</h1> |
878 | </div> |
879 | |
880 | <div class="slide"> |
881 | <h1>errors</h1> |
882 | <p>Read them closely!</p> |
883 | </div> |
884 | |
885 | <div class="slide"> |
886 | <h1>error messages</h1> |
41a0eb8e |
887 | <pre>DBIx::Class::Schema::Loader::connection(): Failed to load external class definition for 'Foo::Schema::Result::Authors': Can't locate object method "many_to_many" via package "Foo::Schema::Result::Author" at lib/Foo/Schema/Result/Authors.pm line 9.Compilation failed in require at /Library/Perl/5.8.8/DBIx/Class/Schema/Loader/Base.pm line 292.</pre> |
52d980cb |
888 | </div> |
889 | |
890 | <div class="slide"> |
891 | <h1>error messages</h1> |
41a0eb8e |
892 | <pre>DBIx::Class::Schema::Loader::connection(): Failed to load external class definition for 'Foo::Schema::Result::Authors': Can't locate object method "many_to_many" via package "Foo::Schema::<strong>Result::Author</strong>" at lib/Foo/Schema/<strong>Result/Authors.pm</strong> line 9.Compilation failed in require at /Library/Perl/5.8.8/DBIx/Class/Schema/Loader/Base.pm line 292.</pre> |
52d980cb |
893 | </div> |
894 | |
895 | <div class="slide"> |
896 | <h1>errors</h1> |
897 | <ul> |
898 | <li>Turn on debugging</li> |
899 | <li>Read error messages (sometimes useful!)</li> |
900 | <li>Check field names</li> |
901 | <li>Check package names</li> |
902 | <li>Check which database you are connected to (dev/test/live?) - repeat above</li> |
903 | </ul> |
904 | </div> |
905 | |
906 | <div class="slide"> |
907 | <h1>bonus slides!</h1> |
908 | </div> |
909 | |
910 | <div class="slide"> |
911 | <h1>Template Toolkit</h1> |
912 | <ul> |
913 | <li><pre>[% author.books.count %]</pre> not working?</li> |
914 | <li>TT all methods are called in list context</li> |
915 | <li><pre>[% author.books<strong>_rs</strong>.count %]</pre> scalar context</li> |
916 | <li><em>Available for all relationships</em></li> |
917 | </ul> |
918 | </div> |
919 | |
920 | <div class="slide"> |
921 | <h1>Catalyst</h1> |
0cffcad7 |
922 | <pre>package Your::App::Model::<strong>Foo</strong>; |
41a0eb8e |
923 | use base qw(<strong>Catalyst::Model::Schema::Schema</strong>); |
52d980cb |
924 | |
925 | use strict; |
926 | use warnings; |
927 | |
928 | __PACKAGE__->config( |
41a0eb8e |
929 | schema_class => '<strong>Foo::Schema</strong>', |
52d980cb |
930 | ); |
931 | |
932 | 1;</pre> |
933 | <p>Keep your Schema in a <em>separate</em> package to your Catalyst application</p> |
934 | </div> |
935 | |
936 | <div class="slide"> |
937 | <h1>Catalyst</h1> |
938 | <pre>sub action_name : Local { |
939 | my ($self, $c) = @_; |
940 | |
41a0eb8e |
941 | my $model = $c->model('Schema::Foo'); |
52d980cb |
942 | my $author_model = $model->resultset('Authors'); |
943 | |
944 | } |
945 | |
946 | 1;</pre> |
947 | </div> |
948 | |
949 | </div> |
950 | </body> |
951 | </html> |