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