1 package DBIx::Class::Schema::Loader::Generic;
5 use base qw/Class::Accessor::Fast/;
8 use Lingua::EN::Inflect;
9 use UNIVERSAL::require;
12 # The first group are all arguments which are may be defaulted within,
13 # The last two (classes, monikers) are generated locally:
15 __PACKAGE__->mk_ro_accessors(qw/
21 additional_base_classes
39 DBIx::Class::Schema::Loader::Generic - Generic DBIx::Class::Schema::Loader Implementation.
43 See L<DBIx::Class::Schema::Loader>
47 This is the base class for the vendor-specific C<DBIx::Class::Schema::*>
48 classes, and implements the common functionality between them.
52 Available constructor options are:
56 Identical to the connect_info arguments to C<connect> and C<connection>
57 that are mentioned in L<DBIx::Class::Schema>.
59 An arrayref of connection information. For DBI-based Schemas,
62 connect_info => [ $dsn, $user, $pass, { AutoCommit => 1 } ],
64 =head2 additional_base_classes
66 List of additional base classes your table classes will use.
68 =head2 left_base_classes
70 List of additional base classes, that need to be leftmost.
72 =head2 additional_classes
74 List of additional classes which your table classes will use.
78 List of additional components to be loaded into your table classes.
79 A good example would be C<ResultSetManager>.
81 =head2 resultset_components
83 List of additional resultset components to be loaded into your table
84 classes. A good example would be C<AlwaysRS>. Component
85 C<ResultSetManager> will be automatically added to the above
86 C<components> list if this option is set.
90 Only load tables matching regex.
94 Exclude tables matching regex.
98 Enable debug messages.
102 Try to automatically detect/setup has_a and has_many relationships.
106 Overrides the default tablename -> moniker translation. Can be either
107 a hashref of table => moniker names, or a coderef for a translator
108 function taking a single scalar table name argument and returning
109 a scalar moniker. If the hash entry does not exist, or the function
110 returns a false/undef value, the code falls back to default behavior
115 Just like L</moniker_map> above, but for inflecting (pluralizing)
120 Deprecated. Equivalent to L</inflect_map>, but previously only took
121 a hashref argument, not a coderef. If you set C<inflect> to anything,
122 that setting will be copied to L</inflect_map>.
126 DEPRECATED, use L</connect_info> instead.
128 DBI Data Source Name.
132 DEPRECATED, use L</connect_info> instead.
138 DEPRECATED, use L</connect_info> instead.
144 DEPRECATED, use L</connect_info> instead.
146 DBI connection options hashref, like:
154 # ensure that a peice of object data is a valid arrayref, creating
155 # an empty one or encapsulating whatever's there.
156 sub _ensure_arrayref {
161 $self->{$_} = [ $self->{$_} ]
162 unless ref $self->{$_} eq 'ARRAY';
168 Constructor for L<DBIx::Class::Schema::Loader::Generic>, used internally
169 by L<DBIx::Class::Schema::Loader>.
174 my ( $class, %args ) = @_;
176 my $self = { %args };
178 bless $self => $class;
180 $self->{db_schema} ||= '';
181 $self->{constraint} ||= '.*';
182 $self->_ensure_arrayref(qw/additional_classes
183 additional_base_classes
189 push(@{$self->{components}}, 'ResultSetManager')
190 if @{$self->{resultset_components}};
192 $self->{monikers} = {};
193 $self->{classes} = {};
195 # Support deprecated argument name
196 $self->{inflect_map} ||= $self->{inflect};
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};
210 Does the actual schema-construction work, used internally by
211 L<DBIx::Class::Schema::Loader> right after object construction.
218 $self->schema->connection(@{$self->connect_info});
220 warn qq/\### START DBIx::Class::Schema::Loader dump ###\n/
223 $self->_load_classes;
224 $self->_load_relationships if $self->relationships;
225 $self->_load_external;
227 warn qq/\### END DBIx::Class::Schema::Loader dump ###\n/
229 $self->schema->storage->disconnect;
237 foreach my $table_class (map { $self->classes->{$_} } $self->tables) {
238 $table_class->require;
239 if($@ && $@ !~ /^Can't locate /) {
240 croak "Failed to load external class definition"
241 . " for '$table_class': $@";
244 warn qq/# Loaded external class definition for '$table_class'\n/
250 # Overload in your driver class
251 sub _db_classes { croak "ABSTRACT METHOD" }
253 # Inflect a relationship name
254 sub _inflect_relname {
255 my ($self, $relname) = @_;
257 if( ref $self->{inflect_map} eq 'HASH' ) {
258 return $self->inflect_map->{$relname}
259 if exists $self->inflect_map->{$relname};
261 elsif( ref $self->{inflect_map} eq 'CODE' ) {
262 my $inflected = $self->inflect_map->($relname);
263 return $inflected if $inflected;
266 return Lingua::EN::Inflect::PL($relname);
269 # Set up a simple relation with just a local col and foreign table
270 sub _make_simple_rel {
271 my ($self, $table, $other, $col) = @_;
273 my $table_class = $self->classes->{$table};
274 my $other_class = $self->classes->{$other};
275 my $table_relname = $self->_inflect_relname(lc $table);
277 warn qq/\# Belongs_to relationship\n/ if $self->debug;
278 warn qq/$table_class->belongs_to( '$col' => '$other_class' );\n\n/
280 $table_class->belongs_to( $col => $other_class );
282 warn qq/\# Has_many relationship\n/ if $self->debug;
283 warn qq/$other_class->has_many( '$table_relname' => '$table_class',/
287 $other_class->has_many( $table_relname => $table_class, $col);
290 # not a class method, just a helper for cond_rel XXX
291 sub _stringify_hash {
295 join(q{, }, map("$_ => $href->{$_}", keys %$href))
299 # Set up a complex relation based on a hashref condition
301 my ( $self, $table, $other, $cond ) = @_;
303 my $table_class = $self->classes->{$table};
304 my $other_class = $self->classes->{$other};
305 my $table_relname = $self->_inflect_relname(lc $table);
306 my $other_relname = lc $other;
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};
315 my $rev_cond = { reverse %$cond };
317 for (keys %$rev_cond) {
318 $rev_cond->{"foreign.$_"} = "self.".$rev_cond->{$_};
319 delete $rev_cond->{$_};
322 my $cond_printable = _stringify_hash($cond)
324 my $rev_cond_printable = _stringify_hash($rev_cond)
327 warn qq/\# Belongs_to relationship\n/ if $self->debug;
329 warn qq/$table_class->belongs_to( '$other_relname' => '$other_class',/
330 . qq/$cond_printable);\n\n/
333 $table_class->belongs_to( $other_relname => $other_class, $cond);
335 warn qq/\# Has_many relationship\n/ if $self->debug;
337 warn qq/$other_class->has_many( '$table_relname' => '$table_class',/
338 . qq/$rev_cond_printable);\n\n/
342 $other_class->has_many( $table_relname => $table_class, $rev_cond);
350 $_->require or croak ($_ . "->require: $@");
351 eval "package $target; use $_;";
352 croak "use $_: $@" if $@;
359 my $schema = $self->schema;
362 $_->require or croak ($_ . "->require: $@");
363 $schema->inject_base($target, $_);
367 # Load and setup classes
371 my @db_classes = $self->_db_classes();
372 my $schema = $self->schema;
374 my $constraint = $self->constraint;
375 my $exclude = $self->exclude;
376 my @tables = sort grep
377 { /$constraint/ && (!$exclude || ! /$exclude/) }
380 $self->{_tables} = \@tables;
382 foreach my $table (@tables) {
383 my ($db_schema, $tbl) = split /\./, $table;
385 $table = $self->drop_db_schema ? $tbl : $table;
387 my $lc_table = lc $table;
389 my $table_moniker = $self->_table2moniker($db_schema, $tbl);
390 my $table_class = $schema . q{::} . $table_moniker;
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;
397 no warnings 'redefine';
398 local *Class::C3::reinitialize = sub { };
402 @{"${table_class}::ISA"} = qw/DBIx::Class/;
404 $self->_use ($table_class, @{$self->additional_classes});
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};
409 $self->_inject($table_class, @{$self->left_base_classes});
412 Class::C3::reinitialize;
414 foreach my $table (@tables) {
415 my $table_class = $self->classes->{$table};
416 my $table_moniker = $self->monikers->{$table};
418 warn qq/\# Initializing table "$table" as "$table_class"\n/
420 $table_class->table($table);
422 my ( $cols, $pks ) = $self->_table_info($table);
423 carp("$table has no primary key") unless @$pks;
424 $table_class->add_columns(@$cols);
425 $table_class->set_primary_key(@$pks) if @$pks;
427 warn qq/$table_class->table('$table');\n/ if $self->debug;
428 my $columns = join "', '", @$cols;
429 warn qq/$table_class->add_columns('$columns')\n/ if $self->debug;
430 my $primaries = join "', '", @$pks;
431 warn qq/$table_class->set_primary_key('$primaries')\n/
432 if $self->debug && @$pks;
434 $schema->register_class($table_moniker, $table_class);
440 Returns a sorted list of loaded tables, using the original database table
443 my @tables = $schema->loader->tables;
450 return @{$self->_tables};
453 # Find and setup relationships
454 sub _load_relationships {
457 my $dbh = $self->schema->storage->dbh;
458 my $quoter = $dbh->get_info(29) || q{"};
459 foreach my $table ( $self->tables ) {
461 my $sth = $dbh->foreign_key_info( '',
462 $self->db_schema, '', '', '', $table );
464 while(my $raw_rel = $sth->fetchrow_hashref) {
465 my $uk_tbl = $raw_rel->{UK_TABLE_NAME};
466 my $uk_col = lc $raw_rel->{UK_COLUMN_NAME};
467 my $fk_col = lc $raw_rel->{FK_COLUMN_NAME};
468 my $relid = $raw_rel->{UK_NAME};
469 $uk_tbl =~ s/$quoter//g;
470 $uk_col =~ s/$quoter//g;
471 $fk_col =~ s/$quoter//g;
472 $relid =~ s/$quoter//g;
473 $rels->{$relid}->{tbl} = $uk_tbl;
474 $rels->{$relid}->{cols}->{$uk_col} = $fk_col;
477 foreach my $relid (keys %$rels) {
478 my $reltbl = $rels->{$relid}->{tbl};
479 my $cond = $rels->{$relid}->{cols};
480 eval { $self->_make_cond_rel( $table, $reltbl, $cond ) };
481 warn qq/\# belongs_to_many failed "$@"\n\n/
482 if $@ && $self->debug;
487 # Make a moniker from a table
489 my ( $self, $db_schema, $table ) = @_;
494 $db_schema = ucfirst lc $db_schema;
495 $db_schema_ns = $db_schema if(!$self->drop_db_schema);
502 if( ref $self->moniker_map eq 'HASH' ) {
503 $moniker = $self->moniker_map->{$table};
505 elsif( ref $self->moniker_map eq 'CODE' ) {
506 $moniker = $self->moniker_map->($table);
509 $moniker ||= join '', map ucfirst, split /[\W_]+/, lc $table;
511 $moniker = $db_schema_ns ? $db_schema_ns . $moniker : $moniker;
516 # Overload in driver class
517 sub _tables_list { croak "ABSTRACT METHOD" }
519 sub _table_info { croak "ABSTRACT METHOD" }
523 Returns a hashref of loaded table-to-moniker mappings for the original
524 database table names. In cases where the database driver returns table
525 names as uppercase or mixed case, there will also be a duplicate entry
526 here in all lowercase. Best practice would be to use lower-case table
527 names when accessing this.
529 my $monikers = $schema->loader->monikers;
530 my $foo_tbl_moniker = $monikers->{foo_tbl};
532 my $foo_tbl_moniker = $schema->loader->monikers->{foo_tbl};
533 # $foo_tbl_moniker would look like "FooTbl"
537 Returns a hashref of table-to-classname mappings for the original database
538 table names. Same lowercase stuff as above applies here.
540 You probably shouldn't be using this for any normal or simple
541 usage of your Schema. The usual way to run queries on your tables is via
542 C<$schema-E<gt>resultset('FooTbl')>, where C<FooTbl> is a moniker as
543 returned by C<monikers> above.
545 my $classes = $schema->loader->classes;
546 my $foo_tbl_class = $classes->{foo_tbl};
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"
554 L<DBIx::Class::Schema::Loader>