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
38 DBIx::Class::Schema::Loader::Generic - Generic DBIx::Class::Schema::Loader Implementation.
42 See L<DBIx::Class::Schema::Loader>
46 This is the base class for the vendor-specific C<DBIx::Class::Schema::*>
47 classes, and implements the common functionality between them.
51 Available constructor options are:
55 Identical to the connect_info arguments to C<connect> and C<connection>
56 that are mentioned in L<DBIx::Class::Schema>.
58 An arrayref of connection information. For DBI-based Schemas,
61 connect_info => [ $dsn, $user, $pass, { AutoCommit => 1 } ],
63 =head2 additional_base_classes
65 List of additional base classes your table classes will use.
67 =head2 left_base_classes
69 List of additional base classes, that need to be leftmost.
71 =head2 additional_classes
73 List of additional classes which your table classes will use.
77 List of additional components to be loaded into your table classes.
78 A good example would be C<ResultSetManager>.
80 =head2 resultset_components
82 List of additional resultset components to be loaded into your table
83 classes. A good example would be C<AlwaysRS>. Component
84 C<ResultSetManager> will be automatically added to the above
85 C<components> list if this option is set.
89 Only load tables matching regex.
93 Exclude tables matching regex.
97 Enable debug messages.
101 Try to automatically detect/setup has_a and has_many relationships.
105 Overrides the default tablename -> moniker translation. Can be either
106 a hashref of table => moniker names, or a coderef for a translator
107 function taking a single scalar table name argument and returning
108 a scalar moniker. If the hash entry does not exist, or the function
109 returns a false/undef value, the code falls back to default behavior
114 Just like L</moniker_map> above, but for inflecting (pluralizing)
119 Deprecated. Equivalent to L</inflect_map>, but previously only took
120 a hashref argument, not a coderef. If you set C<inflect> to anything,
121 that setting will be copied to L</inflect_map>.
125 DEPRECATED, use L</connect_info> instead.
127 DBI Data Source Name.
131 DEPRECATED, use L</connect_info> instead.
137 DEPRECATED, use L</connect_info> instead.
143 DEPRECATED, use L</connect_info> instead.
145 DBI connection options hashref, like:
153 # ensure that a peice of object data is a valid arrayref, creating
154 # an empty one or encapsulating whatever's there.
155 sub _ensure_arrayref {
160 $self->{$_} = [ $self->{$_} ]
161 unless ref $self->{$_} eq 'ARRAY';
167 Constructor for L<DBIx::Class::Schema::Loader::Generic>, used internally
168 by L<DBIx::Class::Schema::Loader>.
173 my ( $class, %args ) = @_;
175 my $self = { %args };
177 bless $self => $class;
179 $self->{db_schema} ||= '';
180 $self->{constraint} ||= '.*';
181 $self->_ensure_arrayref(qw/additional_classes
182 additional_base_classes
188 push(@{$self->{components}}, 'ResultSetManager')
189 if @{$self->{resultset_components}};
191 $self->{monikers} = {};
192 $self->{classes} = {};
194 # Support deprecated argument name
195 $self->{inflect_map} ||= $self->{inflect};
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};
209 Does the actual schema-construction work, used internally by
210 L<DBIx::Class::Schema::Loader> right after object construction.
217 $self->schema->connection(@{$self->connect_info});
219 warn qq/\### START DBIx::Class::Schema::Loader dump ###\n/
222 $self->_load_classes;
223 $self->_load_relationships if $self->relationships;
224 $self->_load_external;
226 warn qq/\### END DBIx::Class::Schema::Loader dump ###\n/
228 $self->schema->storage->disconnect;
236 foreach my $table_class (values %{$self->classes}) {
237 $table_class->require;
238 if($@ && $@ !~ /^Can't locate /) {
239 croak "Failed to load external class definition"
240 . "for '$table_class': $@";
243 warn qq/# Loaded external class definition for '$table_class'\n/
249 # Overload in your driver class
250 sub _db_classes { croak "ABSTRACT METHOD" }
252 # Inflect a relationship name
253 sub _inflect_relname {
254 my ($self, $relname) = @_;
256 if( ref $self->{inflect_map} eq 'HASH' ) {
257 return $self->inflect_map->{$relname}
258 if exists $self->inflect_map->{$relname};
260 elsif( ref $self->{inflect_map} eq 'CODE' ) {
261 my $inflected = $self->inflect_map->($relname);
262 return $inflected if $inflected;
265 return Lingua::EN::Inflect::PL($relname);
268 # Set up a simple relation with just a local col and foreign table
269 sub _make_simple_rel {
270 my ($self, $table, $other, $col) = @_;
272 my $table_class = $self->classes->{$table};
273 my $other_class = $self->classes->{$other};
274 my $table_relname = $self->_inflect_relname(lc $table);
276 warn qq/\# Belongs_to relationship\n/ if $self->debug;
277 warn qq/$table_class->belongs_to( '$col' => '$other_class' );\n\n/
279 $table_class->belongs_to( $col => $other_class );
281 warn qq/\# Has_many relationship\n/ if $self->debug;
282 warn qq/$other_class->has_many( '$table_relname' => '$table_class',/
286 $other_class->has_many( $table_relname => $table_class, $col);
289 # not a class method, just a helper for cond_rel XXX
290 sub _stringify_hash {
294 join(q{, }, map("$_ => $href->{$_}", keys %$href))
298 # Set up a complex relation based on a hashref condition
300 my ( $self, $table, $other, $cond ) = @_;
302 my $table_class = $self->classes->{$table};
303 my $other_class = $self->classes->{$other};
304 my $table_relname = $self->_inflect_relname(lc $table);
305 my $other_relname = lc $other;
307 # for single-column case, set the relname to the column name,
308 # to make filter accessors work
309 if(scalar keys %$cond == 1) {
310 my ($col) = keys %$cond;
311 $other_relname = $cond->{$col};
314 my $rev_cond = { reverse %$cond };
316 for (keys %$rev_cond) {
317 $rev_cond->{"foreign.$_"} = "self.".$rev_cond->{$_};
318 delete $rev_cond->{$_};
321 my $cond_printable = _stringify_hash($cond)
323 my $rev_cond_printable = _stringify_hash($rev_cond)
326 warn qq/\# Belongs_to relationship\n/ if $self->debug;
328 warn qq/$table_class->belongs_to( '$other_relname' => '$other_class',/
329 . qq/$cond_printable);\n\n/
332 $table_class->belongs_to( $other_relname => $other_class, $cond);
334 warn qq/\# Has_many relationship\n/ if $self->debug;
336 warn qq/$other_class->has_many( '$table_relname' => '$table_class',/
337 . qq/$rev_cond_printable);\n\n/
341 $other_class->has_many( $table_relname => $table_class, $rev_cond);
349 $_->require or croak ($_ . "->require: $@");
350 eval "package $target; use $_;";
351 croak "use $_: $@" if $@;
358 my $schema = $self->schema;
361 $_->require or croak ($_ . "->require: $@");
362 $schema->inject_base($target, $_);
366 # Load and setup classes
370 my @tables = $self->_tables();
371 my @db_classes = $self->_db_classes();
372 my $schema = $self->schema;
374 foreach my $table (@tables) {
375 my $constraint = $self->constraint;
376 my $exclude = $self->exclude;
378 next unless $table =~ /$constraint/;
379 next if defined $exclude && $table =~ /$exclude/;
381 my ($db_schema, $tbl) = split /\./, $table;
382 my $tablename = lc $table;
384 $tablename = $self->drop_db_schema ? $tbl : lc $table;
386 my $lc_tblname = lc $tablename;
388 my $table_moniker = $self->_table2moniker($db_schema, $tbl);
389 my $table_class = $schema . q{::} . $table_moniker;
392 @{"${table_class}::ISA"} = qw/DBIx::Class/;
394 $self->_use ($table_class, @{$self->additional_classes});
395 $self->_inject($table_class, @{$self->additional_base_classes});
396 $table_class->load_components(@{$self->components}, @db_classes, 'Core');
397 $table_class->load_resultset_components(@{$self->resultset_components})
398 if @{$self->resultset_components};
399 $self->_inject($table_class, @{$self->left_base_classes});
401 warn qq/\# Initializing table "$tablename" as "$table_class"\n/
403 $table_class->table($lc_tblname);
405 my ( $cols, $pks ) = $self->_table_info($table);
406 carp("$table has no primary key") unless @$pks;
407 $table_class->add_columns(@$cols);
408 $table_class->set_primary_key(@$pks) if @$pks;
410 warn qq/$table_class->table('$tablename');\n/ if $self->debug;
411 my $columns = join "', '", @$cols;
412 warn qq/$table_class->add_columns('$columns')\n/ if $self->debug;
413 my $primaries = join "', '", @$pks;
414 warn qq/$table_class->set_primary_key('$primaries')\n/
415 if $self->debug && @$pks;
417 $schema->register_class($table_moniker, $table_class);
418 $self->classes->{$lc_tblname} = $table_class;
419 $self->monikers->{$lc_tblname} = $table_moniker;
425 Returns a sorted list of loaded tables, using the original database table
426 names. Actually generated from the keys of the C<monikers> hash below.
428 my @tables = $schema->loader->tables;
435 return sort keys %{ $self->monikers };
438 # Find and setup relationships
439 sub _load_relationships {
442 my $dbh = $self->schema->storage->dbh;
443 my $quoter = $dbh->get_info(29) || q{"};
444 foreach my $table ( $self->tables ) {
446 my $sth = $dbh->foreign_key_info( '',
447 $self->db_schema, '', '', '', $table );
449 while(my $raw_rel = $sth->fetchrow_hashref) {
450 my $uk_tbl = lc $raw_rel->{UK_TABLE_NAME};
451 my $uk_col = lc $raw_rel->{UK_COLUMN_NAME};
452 my $fk_col = lc $raw_rel->{FK_COLUMN_NAME};
453 my $relid = lc $raw_rel->{UK_NAME};
454 $uk_tbl =~ s/$quoter//g;
455 $uk_col =~ s/$quoter//g;
456 $fk_col =~ s/$quoter//g;
457 $relid =~ s/$quoter//g;
458 $rels->{$relid}->{tbl} = $uk_tbl;
459 $rels->{$relid}->{cols}->{$uk_col} = $fk_col;
462 foreach my $relid (keys %$rels) {
463 my $reltbl = $rels->{$relid}->{tbl};
464 my $cond = $rels->{$relid}->{cols};
465 eval { $self->_make_cond_rel( $table, $reltbl, $cond ) };
466 warn qq/\# belongs_to_many failed "$@"\n\n/
467 if $@ && $self->debug;
472 # Make a moniker from a table
474 my ( $self, $db_schema, $table ) = @_;
479 $db_schema = ucfirst lc $db_schema;
480 $db_schema_ns = $db_schema if(!$self->drop_db_schema);
487 if( ref $self->moniker_map eq 'HASH' ) {
488 $moniker = $self->moniker_map->{$table};
490 elsif( ref $self->moniker_map eq 'CODE' ) {
491 $moniker = $self->moniker_map->($table);
494 $moniker ||= join '', map ucfirst, split /[\W_]+/, lc $table;
496 $moniker = $db_schema_ns ? $db_schema_ns . $moniker : $moniker;
501 # Overload in driver class
502 sub _tables { croak "ABSTRACT METHOD" }
504 sub _table_info { croak "ABSTRACT METHOD" }
508 Returns a hashref of loaded table-to-moniker mappings for the original
509 database table names.
511 my $monikers = $schema->loader->monikers;
512 my $foo_tbl_moniker = $monikers->{foo_tbl};
514 my $foo_tbl_moniker = $schema->loader->monikers->{foo_tbl};
515 # $foo_tbl_moniker would look like "FooTbl"
519 Returns a hashref of table-to-classname mappings for the original database
520 table names. You probably shouldn't be using this for any normal or simple
521 usage of your Schema. The usual way to run queries on your tables is via
522 C<$schema-E<gt>resultset('FooTbl')>, where C<FooTbl> is a moniker as
523 returned by C<monikers> above.
525 my $classes = $schema->loader->classes;
526 my $foo_tbl_class = $classes->{foo_tbl};
528 my $foo_tbl_class = $schema->loader->classes->{foo_tbl};
529 # $foo_tbl_class would look like "My::Schema::FooTbl",
530 # assuming the schema class is "My::Schema"
534 L<DBIx::Class::Schema::Loader>