schema-loader now uses Class::C3, and ::Pg uses that to override ::Generic->new(...
[dbsrgits/DBIx-Class-Schema-Loader.git] / lib / DBIx / Class / Schema / Loader / Generic.pm
CommitLineData
18fca96a 1package DBIx::Class::Schema::Loader::Generic;
a78e3fed 2
3use strict;
a4a19f3c 4use warnings;
2a4b8262 5use Class::C3;
a4a19f3c 6
a78e3fed 7use Carp;
8use Lingua::EN::Inflect;
3980d69c 9use base qw/Class::Accessor::Fast/;
a4a19f3c 10
a78e3fed 11require DBIx::Class::Core;
a4a19f3c 12
3980d69c 13# The first group are all arguments which are may be defaulted within,
14# The last two (classes, monikers) are generated locally:
15
16__PACKAGE__->mk_ro_accessors(qw/
17 schema
18 dsn
19 user
20 password
21 options
22 exclude
23 constraint
24 additional_classes
25 additional_base_classes
26 left_base_classes
27 relationships
28 inflect
29 db_schema
30 drop_db_schema
31 debug
32
33 classes
34 monikers
35 /);
a4a19f3c 36
a78e3fed 37=head1 NAME
38
18fca96a 39DBIx::Class::Schema::Loader::Generic - Generic DBIx::Class::Schema::Loader Implementation.
a78e3fed 40
41=head1 SYNOPSIS
42
18fca96a 43See L<DBIx::Class::Schema::Loader>
a78e3fed 44
45=head1 DESCRIPTION
46
47=head2 OPTIONS
48
49Available constructor options are:
50
51=head3 additional_base_classes
52
53List of additional base classes your table classes will use.
54
55=head3 left_base_classes
56
57List of additional base classes, that need to be leftmost.
58
59=head3 additional_classes
60
61List of additional classes which your table classes will use.
62
63=head3 constraint
64
65Only load tables matching regex.
66
67=head3 exclude
68
69Exclude tables matching regex.
70
71=head3 debug
72
73Enable debug messages.
74
75=head3 dsn
76
77DBI Data Source Name.
78
a78e3fed 79=head3 password
80
81Password.
82
83=head3 relationships
84
85Try to automatically detect/setup has_a and has_many relationships.
86
87=head3 inflect
88
89An hashref, which contains exceptions to Lingua::EN::Inflect::PL().
90Useful for foreign language column names.
91
92=head3 user
93
94Username.
95
96=head2 METHODS
97
98=cut
99
100=head3 new
101
3980d69c 102Constructor for L<DBIx::Class::Schema::Loader::Generic>, used internally
103by L<DBIx::Class::Schema::Loader>.
a78e3fed 104
2a4b8262 105=head3 load
106
107Does the actual schema-construction work, used internally by
108L<DBIx::Class::Schema::Loader> right after object construction.
109
a78e3fed 110=cut
111
3980d69c 112# ensure that a peice of object data is a valid arrayref, creating
113# an empty one or encapsulating whatever's there.
114sub _ensure_arrayref {
115 my $self = shift;
e26a4023 116
3980d69c 117 foreach (@_) {
118 $self->{$_} ||= [];
119 $self->{$_} = [ $self->{$_} ]
120 unless ref $self->{$_} eq 'ARRAY';
121 }
a78e3fed 122}
123
3980d69c 124sub new {
125 my ( $class, %args ) = @_;
a78e3fed 126
3980d69c 127 my $self = { %args };
fbd83464 128
3980d69c 129 bless $self => $class;
fbd83464 130
3980d69c 131 $self->{db_schema} ||= '';
132 $self->{constraint} ||= '.*';
133 $self->{inflect} ||= {};
134 $self->_ensure_arrayref(qw/additional_classes
135 additional_base_classes
136 left_base_classes/);
fbd83464 137
3980d69c 138 $self->{monikers} = {};
139 $self->{classes} = {};
a78e3fed 140
2a4b8262 141 $self;
142}
143
144sub load {
145 my $self = shift;
146
3980d69c 147 $self->schema->connection($self->dsn, $self->user,
148 $self->password, $self->options);
a78e3fed 149
3980d69c 150 warn qq/\### START DBIx::Class::Schema::Loader dump ###\n/
151 if $self->debug;
a78e3fed 152
3980d69c 153 $self->_load_classes;
154 $self->_load_relationships if $self->relationships;
a78e3fed 155
3980d69c 156 warn qq/\### END DBIx::Class::Schema::Loader dump ###\n/
157 if $self->debug;
52bf3f26 158 $self->schema->storage->disconnect;
a78e3fed 159
3980d69c 160 $self;
a78e3fed 161}
162
163# Overload in your driver class
3980d69c 164sub _db_classes { croak "ABSTRACT METHOD" }
66742793 165
16f6b6ac 166# Inflect a relationship name
167# XXX (should pluralize, but currently also tends to de-pluralize plurals)
3980d69c 168sub _inflect_relname {
169 my ($self, $relname) = @_;
708c0939 170
3980d69c 171 return $self->inflect->{$relname} if exists $self->inflect->{$relname};
172 return Lingua::EN::Inflect::PL($relname);
16f6b6ac 173}
a78e3fed 174
16f6b6ac 175# Set up a simple relation with just a local col and foreign table
3980d69c 176sub _make_simple_rel {
177 my ($self, $table, $other, $col) = @_;
708c0939 178
3980d69c 179 my $table_class = $self->classes->{$table};
180 my $other_class = $self->classes->{$other};
181 my $table_relname = $self->_inflect_relname(lc $table);
66742793 182
3980d69c 183 warn qq/\# Belongs_to relationship\n/ if $self->debug;
16f6b6ac 184 warn qq/$table_class->belongs_to( '$col' => '$other_class' );\n\n/
3980d69c 185 if $self->debug;
16f6b6ac 186 $table_class->belongs_to( $col => $other_class );
708c0939 187
3980d69c 188 warn qq/\# Has_many relationship\n/ if $self->debug;
16f6b6ac 189 warn qq/$other_class->has_many( '$table_relname' => '$table_class',/
190 . qq/$col);\n\n/
3980d69c 191 if $self->debug;
708c0939 192
16f6b6ac 193 $other_class->has_many( $table_relname => $table_class, $col);
194}
a78e3fed 195
3980d69c 196# not a class method, just a helper for cond_rel XXX
197sub _stringify_hash {
198 my $href = shift;
199
200 return '{ ' .
201 join(q{, }, map("$_ => $href->{$_}", keys %$href))
202 . ' }';
203}
204
16f6b6ac 205# Set up a complex relation based on a hashref condition
3980d69c 206sub _make_cond_rel {
207 my ( $self, $table, $other, $cond ) = @_;
a78e3fed 208
3980d69c 209 my $table_class = $self->classes->{$table};
210 my $other_class = $self->classes->{$other};
211 my $table_relname = $self->_inflect_relname(lc $table);
16f6b6ac 212 my $other_relname = lc $other;
708c0939 213
16f6b6ac 214 # for single-column case, set the relname to the column name,
215 # to make filter accessors work
216 if(scalar keys %$cond == 1) {
217 my ($col) = keys %$cond;
218 $other_relname = $cond->{$col};
4ce22656 219 }
16f6b6ac 220
221 my $rev_cond = { reverse %$cond };
222
3980d69c 223 my $cond_printable = _stringify_hash($cond)
224 if $self->debug;
225 my $rev_cond_printable = _stringify_hash($rev_cond)
226 if $self->debug;
16f6b6ac 227
3980d69c 228 warn qq/\# Belongs_to relationship\n/ if $self->debug;
16f6b6ac 229
230 warn qq/$table_class->belongs_to( '$other_relname' => '$other_class',/
231 . qq/$cond_printable);\n\n/
3980d69c 232 if $self->debug;
16f6b6ac 233
234 $table_class->belongs_to( $other_relname => $other_class, $cond);
235
3980d69c 236 warn qq/\# Has_many relationship\n/ if $self->debug;
16f6b6ac 237
238 warn qq/$other_class->has_many( '$table_relname' => '$table_class',/
239 . qq/$rev_cond_printable);\n\n/
240 . qq/);\n\n/
3980d69c 241 if $self->debug;
16f6b6ac 242
243 $other_class->has_many( $table_relname => $table_class, $rev_cond);
a78e3fed 244}
245
42c0680e 246sub _use {
247 my $self = shift;
248 my $target = shift;
249
250 foreach (@_) {
251 $_->require or croak ($_ . "->require: $@");
252 eval "package $target; use $_;";
253 croak "use $_: $@" if $@;
254 }
255}
256
257sub _inject {
258 my $self = shift;
259 my $target = shift;
260 my $schema = $self->schema;
261
262 foreach (@_) {
263 $_->require or croak ($_ . "->require: $@");
264 $schema->inject_base($target, $_);
265 }
266}
267
a78e3fed 268# Load and setup classes
3980d69c 269sub _load_classes {
270 my $self = shift;
af6c2665 271
3980d69c 272 my @tables = $self->_tables();
273 my @db_classes = $self->_db_classes();
274 my $schema = $self->schema;
a78e3fed 275
a78e3fed 276 foreach my $table (@tables) {
3980d69c 277 my $constraint = $self->constraint;
278 my $exclude = $self->exclude;
279
280 next unless $table =~ /$constraint/;
281 next if defined $exclude && $table =~ /$exclude/;
af6c2665 282
af6c2665 283 my ($db_schema, $tbl) = split /\./, $table;
af96f52e 284 my $tablename = lc $table;
a78e3fed 285 if($tbl) {
3980d69c 286 $tablename = $self->drop_db_schema ? $tbl : lc $table;
af6c2665 287 }
3980d69c 288 my $lc_tblname = lc $tablename;
af6c2665 289
3980d69c 290 my $table_moniker = $self->_table2moniker($db_schema, $tbl);
291 my $table_class = $schema . q{::} . $table_moniker;
af6c2665 292
42c0680e 293 $self->_inject($table_class, 'DBIx::Class::Core');
294 $self->_inject($table_class, @db_classes);
295 $self->_inject($table_class, @{$self->additional_base_classes});
296 $self->_use ($table_class, @{$self->additional_classes});
297 $self->_inject($table_class, @{$self->left_base_classes});
3980d69c 298
299 warn qq/\# Initializing table "$tablename" as "$table_class"\n/
300 if $self->debug;
e26a4023 301 $table_class->table($lc_tblname);
af6c2665 302
3980d69c 303 my ( $cols, $pks ) = $self->_table_info($table);
a78e3fed 304 carp("$table has no primary key") unless @$pks;
a4a19f3c 305 $table_class->add_columns(@$cols);
306 $table_class->set_primary_key(@$pks) if @$pks;
af6c2665 307
3980d69c 308 warn qq/$table_class->table('$tablename');\n/ if $self->debug;
a78e3fed 309 my $columns = join "', '", @$cols;
3980d69c 310 warn qq/$table_class->add_columns('$columns')\n/ if $self->debug;
a78e3fed 311 my $primaries = join "', '", @$pks;
3980d69c 312 warn qq/$table_class->set_primary_key('$primaries')\n/
313 if $self->debug && @$pks;
af6c2665 314
3980d69c 315 $schema->register_class($table_moniker, $table_class);
316 $self->classes->{$lc_tblname} = $table_class;
317 $self->monikers->{$lc_tblname} = $table_moniker;
a78e3fed 318 }
319}
320
3980d69c 321=head3 tables
322
8a6b44ef 323Returns a sorted list of loaded tables, using the original database table
324names. Actually generated from the keys of the C<monikers> hash below.
3980d69c 325
8a6b44ef 326 my @tables = $schema->loader->tables;
327
328=head3 monikers
329
330Returns a hashref of loaded table-to-moniker mappings for the original
331database table names.
332
333 my $monikers = $schema->loader->monikers;
334 my $foo_tbl_moniker = $monikers->{foo_tbl};
335 # -or-
336 my $foo_tbl_moniker = $schema->loader->monikers->{foo_tbl};
337 # $foo_tbl_moniker would look like "FooTbl"
338
339=head3 classes
340
341Returns a hashref of table-to-classname mappings for the original database
342table names. You probably shouldn't be using this, as operating on the
343original class names is usually a bad idea. This hook is here for people
344who want to do strange and/or possibly broken things. The usual way to
345get at things is C<$schema->resultset('FooTbl')>, where C<FooTbl> is a
346moniker as returned by C<monikers> above.
347
348 my $classes = $schema->loader->classes;
349 my $foo_tbl_class = $classes->{foo_tbl};
350 # -or-
351 my $foo_tbl_class = $schema->loader->classes->{foo_tbl};
352 # $foo_tbl_class would look like "My::Schema::FooTbl",
353 # assuming the schema class is "My::Schema"
3980d69c 354
355=cut
356
357sub tables {
358 my $self = shift;
359
360 return sort keys %{ $self->monikers };
361}
362
a78e3fed 363# Find and setup relationships
3980d69c 364sub _load_relationships {
365 my $self = shift;
366
367 my $dbh = $self->schema->storage->dbh;
708c0939 368 my $quoter = $dbh->get_info(29) || q{"};
3980d69c 369 foreach my $table ( $self->tables ) {
708c0939 370 my $rels = {};
371 my $sth = $dbh->foreign_key_info( '',
3980d69c 372 $self->db_schema, '', '', '', $table );
708c0939 373 next if !$sth;
374 while(my $raw_rel = $sth->fetchrow_hashref) {
375 my $uk_tbl = lc $raw_rel->{UK_TABLE_NAME};
376 my $uk_col = lc $raw_rel->{UK_COLUMN_NAME};
377 my $fk_col = lc $raw_rel->{FK_COLUMN_NAME};
4ce22656 378 my $relid = lc $raw_rel->{UK_NAME};
708c0939 379 $uk_tbl =~ s/$quoter//g;
380 $uk_col =~ s/$quoter//g;
381 $fk_col =~ s/$quoter//g;
4ce22656 382 $relid =~ s/$quoter//g;
383 $rels->{$relid}->{tbl} = $uk_tbl;
384 $rels->{$relid}->{cols}->{$uk_col} = $fk_col;
708c0939 385 }
386
4ce22656 387 foreach my $relid (keys %$rels) {
388 my $reltbl = $rels->{$relid}->{tbl};
389 my $cond = $rels->{$relid}->{cols};
3980d69c 390 eval { $self->_make_cond_rel( $table, $reltbl, $cond ) };
708c0939 391 warn qq/\# belongs_to_many failed "$@"\n\n/
3980d69c 392 if $@ && $self->debug;
a78e3fed 393 }
394 }
395}
396
65644119 397# Make a moniker from a table
3980d69c 398sub _table2moniker {
399 my ( $self, $db_schema, $table ) = @_;
af6c2665 400
af96f52e 401 my $db_schema_ns;
af6c2665 402
af96f52e 403 if($table) {
404 $db_schema = ucfirst lc $db_schema;
3980d69c 405 $db_schema_ns = $db_schema if(!$self->drop_db_schema);
af96f52e 406 } else {
407 $table = $db_schema;
a78e3fed 408 }
af6c2665 409
65644119 410 my $moniker = join '', map ucfirst, split /[\W_]+/, lc $table;
411 $moniker = $db_schema_ns ? $db_schema_ns . $moniker : $moniker;
af96f52e 412
65644119 413 return $moniker;
a78e3fed 414}
415
416# Overload in driver class
3980d69c 417sub _tables { croak "ABSTRACT METHOD" }
a78e3fed 418
3980d69c 419sub _table_info { croak "ABSTRACT METHOD" }
a78e3fed 420
421=head1 SEE ALSO
422
18fca96a 423L<DBIx::Class::Schema::Loader>
a78e3fed 424
425=cut
426
4271;