Beginning of docs. Making headway. Next is subroutine docs
[dbsrgits/DBIx-Class-ResultSource-MultipleTableInheritance.git] / README
1 NAME
2     DBIx::Class::ResultSource::MultipleTableInheritance -- Use multiple
3     tables to define your classes
4
5 SYNOPSIS
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
72 WHY?
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
153 HOW?
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
192 AUTHOR
193     Matt S. Trout, <mst@shadowcatsystems.co.uk>
194
195   CONTRIBUTORS
196     Docs: Amiri Barksdale, <amiri@metalabel.com>
197
198 LICENSE
199     This library is free software; you can redistribute it and/or modify it
200     under the same terms as Perl itself.
201
202 SEE ALSO
203     DBIx::Class DBIx::Class::ResultSource
204