Beginning of docs. Making headway. Next is subroutine docs
[dbsrgits/DBIx-Class-ResultSource-MultipleTableInheritance.git] / README.html
1 <?xml version="1.0" ?>
2 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
3 <html xmlns="http://www.w3.org/1999/xhtml">
4 <head>
5 <title>DBIx::Class::ResultSource::MultipleTableInheritance -- Use multiple tables to define your classes</title>
6 <meta http-equiv="content-type" content="text/html; charset=utf-8" />
7 <link rev="made" href="mailto:amiri@akbuntu.nonet" />
8 </head>
9
10 <body style="background-color: white">
11
12
13 <!-- INDEX BEGIN -->
14 <div name="index">
15 <p><a name="__index__"></a></p>
16
17 <ul>
18
19         <li><a href="#name">NAME</a></li>
20         <li><a href="#synopsis">SYNOPSIS</a></li>
21         <li><a href="#why">WHY?</a></li>
22         <li><a href="#how">HOW?</a></li>
23         <li><a href="#author">AUTHOR</a></li>
24         <ul>
25
26                 <li><a href="#contributors">CONTRIBUTORS</a></li>
27         </ul>
28
29         <li><a href="#license">LICENSE</a></li>
30         <li><a href="#see_also">SEE ALSO</a></li>
31 </ul>
32
33 <hr name="index" />
34 </div>
35 <!-- INDEX END -->
36
37 <p>
38 </p>
39 <hr />
40 <h1><a name="name">NAME</a></h1>
41 <p>DBIx::Class::ResultSource::MultipleTableInheritance -- Use multiple tables to define your classes</p>
42 <p>
43 </p>
44 <hr />
45 <h1><a name="synopsis">SYNOPSIS</a></h1>
46 <pre>
47     {
48         package MyApp::Schema::Result::Coffee;</pre>
49 <pre>
50         __PACKAGE__-&gt;table_class('DBIx::Class::ResultSource::MultipleTableInheritance');
51         __PACKAGE__-&gt;table('coffee');
52         __PACKAGE__-&gt;add_columns(
53           &quot;id&quot;,
54           {
55             data_type =&gt; &quot;integer&quot;,
56             default_value =&gt; &quot;nextval('coffee_seq'::regclass)&quot;,
57             is_auto_increment =&gt; 1,
58             is_foreign_key =&gt; 1,
59             is_nullable =&gt; 0,
60             size =&gt; 4,
61           },
62           &quot;flavor&quot;,
63           {
64             data_type =&gt; &quot;text&quot;,
65             default_value =&gt; &quot;good&quot;,
66           },
67         );</pre>
68 <pre>
69         __PACKAGE__-&gt;set_primary_key(&quot;id&quot;);</pre>
70 <pre>
71         1;
72     }</pre>
73 <pre>
74     {
75         package MyApp::Schema::Result::Sumatra;</pre>
76 <pre>
77         use parent 'Coffee';</pre>
78 <pre>
79         __PACKAGE__-&gt;table('sumatra');</pre>
80 <pre>
81         __PACKAGE__-&gt;add_columns(
82           &quot;aroma&quot;,
83           {
84             data_type =&gt; &quot;text&quot;,
85             default_value =&gt; undef,
86             is_nullable =&gt; 0,
87           },
88         );</pre>
89 <pre>
90         1;
91     }
92     
93     ...</pre>
94 <pre>
95     my $schema = MyApp::Schema-&gt;connect($dsn);</pre>
96 <pre>
97     my $cup = $schema-&gt;resultset('Sumatra')-&gt;new;</pre>
98 <pre>
99     print STDERR Dumper $cup-&gt;columns;</pre>
100 <pre>
101         $VAR1 = 'id';
102         $VAR2 = 'flavor';
103         $VAR3 = 'aroma';</pre>
104 <p>Inherit from this package and you can make a resultset class from a view, but that's more than a little bit misleading: the result is <strong>writable</strong> and updateable--transparently.</p>
105 <p>This is accomplished through the use of stored functions that map changes written to the view to changes to the underlying concrete tables.</p>
106 <p>
107 </p>
108 <hr />
109 <h1><a name="why">WHY?</a></h1>
110 <p>In many applications, many classes are subclasses of others. Let's say you have this schema:</p>
111 <pre>
112     # Conceptual domain model
113     
114     class User {
115             has id,
116             has name,
117             has password
118     }</pre>
119 <pre>
120     class Investor {
121         has id,
122         has name,
123         has password,
124         has dollars
125     }</pre>
126 <p>That's redundant. Hold on a sec...</p>
127 <pre>
128     class User {
129             has id,
130             has name,
131             has password
132     }</pre>
133 <pre>
134     class Investor isa User {
135         has dollars
136     }</pre>
137 <p>Good idea, but how to put this into code?</p>
138 <p>One far-too common and absolutely horrendous solution is to have a &quot;checkbox&quot; in your database: a nullable &quot;investor&quot; column, which entails a nullable &quot;dollars&quot; column, in the user table.</p>
139 <pre>
140     create table &quot;user&quot; (
141         &quot;id&quot; integer not null primary key autoincrement,
142         &quot;name&quot; text not null,
143         &quot;password&quot; text not null,
144         &quot;investor&quot; tinyint(1),
145         &quot;dollars&quot; integer
146     );</pre>
147 <p>Let's not discuss that further.</p>
148 <p>A second, better, solution is to break out the two tables into user and investor:</p>
149 <pre>
150     create table &quot;user&quot; (
151         &quot;id&quot; integer not null primary key autoincrement,
152         &quot;name&quot; text not null,
153         &quot;password&quot; text not null
154     );
155         
156     create table &quot;investor&quot; (
157         &quot;id&quot; integer not null references user(&quot;id&quot;),
158         &quot;dollars&quot; integer
159     );</pre>
160 <p>So that investor's PK is just an FK to the user. We can clearly see the class hierarchy here, in which investor is a subclass of user. In DBIx::Class applications, this second strategy looks like:</p>
161 <pre>
162
163     my $user_rs = $schema-&gt;resultset('User');
164     my $new_user = $user_rs-&gt;create(
165         name =&gt; $args-&gt;{name},
166         password =&gt; $args-&gt;{password},
167     );</pre>
168 <pre>
169     ...</pre>
170 <pre>
171     my $new_investor = $schema-&gt;resultset('Investor')-&gt;create(
172         id =&gt; $new_user-&gt;id,
173         dollars =&gt; $args-&gt;{dollars},
174     );</pre>
175 <p>One can cope well with the second strategy, and it seems to be the most popular smart choice.</p>
176 <p>
177 </p>
178 <hr />
179 <h1><a name="how">HOW?</a></h1>
180 <p>There is a third strategy implemented here. Make the database do more of the work. It'll save us some typing and it'll make for more expressive code. What if we could do this:</p>
181 <pre>
182     my $new_investor = $schema-&gt;resultset('Investor')-&gt;create(
183         name =&gt; $args-&gt;{name},
184         password =&gt; $args-&gt;{password},
185         dollars =&gt; $args-&gt;{dollars},
186     );
187     
188 And have it Just Work? The user ( {name =&gt; $args-&gt;{name}, password =&gt; $args-&gt;{password} } ) should be created transparently, and the use of either user or investor in your code should require no special handling. Deleting and updating $new_investor should also delete or update the user row.</pre>
189 <p>It does. User and investor are both views, their concrete tables abstracted away behind a set of rules and triggers. You would expect the above DBIC create statement to look like this in SQL:</p>
190 <pre>
191     INSERT INTO investor (&quot;name&quot;,&quot;password&quot;,&quot;dollars&quot;) VALUES (...);</pre>
192 <p>But using MTI, it is really this:</p>
193 <pre>
194     INSERT INTO _user_table (&quot;username&quot;,&quot;password&quot;) VALUES (...);
195     INSERT INTO _investor_table (&quot;id&quot;,&quot;dollars&quot;) VALUES (currval('_user_table_id_seq',...) );</pre>
196 <p>For deletes, the triggers fire in reverse, to preserve referential integrity (foreign key constraints). For instance:</p>
197 <pre>
198    my $investor = $schema-&gt;resultset('Investor')-&gt;find({id =&gt; $args-&gt;{id}});
199    $investor-&gt;delete;</pre>
200 <p>Becomes:</p>
201 <pre>
202     DELETE FROM _investor_table WHERE (&quot;id&quot; = ?);
203     DELETE FROM _user_table WHERE (&quot;id&quot; = ?);</pre>
204 <p></p>
205 <p>
206 </p>
207 <hr />
208 <h1><a name="author">AUTHOR</a></h1>
209 <p>Matt S. Trout, &lt;<a href="mailto:mst@shadowcatsystems.co.uk">mst@shadowcatsystems.co.uk</a>&gt;</p>
210 <p>
211 </p>
212 <h2><a name="contributors">CONTRIBUTORS</a></h2>
213 <p>Docs: Amiri Barksdale, &lt;<a href="mailto:amiri@metalabel.com">amiri@metalabel.com</a>&gt;</p>
214 <p>
215 </p>
216 <hr />
217 <h1><a name="license">LICENSE</a></h1>
218 <p>This library is free software; you can redistribute it and/or modify
219 it under the same terms as Perl itself.</p>
220 <p>
221 </p>
222 <hr />
223 <h1><a name="see_also">SEE ALSO</a></h1>
224 <p><a href="/DBIx/Class.html">the DBIx::Class manpage</a>
225 <a href="/DBIx/Class/ResultSource.html">the DBIx::Class::ResultSource manpage</a></p>
226
227 </body>
228
229 </html>