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