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 {
170 my ( $class, $table, $column, $other, $other_column ) = @_;
171 my $table_class = $class->_find_table_class($table);
172 my $other_class = $class->_find_table_class($other);
174 warn qq/\# Belongs_to relationship\n/ if $class->debug_loader;
177 warn qq/$table_class->belongs_to( '$column' => '$other_class',/
178 . qq/ { "foreign.$other_column" => "self.$column" },/
179 . qq/ { accessor => 'filter' });\n\n/
180 if $class->debug_loader;
181 $table_class->belongs_to( $column => $other_class,
182 { "foreign.$other_column" => "self.$column" },
183 { accessor => 'filter' }
187 warn qq/$table_class->belongs_to( '$column' => '$other_class' );\n\n/
188 if $class->debug_loader;
189 $table_class->belongs_to( $column => $other_class );
192 my ($table_class_base) = $table_class =~ /.*::(.+)/;
193 my $plural = Lingua::EN::Inflect::PL( lc $table_class_base );
194 $plural = $class->loader_data->{_inflect}->{ lc $table_class_base }
195 if $class->loader_data->{_inflect}
196 and exists $class->loader_data->{_inflect}->{ lc $table_class_base };
198 warn qq/\# Has_many relationship\n/ if $class->debug_loader;
201 warn qq/$other_class->has_many( '$plural' => '$table_class',/
202 . qq/ { "foreign.$column" => "self.$other_column" } );\n\n/
203 if $class->debug_loader;
204 $other_class->has_many( $plural => $table_class,
205 { "foreign.$column" => "self.$other_column" }
209 warn qq/$other_class->has_many( '$plural' => '$table_class',/
210 . qq/'$other_column' );\n\n/
211 if $class->debug_loader;
212 $other_class->has_many( $plural => $table_class, $column );
216 # Load and setup classes
220 my @tables = $class->_tables();
221 my @db_classes = $class->_db_classes();
222 my $additional = join '', map "use $_;\n", @{ $class->loader_data->{_additional} };
223 my $additional_base = join '', map "use base '$_';\n",
224 @{ $class->loader_data->{_additional_base} };
225 my $left_base = join '', map "use base '$_';\n", @{ $class->loader_data->{_left_base} };
226 my $constraint = $class->loader_data->{_constraint};
227 my $exclude = $class->loader_data->{_exclude};
229 foreach my $table (@tables) {
230 next unless $table =~ /$constraint/;
231 next if ( defined $exclude && $table =~ /$exclude/ );
233 my ($db_schema, $tbl) = split /\./, $table;
234 my $tablename = lc $table;
236 $tablename = $class->loader_data->{_drop_db_schema} ? $tbl : lc $table;
239 my $table_moniker = $class->_table2moniker($db_schema, $tbl);
240 my $table_class = "$class\::$table_moniker";
242 $class->inject_base( $table_class, 'DBIx::Class::Core' );
243 $_->require for @db_classes;
244 $class->inject_base( $table_class, $_ ) for @db_classes;
245 warn qq/\# Initializing table "$tablename" as "$table_class"\n/ if $class->debug_loader;
246 $table_class->table(lc $tablename);
248 my ( $cols, $pks ) = $class->_table_info($table);
249 carp("$table has no primary key") unless @$pks;
250 $table_class->add_columns(@$cols);
251 $table_class->set_primary_key(@$pks) if @$pks;
253 my $code = "package $table_class;\n$additional_base$additional$left_base";
254 warn qq/$code/ if $class->debug_loader;
255 warn qq/$table_class->table('$tablename');\n/ if $class->debug_loader;
256 my $columns = join "', '", @$cols;
257 warn qq/$table_class->add_columns('$columns')\n/ if $class->debug_loader;
258 my $primaries = join "', '", @$pks;
259 warn qq/$table_class->set_primary_key('$primaries')\n/ if $class->debug_loader && @$pks;
261 croak qq/Couldn't load additional classes "$@"/ if $@;
262 unshift @{"$table_class\::ISA"}, $_ foreach ( @{ $class->loader_data->{_left_base} } );
264 $class->register_class($table_moniker, $table_class);
265 $class->loader_data->{TABLE_CLASSES}->{lc $tablename} = $table_class;
266 $class->loader_data->{MONIKERS}->{lc $tablename} = $table_moniker;
270 # Find and setup relationships
273 my $dbh = $class->storage->dbh;
274 foreach my $table ( $class->tables ) {
275 my $quoter = $dbh->get_info(29) || q{"};
276 if ( my $sth = $dbh->foreign_key_info( '', $class->loader_data->{_db_schema}, '', '', '', $table ) ) {
277 for my $res ( @{ $sth->fetchall_arrayref( {} ) } ) {
278 my $column = lc $res->{FK_COLUMN_NAME};
279 my $other = lc $res->{UK_TABLE_NAME};
280 my $other_column = lc $res->{UK_COLUMN_NAME};
281 $column =~ s/$quoter//g;
282 $other =~ s/$quoter//g;
283 $other_column =~ s/$quoter//g;
284 eval { $class->_belongs_to_many( $table, $column, $other,
286 warn qq/\# belongs_to_many failed "$@"\n\n/
287 if $@ && $class->debug_loader;
293 # Make a moniker from a table
295 my ( $class, $db_schema, $table ) = @_;
300 $db_schema = ucfirst lc $db_schema;
301 $db_schema_ns = $db_schema if(!$class->loader_data->{_drop_db_schema});
306 my $moniker = join '', map ucfirst, split /[\W_]+/, lc $table;
307 $moniker = $db_schema_ns ? $db_schema_ns . $moniker : $moniker;
312 # Overload in driver class
313 sub _tables { croak "ABSTRACT METHOD" }
315 sub _table_info { croak "ABSTRACT METHOD" }
319 L<DBIx::Class::Schema::Loader>