2 DBIx::Class::ResultSource::MultipleTableInheritance -- Use multiple
3 tables to define your classes
7 package MyApp::Schema::Result::Coffee;
9 __PACKAGE__->table_class('DBIx::Class::ResultSource::MultipleTableInheritance');
10 __PACKAGE__->table('coffee');
11 __PACKAGE__->add_columns(
14 data_type => "integer",
15 default_value => "nextval('coffee_seq'::regclass)",
16 is_auto_increment => 1,
24 default_value => "good",
28 __PACKAGE__->set_primary_key("id");
34 package MyApp::Schema::Result::Sumatra;
38 __PACKAGE__->table('sumatra');
40 __PACKAGE__->add_columns(
44 default_value => undef,
54 my $schema = MyApp::Schema->connect($dsn);
56 my $cup = $schema->resultset('Sumatra')->new;
58 print STDERR Dumper $cup->columns;
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 transparently writable.
68 This is accomplished through the use of stored procedures that map
69 changes written to the view to changes to the underlying concrete
73 In many applications, many classes are subclasses of others. Let's say
76 # Conceptual domain model
91 That's redundant. Hold on a sec...
99 class Investor extends User {
103 Good idea, but how to put this into code?
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.
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),
117 Let's not discuss that further.
119 A second, better, solution is to break out the two tables into user and
122 create table "user" (
123 "id" integer not null primary key autoincrement,
124 "name" text not null,
125 "password" text not null
128 create table "investor" (
129 "id" integer not null references user("id"),
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:
137 my $user_rs = $schema->resultset('User');
138 my $new_user = $user_rs->create(
139 name => $args->{name},
140 password => $args->{password},
145 my $new_investor = $schema->resultset('Investor')->create(
147 dollars => $args->{dollars},
150 One can cope well with the second strategy, and it seems to be the most
151 popular smart choice.
154 There is a third strategy implemented here. Make the database do more of
155 the work: hide the nasty bits so we don't have to handle them unless we
156 really want to. It'll save us some typing and it'll make for more
157 expressive code. What if we could do this:
159 my $new_investor = $schema->resultset('Investor')->create(
160 name => $args->{name},
161 password => $args->{password},
162 dollars => $args->{dollars},
165 And have it Just Work? The user...
168 name => $args->{name},
169 password => $args->{password},
172 should be created behind the scenes, and the use of either user or
173 investor in your code should require no special handling. Deleting and
174 updating $new_investor should also delete or update the user row.
176 It does. User and investor are both views, their concrete tables
177 abstracted away behind a set of rules and triggers. You would expect the
178 above DBIC create statement to look like this in SQL:
180 INSERT INTO investor ("name","password","dollars") VALUES (...);
182 But using MTI, it is really this:
184 INSERT INTO _user_table ("username","password") VALUES (...);
185 INSERT INTO _investor_table ("id","dollars") VALUES (currval('_user_table_id_seq',...) );
187 For deletes, the triggers fire in reverse, to preserve referential
188 integrity (foreign key constraints). For instance:
190 my $investor = $schema->resultset('Investor')->find({id => $args->{id}});
195 DELETE FROM _investor_table WHERE ("id" = ?);
196 DELETE FROM _user_table WHERE ("id" = ?);
199 new MTI find the parents, if any, of your resultset class and adds them
200 to the list of parent_sources for the table.
202 add_additional_parents
203 Continuing with coffee:
205 __PACKAGE__->result_source_instance->add_additional_parents(
207 MyApp::Schema::Result::Beverage
208 MyApp::Schema::Result::Liquid
212 This just lets you manually add additional parents beyond the ones
215 add_additional_parent
216 __PACKAGE__->result_source_instance->add_additional_parent(
217 MyApp::Schema::Result::Beverage
220 You can also add just one.
222 attach_additional_sources
223 MTI takes the parents' sources and relationships, creates new
224 DBIx::Class:Table object from them, and registers this as a new,
225 raw, source in the schema, e.g.,
229 print STDERR map { "$_\n" } MyApp::Schema->sources;
237 Raw::Sumatra will be used to generate the view.
240 This takes the raw table and generates the view (and stored
241 procedures) you will use.
244 Matt S. Trout, <mst@shadowcatsystems.co.uk>
247 Docs: Amiri Barksdale, <amiri@metalabel.com>
250 This library is free software; you can redistribute it and/or modify it
251 under the same terms as Perl itself.
254 DBIx::Class DBIx::Class::ResultSource