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');
15 # XXX convert all usage of $class/$self->debug to ->debug_loader
19 DBIx::Class::Schema::Loader::Generic - Generic DBIx::Class::Schema::Loader Implementation.
23 See L<DBIx::Class::Schema::Loader>
29 Available constructor options are:
31 =head3 additional_base_classes
33 List of additional base classes your table classes will use.
35 =head3 left_base_classes
37 List of additional base classes, that need to be leftmost.
39 =head3 additional_classes
41 List of additional classes which your table classes will use.
45 Only load tables matching regex.
49 Exclude tables matching regex.
53 Enable debug messages.
61 Namespace under which your table classes will be initialized.
69 Try to automatically detect/setup has_a and has_many relationships.
73 An hashref, which contains exceptions to Lingua::EN::Inflect::PL().
74 Useful for foreign language column names.
86 Not intended to be called directly. This is used internally by the
87 C<new()> method in L<DBIx::Class::Schema::Loader>.
91 sub _load_from_connection {
92 my ( $class, %args ) = @_;
95 *{"$class\::debug_loader"} = sub { 1 };
97 my $additional = $args{additional_classes} || [];
98 $additional = [$additional] unless ref $additional eq 'ARRAY';
99 my $additional_base = $args{additional_base_classes} || [];
100 $additional_base = [$additional_base]
101 unless ref $additional_base eq 'ARRAY';
102 my $left_base = $args{left_base_classes} || [];
103 $left_base = [$left_base] unless ref $left_base eq 'ARRAY';
104 $class->loader_data({
106 [ $args{dsn}, $args{user}, $args{password}, $args{options} ],
107 _namespace => $args{namespace} || $class,
108 _additional => $additional,
109 _additional_base => $additional_base,
110 _left_base => $left_base,
111 _constraint => $args{constraint} || '.*',
112 _exclude => $args{exclude},
113 _relationships => $args{relationships},
114 _inflect => $args{inflect},
115 _db_schema => $args{db_schema},
116 _drop_db_schema => $args{drop_db_schema},
121 $class->connection(@{$class->loader_data->{_datasource}});
122 warn qq/\### START DBIx::Class::Schema::Loader dump ###\n/ if $class->debug;
123 $class->_load_classes;
124 $class->_relationships if $class->loader_data->{_relationships};
125 warn qq/\### END DBIx::Class::Schema::Loader dump ###\n/ if $class->debug;
126 $class->storage->dbh->disconnect; # XXX this should be ->storage->disconnect later?
131 # The original table class name during Loader,
132 sub _find_table_class {
133 my ( $class, $table ) = @_;
134 return $class->loader_data->{TABLE_CLASSES}->{$table};
137 # Returns the moniker for a given table name,
138 # for use in $conn->resultset($moniker)
140 my ( $class, $table ) = @_;
141 return $class->loader_data->{MONIKERS}->{$table};
146 Overload to enable debug messages.
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;
177 warn qq/$table_class->belongs_to( '$column' => '$other_class',/
178 . qq/ { "foreign.$other_column" => "self.$column" },/
179 . qq/ { accessor => 'filter' });\n\n/
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/
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;
201 warn qq/$other_class->has_many( '$plural' => '$table_class',/
202 . qq/ { "foreign.$column" => "self.$other_column" } );\n\n/
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/
212 $other_class->has_many( $plural => $table_class, $column );
216 # Load and setup classes
220 my $namespace = $class->loader_data->{_namespace};
222 my @tables = $class->_tables();
223 my @db_classes = $class->_db_classes();
224 my $additional = join '', map "use $_;\n", @{ $class->loader_data->{_additional} };
225 my $additional_base = join '', map "use base '$_';\n",
226 @{ $class->loader_data->{_additional_base} };
227 my $left_base = join '', map "use base '$_';\n", @{ $class->loader_data->{_left_base} };
228 my $constraint = $class->loader_data->{_constraint};
229 my $exclude = $class->loader_data->{_exclude};
231 foreach my $table (@tables) {
232 next unless $table =~ /$constraint/;
233 next if ( defined $exclude && $table =~ /$exclude/ );
235 my $table = lc $table;
236 my $table_name_db_schema = $table;
237 my $table_name_only = $table_name_db_schema;
238 my ($db_schema, $tbl) = split /\./, $table;
240 $table_name_db_schema = $tbl if $class->loader_data->{_drop_db_schema};
241 $table_name_only = $tbl;
247 my $table_subclass = $class->_table2subclass($db_schema, $table_name_only);
248 my $table_class = $namespace . '::' . $table_subclass;
250 $class->inject_base( $table_class, 'DBIx::Class::Core' );
251 $_->require for @db_classes;
252 $class->inject_base( $table_class, $_ ) for @db_classes;
253 warn qq/\# Initializing table "$table_name_db_schema" as "$table_class"\n/ if $class->debug;
254 $table_class->table(lc $table_name_db_schema);
256 my ( $cols, $pks ) = $class->_table_info($table_name_db_schema);
257 carp("$table has no primary key") unless @$pks;
258 $table_class->add_columns(@$cols);
259 $table_class->set_primary_key(@$pks) if @$pks;
261 my $code = "package $table_class;\n$additional_base$additional$left_base";
262 warn qq/$code/ if $class->debug;
263 warn qq/$table_class->table('$table_name_db_schema');\n/ if $class->debug;
264 my $columns = join "', '", @$cols;
265 warn qq/$table_class->add_columns('$columns')\n/ if $class->debug;
266 my $primaries = join "', '", @$pks;
267 warn qq/$table_class->set_primary_key('$primaries')\n/ if $class->debug && @$pks;
269 croak qq/Couldn't load additional classes "$@"/ if $@;
270 unshift @{"$table_class\::ISA"}, $_ foreach ( @{ $class->loader_data->{_left_base} } );
272 $class->register_class($table_subclass, $table_class);
273 $class->loader_data->{TABLE_CLASSES}->{$table_name_db_schema} = $table_class;
274 $class->loader_data->{MONIKERS}->{$table_name_db_schema} = $table_subclass;
278 # Find and setup relationships
281 my $dbh = $class->storage->dbh;
282 foreach my $table ( $class->tables ) {
283 my $quoter = $dbh->get_info(29) || q{"};
284 if ( my $sth = $dbh->foreign_key_info( '', '', '', '', '', $table ) ) {
285 for my $res ( @{ $sth->fetchall_arrayref( {} ) } ) {
286 my $column = $res->{FK_COLUMN_NAME};
287 my $other = $res->{UK_TABLE_NAME};
288 my $other_column = $res->{UK_COLUMN_NAME};
289 $column =~ s/$quoter//g;
290 $other =~ s/$quoter//g;
291 $other_column =~ s/$quoter//g;
292 eval { $class->_belongs_to_many( $table, $column, $other,
294 warn qq/\# belongs_to_many failed "$@"\n\n/
295 if $@ && $class->debug;
301 # Make a subclass (dbix moniker) from a table
302 sub _table2subclass {
303 my ( $class, $db_schema, $table ) = @_;
305 my $table_subclass = join '', map ucfirst, split /[\W_]+/, $table;
307 if($db_schema && !$class->loader_data->{_drop_db_schema}) {
308 $table_subclass = (ucfirst lc $db_schema) . '-' . $table_subclass;
314 # Overload in driver class
315 sub _tables { croak "ABSTRACT METHOD" }
317 sub _table_info { croak "ABSTRACT METHOD" }
321 L<DBIx::Class::Schema::Loader>