Class::C3::reinit changes backported from branch for speed reasons, version bumped...
[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;
457eb8a6 5use base qw/Class::Accessor::Fast/;
2a4b8262 6use Class::C3;
a78e3fed 7use Carp;
8use Lingua::EN::Inflect;
8563178e 9use UNIVERSAL::require;
f3be6194 10require DBIx::Class;
a4a19f3c 11
3980d69c 12# The first group are all arguments which are may be defaulted within,
13# The last two (classes, monikers) are generated locally:
14
15__PACKAGE__->mk_ro_accessors(qw/
16 schema
1031d4f6 17 connect_info
3980d69c 18 exclude
19 constraint
20 additional_classes
21 additional_base_classes
22 left_base_classes
9fa99683 23 components
24 resultset_components
3980d69c 25 relationships
4350370d 26 inflect_map
27 moniker_map
3980d69c 28 db_schema
29 drop_db_schema
30 debug
31
ac5ad557 32 _tables
3980d69c 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
457eb8a6 47This is the base class for the vendor-specific C<DBIx::Class::Schema::*>
48classes, and implements the common functionality between them.
49
50=head1 OPTIONS
a78e3fed 51
52Available constructor options are:
53
1031d4f6 54=head2 connect_info
55
56Identical to the connect_info arguments to C<connect> and C<connection>
57that are mentioned in L<DBIx::Class::Schema>.
58
59An arrayref of connection information. For DBI-based Schemas,
60this takes the form:
61
62 connect_info => [ $dsn, $user, $pass, { AutoCommit => 1 } ],
63
457eb8a6 64=head2 additional_base_classes
a78e3fed 65
66List of additional base classes your table classes will use.
67
457eb8a6 68=head2 left_base_classes
a78e3fed 69
70List of additional base classes, that need to be leftmost.
71
457eb8a6 72=head2 additional_classes
a78e3fed 73
74List of additional classes which your table classes will use.
75
c2849787 76=head2 components
77
78List of additional components to be loaded into your table classes.
79A good example would be C<ResultSetManager>.
80
81=head2 resultset_components
82
83List of additional resultset components to be loaded into your table
84classes. A good example would be C<AlwaysRS>. Component
85C<ResultSetManager> will be automatically added to the above
86C<components> list if this option is set.
87
457eb8a6 88=head2 constraint
a78e3fed 89
90Only load tables matching regex.
91
457eb8a6 92=head2 exclude
a78e3fed 93
94Exclude tables matching regex.
95
457eb8a6 96=head2 debug
a78e3fed 97
98Enable debug messages.
99
457eb8a6 100=head2 relationships
a78e3fed 101
102Try to automatically detect/setup has_a and has_many relationships.
103
4350370d 104=head2 moniker_map
105
106Overrides the default tablename -> moniker translation. Can be either
107a hashref of table => moniker names, or a coderef for a translator
108function taking a single scalar table name argument and returning
109a scalar moniker. If the hash entry does not exist, or the function
110returns a false/undef value, the code falls back to default behavior
111for that table name.
112
113=head2 inflect_map
114
115Just like L</moniker_map> above, but for inflecting (pluralizing)
116relationship names.
117
457eb8a6 118=head2 inflect
a78e3fed 119
4350370d 120Deprecated. Equivalent to L</inflect_map>, but previously only took
121a hashref argument, not a coderef. If you set C<inflect> to anything,
122that setting will be copied to L</inflect_map>.
a78e3fed 123
1031d4f6 124=head2 dsn
125
126DEPRECATED, use L</connect_info> instead.
127
128DBI Data Source Name.
129
457eb8a6 130=head2 user
a78e3fed 131
1031d4f6 132DEPRECATED, use L</connect_info> instead.
133
a78e3fed 134Username.
135
1031d4f6 136=head2 password
137
138DEPRECATED, use L</connect_info> instead.
139
140Password.
141
142=head2 options
143
144DEPRECATED, use L</connect_info> instead.
145
146DBI connection options hashref, like:
147
148 { AutoCommit => 1 }
149
457eb8a6 150=head1 METHODS
2a4b8262 151
a78e3fed 152=cut
153
3980d69c 154# ensure that a peice of object data is a valid arrayref, creating
155# an empty one or encapsulating whatever's there.
156sub _ensure_arrayref {
157 my $self = shift;
e26a4023 158
3980d69c 159 foreach (@_) {
160 $self->{$_} ||= [];
161 $self->{$_} = [ $self->{$_} ]
162 unless ref $self->{$_} eq 'ARRAY';
163 }
a78e3fed 164}
165
457eb8a6 166=head2 new
167
168Constructor for L<DBIx::Class::Schema::Loader::Generic>, used internally
169by L<DBIx::Class::Schema::Loader>.
170
171=cut
172
3980d69c 173sub new {
174 my ( $class, %args ) = @_;
a78e3fed 175
3980d69c 176 my $self = { %args };
fbd83464 177
3980d69c 178 bless $self => $class;
fbd83464 179
3980d69c 180 $self->{db_schema} ||= '';
181 $self->{constraint} ||= '.*';
3980d69c 182 $self->_ensure_arrayref(qw/additional_classes
183 additional_base_classes
c2849787 184 left_base_classes
9fa99683 185 components
1031d4f6 186 resultset_components
187 connect_info/);
c2849787 188
189 push(@{$self->{components}}, 'ResultSetManager')
190 if @{$self->{resultset_components}};
fbd83464 191
3980d69c 192 $self->{monikers} = {};
193 $self->{classes} = {};
a78e3fed 194
4350370d 195 # Support deprecated argument name
196 $self->{inflect_map} ||= $self->{inflect};
197
1031d4f6 198 # Support deprecated connect_info args, even mixed
199 # with a valid partially-filled connect_info
200 $self->{connect_info}->[0] ||= $self->{dsn};
201 $self->{connect_info}->[1] ||= $self->{user};
202 $self->{connect_info}->[2] ||= $self->{password};
203 $self->{connect_info}->[3] ||= $self->{options};
204
2a4b8262 205 $self;
206}
207
457eb8a6 208=head2 load
209
210Does the actual schema-construction work, used internally by
211L<DBIx::Class::Schema::Loader> right after object construction.
212
213=cut
214
2a4b8262 215sub load {
216 my $self = shift;
217
1031d4f6 218 $self->schema->connection(@{$self->connect_info});
a78e3fed 219
3980d69c 220 warn qq/\### START DBIx::Class::Schema::Loader dump ###\n/
221 if $self->debug;
a78e3fed 222
3980d69c 223 $self->_load_classes;
224 $self->_load_relationships if $self->relationships;
d161cb49 225 $self->_load_external;
a78e3fed 226
3980d69c 227 warn qq/\### END DBIx::Class::Schema::Loader dump ###\n/
228 if $self->debug;
52bf3f26 229 $self->schema->storage->disconnect;
a78e3fed 230
3980d69c 231 $self;
a78e3fed 232}
233
d161cb49 234sub _load_external {
235 my $self = shift;
236
ac5ad557 237 foreach my $table_class (map { $self->classes->{$_} } $self->tables) {
d161cb49 238 $table_class->require;
239 if($@ && $@ !~ /^Can't locate /) {
240 croak "Failed to load external class definition"
241 . "for '$table_class': $@";
242 }
243 elsif(!$@) {
244 warn qq/# Loaded external class definition for '$table_class'\n/
245 if $self->debug;
246 }
247 }
248}
249
a78e3fed 250# Overload in your driver class
3980d69c 251sub _db_classes { croak "ABSTRACT METHOD" }
66742793 252
16f6b6ac 253# Inflect a relationship name
3980d69c 254sub _inflect_relname {
255 my ($self, $relname) = @_;
708c0939 256
4350370d 257 if( ref $self->{inflect_map} eq 'HASH' ) {
258 return $self->inflect_map->{$relname}
259 if exists $self->inflect_map->{$relname};
260 }
261 elsif( ref $self->{inflect_map} eq 'CODE' ) {
262 my $inflected = $self->inflect_map->($relname);
263 return $inflected if $inflected;
264 }
265
3980d69c 266 return Lingua::EN::Inflect::PL($relname);
16f6b6ac 267}
a78e3fed 268
16f6b6ac 269# Set up a simple relation with just a local col and foreign table
3980d69c 270sub _make_simple_rel {
271 my ($self, $table, $other, $col) = @_;
708c0939 272
3980d69c 273 my $table_class = $self->classes->{$table};
274 my $other_class = $self->classes->{$other};
275 my $table_relname = $self->_inflect_relname(lc $table);
66742793 276
3980d69c 277 warn qq/\# Belongs_to relationship\n/ if $self->debug;
16f6b6ac 278 warn qq/$table_class->belongs_to( '$col' => '$other_class' );\n\n/
3980d69c 279 if $self->debug;
16f6b6ac 280 $table_class->belongs_to( $col => $other_class );
708c0939 281
3980d69c 282 warn qq/\# Has_many relationship\n/ if $self->debug;
16f6b6ac 283 warn qq/$other_class->has_many( '$table_relname' => '$table_class',/
284 . qq/$col);\n\n/
3980d69c 285 if $self->debug;
708c0939 286
16f6b6ac 287 $other_class->has_many( $table_relname => $table_class, $col);
288}
a78e3fed 289
3980d69c 290# not a class method, just a helper for cond_rel XXX
291sub _stringify_hash {
292 my $href = shift;
293
294 return '{ ' .
295 join(q{, }, map("$_ => $href->{$_}", keys %$href))
296 . ' }';
297}
298
16f6b6ac 299# Set up a complex relation based on a hashref condition
3980d69c 300sub _make_cond_rel {
301 my ( $self, $table, $other, $cond ) = @_;
a78e3fed 302
3980d69c 303 my $table_class = $self->classes->{$table};
304 my $other_class = $self->classes->{$other};
305 my $table_relname = $self->_inflect_relname(lc $table);
16f6b6ac 306 my $other_relname = lc $other;
708c0939 307
16f6b6ac 308 # for single-column case, set the relname to the column name,
309 # to make filter accessors work
310 if(scalar keys %$cond == 1) {
311 my ($col) = keys %$cond;
312 $other_relname = $cond->{$col};
4ce22656 313 }
16f6b6ac 314
315 my $rev_cond = { reverse %$cond };
316
c5de0b0c 317 for (keys %$rev_cond) {
c2849787 318 $rev_cond->{"foreign.$_"} = "self.".$rev_cond->{$_};
319 delete $rev_cond->{$_};
c5de0b0c 320 }
321
3980d69c 322 my $cond_printable = _stringify_hash($cond)
323 if $self->debug;
324 my $rev_cond_printable = _stringify_hash($rev_cond)
325 if $self->debug;
16f6b6ac 326
3980d69c 327 warn qq/\# Belongs_to relationship\n/ if $self->debug;
16f6b6ac 328
329 warn qq/$table_class->belongs_to( '$other_relname' => '$other_class',/
330 . qq/$cond_printable);\n\n/
3980d69c 331 if $self->debug;
16f6b6ac 332
333 $table_class->belongs_to( $other_relname => $other_class, $cond);
334
3980d69c 335 warn qq/\# Has_many relationship\n/ if $self->debug;
16f6b6ac 336
337 warn qq/$other_class->has_many( '$table_relname' => '$table_class',/
338 . qq/$rev_cond_printable);\n\n/
339 . qq/);\n\n/
3980d69c 340 if $self->debug;
16f6b6ac 341
342 $other_class->has_many( $table_relname => $table_class, $rev_cond);
a78e3fed 343}
344
42c0680e 345sub _use {
346 my $self = shift;
347 my $target = shift;
348
349 foreach (@_) {
350 $_->require or croak ($_ . "->require: $@");
351 eval "package $target; use $_;";
352 croak "use $_: $@" if $@;
353 }
354}
355
356sub _inject {
357 my $self = shift;
358 my $target = shift;
359 my $schema = $self->schema;
360
361 foreach (@_) {
362 $_->require or croak ($_ . "->require: $@");
363 $schema->inject_base($target, $_);
364 }
365}
366
a78e3fed 367# Load and setup classes
3980d69c 368sub _load_classes {
369 my $self = shift;
af6c2665 370
3980d69c 371 my @db_classes = $self->_db_classes();
372 my $schema = $self->schema;
a78e3fed 373
ac5ad557 374 my $constraint = $self->constraint;
375 my $exclude = $self->exclude;
376 my @tables = sort grep
377 { /$constraint/ && (!$exclude || ! /$exclude/) }
378 $self->_tables_list;
379
380 $self->{_tables} = \@tables;
3980d69c 381
ac5ad557 382 foreach my $table (@tables) {
af6c2665 383 my ($db_schema, $tbl) = split /\./, $table;
a78e3fed 384 if($tbl) {
ac5ad557 385 $table = $self->drop_db_schema ? $tbl : $table;
af6c2665 386 }
ac5ad557 387 my $lc_table = lc $table;
af6c2665 388
3980d69c 389 my $table_moniker = $self->_table2moniker($db_schema, $tbl);
390 my $table_class = $schema . q{::} . $table_moniker;
af6c2665 391
09d632a1 392 $self->classes->{$lc_table} = $table_class;
393 $self->monikers->{$lc_table} = $table_moniker;
394 $self->classes->{$table} = $table_class;
395 $self->monikers->{$table} = $table_moniker;
396
397 no warnings 'redefine';
398 local *Class::C3::reinitialize = sub { };
399 use warnings;
400
9fa99683 401 { no strict 'refs';
f3be6194 402 @{"${table_class}::ISA"} = qw/DBIx::Class/;
9fa99683 403 }
42c0680e 404 $self->_use ($table_class, @{$self->additional_classes});
9fa99683 405 $self->_inject($table_class, @{$self->additional_base_classes});
406 $table_class->load_components(@{$self->components}, @db_classes, 'Core');
407 $table_class->load_resultset_components(@{$self->resultset_components})
408 if @{$self->resultset_components};
42c0680e 409 $self->_inject($table_class, @{$self->left_base_classes});
09d632a1 410 }
411
412 Class::C3::reinitialize;
413
414 foreach my $table (@tables) {
415 my $table_class = $self->classes->{$table};
416 my $table_moniker = $self->monikers->{$table};
3980d69c 417
ac5ad557 418 warn qq/\# Initializing table "$table" as "$table_class"\n/
3980d69c 419 if $self->debug;
ac5ad557 420 $table_class->table($table);
af6c2665 421
3980d69c 422 my ( $cols, $pks ) = $self->_table_info($table);
a78e3fed 423 carp("$table has no primary key") unless @$pks;
a4a19f3c 424 $table_class->add_columns(@$cols);
425 $table_class->set_primary_key(@$pks) if @$pks;
af6c2665 426
ac5ad557 427 warn qq/$table_class->table('$table');\n/ if $self->debug;
a78e3fed 428 my $columns = join "', '", @$cols;
3980d69c 429 warn qq/$table_class->add_columns('$columns')\n/ if $self->debug;
a78e3fed 430 my $primaries = join "', '", @$pks;
3980d69c 431 warn qq/$table_class->set_primary_key('$primaries')\n/
432 if $self->debug && @$pks;
af6c2665 433
3980d69c 434 $schema->register_class($table_moniker, $table_class);
a78e3fed 435 }
436}
437
457eb8a6 438=head2 tables
3980d69c 439
8a6b44ef 440Returns a sorted list of loaded tables, using the original database table
ac5ad557 441names.
3980d69c 442
457eb8a6 443 my @tables = $schema->loader->tables;
3980d69c 444
445=cut
446
447sub tables {
448 my $self = shift;
449
ac5ad557 450 return @{$self->_tables};
3980d69c 451}
452
a78e3fed 453# Find and setup relationships
3980d69c 454sub _load_relationships {
455 my $self = shift;
456
457 my $dbh = $self->schema->storage->dbh;
708c0939 458 my $quoter = $dbh->get_info(29) || q{"};
3980d69c 459 foreach my $table ( $self->tables ) {
708c0939 460 my $rels = {};
461 my $sth = $dbh->foreign_key_info( '',
3980d69c 462 $self->db_schema, '', '', '', $table );
708c0939 463 next if !$sth;
464 while(my $raw_rel = $sth->fetchrow_hashref) {
ac5ad557 465 my $uk_tbl = $raw_rel->{UK_TABLE_NAME};
708c0939 466 my $uk_col = lc $raw_rel->{UK_COLUMN_NAME};
467 my $fk_col = lc $raw_rel->{FK_COLUMN_NAME};
ac5ad557 468 my $relid = $raw_rel->{UK_NAME};
708c0939 469 $uk_tbl =~ s/$quoter//g;
470 $uk_col =~ s/$quoter//g;
471 $fk_col =~ s/$quoter//g;
4ce22656 472 $relid =~ s/$quoter//g;
473 $rels->{$relid}->{tbl} = $uk_tbl;
474 $rels->{$relid}->{cols}->{$uk_col} = $fk_col;
708c0939 475 }
476
4ce22656 477 foreach my $relid (keys %$rels) {
478 my $reltbl = $rels->{$relid}->{tbl};
479 my $cond = $rels->{$relid}->{cols};
3980d69c 480 eval { $self->_make_cond_rel( $table, $reltbl, $cond ) };
708c0939 481 warn qq/\# belongs_to_many failed "$@"\n\n/
3980d69c 482 if $@ && $self->debug;
a78e3fed 483 }
484 }
485}
486
65644119 487# Make a moniker from a table
3980d69c 488sub _table2moniker {
489 my ( $self, $db_schema, $table ) = @_;
af6c2665 490
af96f52e 491 my $db_schema_ns;
af6c2665 492
af96f52e 493 if($table) {
494 $db_schema = ucfirst lc $db_schema;
3980d69c 495 $db_schema_ns = $db_schema if(!$self->drop_db_schema);
af96f52e 496 } else {
497 $table = $db_schema;
a78e3fed 498 }
af6c2665 499
4350370d 500 my $moniker;
501
502 if( ref $self->moniker_map eq 'HASH' ) {
503 $moniker = $self->moniker_map->{$table};
504 }
505 elsif( ref $self->moniker_map eq 'CODE' ) {
506 $moniker = $self->moniker_map->($table);
507 }
508
509 $moniker ||= join '', map ucfirst, split /[\W_]+/, lc $table;
510
65644119 511 $moniker = $db_schema_ns ? $db_schema_ns . $moniker : $moniker;
af96f52e 512
65644119 513 return $moniker;
a78e3fed 514}
515
516# Overload in driver class
ac5ad557 517sub _tables_list { croak "ABSTRACT METHOD" }
a78e3fed 518
3980d69c 519sub _table_info { croak "ABSTRACT METHOD" }
a78e3fed 520
457eb8a6 521=head2 monikers
522
523Returns a hashref of loaded table-to-moniker mappings for the original
ac5ad557 524database table names. In cases where the database driver returns table
525names as uppercase or mixed case, there will also be a duplicate entry
526here in all lowercase. Best practice would be to use lower-case table
527names when accessing this.
457eb8a6 528
529 my $monikers = $schema->loader->monikers;
530 my $foo_tbl_moniker = $monikers->{foo_tbl};
531 # -or-
532 my $foo_tbl_moniker = $schema->loader->monikers->{foo_tbl};
533 # $foo_tbl_moniker would look like "FooTbl"
534
535=head2 classes
536
537Returns a hashref of table-to-classname mappings for the original database
ac5ad557 538table names. Same lowercase stuff as above applies here.
539
540You probably shouldn't be using this for any normal or simple
457eb8a6 541usage of your Schema. The usual way to run queries on your tables is via
542C<$schema-E<gt>resultset('FooTbl')>, where C<FooTbl> is a moniker as
543returned by C<monikers> above.
544
545 my $classes = $schema->loader->classes;
546 my $foo_tbl_class = $classes->{foo_tbl};
547 # -or-
548 my $foo_tbl_class = $schema->loader->classes->{foo_tbl};
549 # $foo_tbl_class would look like "My::Schema::FooTbl",
550 # assuming the schema class is "My::Schema"
551
a78e3fed 552=head1 SEE ALSO
553
18fca96a 554L<DBIx::Class::Schema::Loader>
a78e3fed 555
556=cut
557
5581;