1 package DBIx::Class::Schema::Loader::Generic;
6 use base qw/DBIx::Class::Schema/;
9 use Lingua::EN::Inflect;
11 require DBIx::Class::Core;
13 __PACKAGE__->mk_classdata('loader_data');
17 DBIx::Class::Schema::Loader::Generic - Generic DBIx::Class::Schema::Loader Implementation.
21 See L<DBIx::Class::Schema::Loader>
27 Available constructor options are:
29 =head3 additional_base_classes
31 List of additional base classes your table classes will use.
33 =head3 left_base_classes
35 List of additional base classes, that need to be leftmost.
37 =head3 additional_classes
39 List of additional classes which your table classes will use.
43 Only load tables matching regex.
47 Exclude tables matching regex.
51 Enable debug messages.
63 Try to automatically detect/setup has_a and has_many relationships.
67 An hashref, which contains exceptions to Lingua::EN::Inflect::PL().
68 Useful for foreign language column names.
80 Not intended to be called directly. This is used internally by the
81 C<new()> method in L<DBIx::Class::Schema::Loader>.
85 sub _load_from_connection {
86 my ( $class, %args ) = @_;
89 *{"$class\::debug_loader"} = sub { 1 };
91 my $additional = $args{additional_classes} || [];
92 $additional = [$additional] unless ref $additional eq 'ARRAY';
93 my $additional_base = $args{additional_base_classes} || [];
94 $additional_base = [$additional_base]
95 unless ref $additional_base eq 'ARRAY';
96 my $left_base = $args{left_base_classes} || [];
97 $left_base = [$left_base] unless ref $left_base eq 'ARRAY';
100 [ $args{dsn}, $args{user}, $args{password}, $args{options} ],
101 _additional => $additional,
102 _additional_base => $additional_base,
103 _left_base => $left_base,
104 _constraint => $args{constraint} || '.*',
105 _exclude => $args{exclude},
106 _relationships => $args{relationships},
107 _inflect => $args{inflect},
108 _db_schema => $args{db_schema} || '',
109 _drop_db_schema => $args{drop_db_schema},
114 $class->connection(@{$class->loader_data->{_datasource}});
115 warn qq/\### START DBIx::Class::Schema::Loader dump ###\n/ if $class->debug_loader;
116 $class->_load_classes;
117 $class->_relationships if $class->loader_data->{_relationships};
118 warn qq/\### END DBIx::Class::Schema::Loader dump ###\n/ if $class->debug_loader;
119 $class->storage->dbh->disconnect; # XXX this should be ->storage->disconnect later?
124 # The original table class name during Loader,
125 sub _find_table_class {
126 my ( $class, $table ) = @_;
127 return $class->loader_data->{TABLE_CLASSES}->{$table};
130 # Returns the moniker for a given table name,
131 # for use in $conn->resultset($moniker)
135 Returns the moniker for a given literal table name. Used
136 as $schema->resultset($moniker), etc.
140 my ( $class, $table ) = @_;
141 return $class->loader_data->{MONIKERS}->{$table};
146 Overload to enable Loader debug messages.
150 sub debug_loader { 0 }
154 Returns a sorted list of tables.
156 my @tables = $loader->tables;
162 return sort keys %{ $class->loader_data->{MONIKERS} };
165 # Overload in your driver class
166 sub _db_classes { croak "ABSTRACT METHOD" }
168 # Setup has_a and has_many relationships
169 sub _belongs_to_many {
172 my ( $class, $table, $other, $cond ) = @_;
173 my $table_class = $class->_find_table_class($table);
174 my $other_class = $class->_find_table_class($other);
176 my $table_relname = lc $table;
177 my $other_relname = lc $other;
179 if(my $inflections = $class->loader_data->{_inflect}) {
180 $table_relname = $inflections->{$table_relname}
181 if exists $inflections->{$table_relname};
184 $table_relname = Lingua::EN::Inflect::PL($table_relname);
187 # for single-column case, set the relname to the column name,
188 # to make filter accessors work
189 if(scalar keys %$cond == 1) {
190 my ($col) = keys %$cond;
191 $other_relname = $cond->{$col};
194 my $rev_cond = { reverse %$cond };
196 warn qq/\# Belongs_to relationship\n/ if $class->debug_loader;
198 warn qq/$table_class->belongs_to( '$other_relname' => '$other_class',/
201 if $class->debug_loader;
203 $table_class->belongs_to( $other_relname => $other_class, $cond);
205 warn qq/\# Has_many relationship\n/ if $class->debug_loader;
207 warn qq/$other_class->has_many( '$table_relname' => '$table_class',/
210 if $class->debug_loader;
212 $other_class->has_many( $table_relname => $table_class, $rev_cond);
215 # Load and setup classes
219 my @tables = $class->_tables();
220 my @db_classes = $class->_db_classes();
221 my $additional = join '', map "use $_;\n", @{ $class->loader_data->{_additional} };
222 my $additional_base = join '', map "use base '$_';\n",
223 @{ $class->loader_data->{_additional_base} };
224 my $left_base = join '', map "use base '$_';\n", @{ $class->loader_data->{_left_base} };
225 my $constraint = $class->loader_data->{_constraint};
226 my $exclude = $class->loader_data->{_exclude};
228 foreach my $table (@tables) {
229 next unless $table =~ /$constraint/;
230 next if ( defined $exclude && $table =~ /$exclude/ );
232 my ($db_schema, $tbl) = split /\./, $table;
233 my $tablename = lc $table;
235 $tablename = $class->loader_data->{_drop_db_schema} ? $tbl : lc $table;
238 my $table_moniker = $class->_table2moniker($db_schema, $tbl);
239 my $table_class = "$class\::$table_moniker";
241 $class->inject_base( $table_class, 'DBIx::Class::Core' );
242 $_->require for @db_classes;
243 $class->inject_base( $table_class, $_ ) for @db_classes;
244 warn qq/\# Initializing table "$tablename" as "$table_class"\n/ if $class->debug_loader;
245 $table_class->table(lc $tablename);
247 my ( $cols, $pks ) = $class->_table_info($table);
248 carp("$table has no primary key") unless @$pks;
249 $table_class->add_columns(@$cols);
250 $table_class->set_primary_key(@$pks) if @$pks;
252 my $code = "package $table_class;\n$additional_base$additional$left_base";
253 warn qq/$code/ if $class->debug_loader;
254 warn qq/$table_class->table('$tablename');\n/ if $class->debug_loader;
255 my $columns = join "', '", @$cols;
256 warn qq/$table_class->add_columns('$columns')\n/ if $class->debug_loader;
257 my $primaries = join "', '", @$pks;
258 warn qq/$table_class->set_primary_key('$primaries')\n/ if $class->debug_loader && @$pks;
260 croak qq/Couldn't load additional classes "$@"/ if $@;
261 unshift @{"$table_class\::ISA"}, $_ foreach ( @{ $class->loader_data->{_left_base} } );
263 $class->register_class($table_moniker, $table_class);
264 $class->loader_data->{TABLE_CLASSES}->{lc $tablename} = $table_class;
265 $class->loader_data->{MONIKERS}->{lc $tablename} = $table_moniker;
269 # Find and setup relationships
272 my $dbh = $class->storage->dbh;
273 my $quoter = $dbh->get_info(29) || q{"};
274 foreach my $table ( $class->tables ) {
276 my $sth = $dbh->foreign_key_info( '',
277 $class->loader_data->{_db_schema}, '', '', '', $table );
279 while(my $raw_rel = $sth->fetchrow_hashref) {
280 my $uk_tbl = lc $raw_rel->{UK_TABLE_NAME};
281 my $uk_col = lc $raw_rel->{UK_COLUMN_NAME};
282 my $fk_col = lc $raw_rel->{FK_COLUMN_NAME};
283 $uk_tbl =~ s/$quoter//g;
284 $uk_col =~ s/$quoter//g;
285 $fk_col =~ s/$quoter//g;
286 $rels->{$uk_tbl}->{$uk_col} = $fk_col;
289 foreach my $reltbl (keys %$rels) {
290 my $cond = $rels->{$reltbl};
291 eval { $class->_belongs_to_many( $table, $reltbl, $cond ) };
292 warn qq/\# belongs_to_many failed "$@"\n\n/
293 if $@ && $class->debug_loader;
298 # Make a moniker from a table
300 my ( $class, $db_schema, $table ) = @_;
305 $db_schema = ucfirst lc $db_schema;
306 $db_schema_ns = $db_schema if(!$class->loader_data->{_drop_db_schema});
311 my $moniker = join '', map ucfirst, split /[\W_]+/, lc $table;
312 $moniker = $db_schema_ns ? $db_schema_ns . $moniker : $moniker;
317 # Overload in driver class
318 sub _tables { croak "ABSTRACT METHOD" }
320 sub _table_info { croak "ABSTRACT METHOD" }
324 L<DBIx::Class::Schema::Loader>