0.01003 - fixed has_many cond rels
[dbsrgits/DBIx-Class-Schema-Loader.git] / lib / DBIx / Class / Schema / Loader / Generic.pm
CommitLineData
18fca96a 1package DBIx::Class::Schema::Loader::Generic;
a78e3fed 2
3use strict;
a4a19f3c 4use warnings;
457eb8a6 5use base qw/Class::Accessor::Fast/;
2a4b8262 6use Class::C3;
a78e3fed 7use Carp;
8use Lingua::EN::Inflect;
a78e3fed 9require DBIx::Class::Core;
a4a19f3c 10
3980d69c 11# The first group are all arguments which are may be defaulted within,
12# The last two (classes, monikers) are generated locally:
13
14__PACKAGE__->mk_ro_accessors(qw/
15 schema
16 dsn
17 user
18 password
19 options
20 exclude
21 constraint
22 additional_classes
23 additional_base_classes
24 left_base_classes
25 relationships
26 inflect
27 db_schema
28 drop_db_schema
29 debug
30
31 classes
32 monikers
33 /);
a4a19f3c 34
a78e3fed 35=head1 NAME
36
18fca96a 37DBIx::Class::Schema::Loader::Generic - Generic DBIx::Class::Schema::Loader Implementation.
a78e3fed 38
39=head1 SYNOPSIS
40
18fca96a 41See L<DBIx::Class::Schema::Loader>
a78e3fed 42
43=head1 DESCRIPTION
44
457eb8a6 45This is the base class for the vendor-specific C<DBIx::Class::Schema::*>
46classes, and implements the common functionality between them.
47
48=head1 OPTIONS
a78e3fed 49
50Available constructor options are:
51
457eb8a6 52=head2 additional_base_classes
a78e3fed 53
54List of additional base classes your table classes will use.
55
457eb8a6 56=head2 left_base_classes
a78e3fed 57
58List of additional base classes, that need to be leftmost.
59
457eb8a6 60=head2 additional_classes
a78e3fed 61
62List of additional classes which your table classes will use.
63
457eb8a6 64=head2 constraint
a78e3fed 65
66Only load tables matching regex.
67
457eb8a6 68=head2 exclude
a78e3fed 69
70Exclude tables matching regex.
71
457eb8a6 72=head2 debug
a78e3fed 73
74Enable debug messages.
75
457eb8a6 76=head2 dsn
a78e3fed 77
78DBI Data Source Name.
79
457eb8a6 80=head2 password
a78e3fed 81
82Password.
83
457eb8a6 84=head2 relationships
a78e3fed 85
86Try to automatically detect/setup has_a and has_many relationships.
87
457eb8a6 88=head2 inflect
a78e3fed 89
90An hashref, which contains exceptions to Lingua::EN::Inflect::PL().
91Useful for foreign language column names.
92
457eb8a6 93=head2 user
a78e3fed 94
95Username.
96
457eb8a6 97=head1 METHODS
2a4b8262 98
a78e3fed 99=cut
100
3980d69c 101# ensure that a peice of object data is a valid arrayref, creating
102# an empty one or encapsulating whatever's there.
103sub _ensure_arrayref {
104 my $self = shift;
e26a4023 105
3980d69c 106 foreach (@_) {
107 $self->{$_} ||= [];
108 $self->{$_} = [ $self->{$_} ]
109 unless ref $self->{$_} eq 'ARRAY';
110 }
a78e3fed 111}
112
457eb8a6 113=head2 new
114
115Constructor for L<DBIx::Class::Schema::Loader::Generic>, used internally
116by L<DBIx::Class::Schema::Loader>.
117
118=cut
119
3980d69c 120sub new {
121 my ( $class, %args ) = @_;
a78e3fed 122
3980d69c 123 my $self = { %args };
fbd83464 124
3980d69c 125 bless $self => $class;
fbd83464 126
3980d69c 127 $self->{db_schema} ||= '';
128 $self->{constraint} ||= '.*';
129 $self->{inflect} ||= {};
130 $self->_ensure_arrayref(qw/additional_classes
131 additional_base_classes
132 left_base_classes/);
fbd83464 133
3980d69c 134 $self->{monikers} = {};
135 $self->{classes} = {};
a78e3fed 136
2a4b8262 137 $self;
138}
139
457eb8a6 140=head2 load
141
142Does the actual schema-construction work, used internally by
143L<DBIx::Class::Schema::Loader> right after object construction.
144
145=cut
146
2a4b8262 147sub load {
148 my $self = shift;
149
3980d69c 150 $self->schema->connection($self->dsn, $self->user,
151 $self->password, $self->options);
a78e3fed 152
3980d69c 153 warn qq/\### START DBIx::Class::Schema::Loader dump ###\n/
154 if $self->debug;
a78e3fed 155
3980d69c 156 $self->_load_classes;
157 $self->_load_relationships if $self->relationships;
a78e3fed 158
3980d69c 159 warn qq/\### END DBIx::Class::Schema::Loader dump ###\n/
160 if $self->debug;
52bf3f26 161 $self->schema->storage->disconnect;
a78e3fed 162
3980d69c 163 $self;
a78e3fed 164}
165
166# Overload in your driver class
3980d69c 167sub _db_classes { croak "ABSTRACT METHOD" }
66742793 168
16f6b6ac 169# Inflect a relationship name
170# XXX (should pluralize, but currently also tends to de-pluralize plurals)
3980d69c 171sub _inflect_relname {
172 my ($self, $relname) = @_;
708c0939 173
3980d69c 174 return $self->inflect->{$relname} if exists $self->inflect->{$relname};
175 return Lingua::EN::Inflect::PL($relname);
16f6b6ac 176}
a78e3fed 177
16f6b6ac 178# Set up a simple relation with just a local col and foreign table
3980d69c 179sub _make_simple_rel {
180 my ($self, $table, $other, $col) = @_;
708c0939 181
3980d69c 182 my $table_class = $self->classes->{$table};
183 my $other_class = $self->classes->{$other};
184 my $table_relname = $self->_inflect_relname(lc $table);
66742793 185
3980d69c 186 warn qq/\# Belongs_to relationship\n/ if $self->debug;
16f6b6ac 187 warn qq/$table_class->belongs_to( '$col' => '$other_class' );\n\n/
3980d69c 188 if $self->debug;
16f6b6ac 189 $table_class->belongs_to( $col => $other_class );
708c0939 190
3980d69c 191 warn qq/\# Has_many relationship\n/ if $self->debug;
16f6b6ac 192 warn qq/$other_class->has_many( '$table_relname' => '$table_class',/
193 . qq/$col);\n\n/
3980d69c 194 if $self->debug;
708c0939 195
16f6b6ac 196 $other_class->has_many( $table_relname => $table_class, $col);
197}
a78e3fed 198
3980d69c 199# not a class method, just a helper for cond_rel XXX
200sub _stringify_hash {
201 my $href = shift;
202
203 return '{ ' .
204 join(q{, }, map("$_ => $href->{$_}", keys %$href))
205 . ' }';
206}
207
16f6b6ac 208# Set up a complex relation based on a hashref condition
3980d69c 209sub _make_cond_rel {
210 my ( $self, $table, $other, $cond ) = @_;
a78e3fed 211
3980d69c 212 my $table_class = $self->classes->{$table};
213 my $other_class = $self->classes->{$other};
214 my $table_relname = $self->_inflect_relname(lc $table);
16f6b6ac 215 my $other_relname = lc $other;
708c0939 216
16f6b6ac 217 # for single-column case, set the relname to the column name,
218 # to make filter accessors work
219 if(scalar keys %$cond == 1) {
220 my ($col) = keys %$cond;
221 $other_relname = $cond->{$col};
4ce22656 222 }
16f6b6ac 223
224 my $rev_cond = { reverse %$cond };
225
c5de0b0c 226 for (keys %$rev_cond) {
227 $rev_cond->{"foreign.$_"} = "self.".$rev_cond->{$_};
228 delete $rev_cond->{$_};
229 }
230
3980d69c 231 my $cond_printable = _stringify_hash($cond)
232 if $self->debug;
233 my $rev_cond_printable = _stringify_hash($rev_cond)
234 if $self->debug;
16f6b6ac 235
3980d69c 236 warn qq/\# Belongs_to relationship\n/ if $self->debug;
16f6b6ac 237
238 warn qq/$table_class->belongs_to( '$other_relname' => '$other_class',/
239 . qq/$cond_printable);\n\n/
3980d69c 240 if $self->debug;
16f6b6ac 241
242 $table_class->belongs_to( $other_relname => $other_class, $cond);
243
3980d69c 244 warn qq/\# Has_many relationship\n/ if $self->debug;
16f6b6ac 245
246 warn qq/$other_class->has_many( '$table_relname' => '$table_class',/
247 . qq/$rev_cond_printable);\n\n/
248 . qq/);\n\n/
3980d69c 249 if $self->debug;
16f6b6ac 250
251 $other_class->has_many( $table_relname => $table_class, $rev_cond);
a78e3fed 252}
253
42c0680e 254sub _use {
255 my $self = shift;
256 my $target = shift;
257
258 foreach (@_) {
259 $_->require or croak ($_ . "->require: $@");
260 eval "package $target; use $_;";
261 croak "use $_: $@" if $@;
262 }
263}
264
265sub _inject {
266 my $self = shift;
267 my $target = shift;
268 my $schema = $self->schema;
269
270 foreach (@_) {
271 $_->require or croak ($_ . "->require: $@");
272 $schema->inject_base($target, $_);
273 }
274}
275
a78e3fed 276# Load and setup classes
3980d69c 277sub _load_classes {
278 my $self = shift;
af6c2665 279
3980d69c 280 my @tables = $self->_tables();
281 my @db_classes = $self->_db_classes();
282 my $schema = $self->schema;
a78e3fed 283
a78e3fed 284 foreach my $table (@tables) {
3980d69c 285 my $constraint = $self->constraint;
286 my $exclude = $self->exclude;
287
288 next unless $table =~ /$constraint/;
289 next if defined $exclude && $table =~ /$exclude/;
af6c2665 290
af6c2665 291 my ($db_schema, $tbl) = split /\./, $table;
af96f52e 292 my $tablename = lc $table;
a78e3fed 293 if($tbl) {
3980d69c 294 $tablename = $self->drop_db_schema ? $tbl : lc $table;
af6c2665 295 }
3980d69c 296 my $lc_tblname = lc $tablename;
af6c2665 297
3980d69c 298 my $table_moniker = $self->_table2moniker($db_schema, $tbl);
299 my $table_class = $schema . q{::} . $table_moniker;
af6c2665 300
42c0680e 301 $self->_inject($table_class, 'DBIx::Class::Core');
302 $self->_inject($table_class, @db_classes);
303 $self->_inject($table_class, @{$self->additional_base_classes});
304 $self->_use ($table_class, @{$self->additional_classes});
305 $self->_inject($table_class, @{$self->left_base_classes});
3980d69c 306
307 warn qq/\# Initializing table "$tablename" as "$table_class"\n/
308 if $self->debug;
e26a4023 309 $table_class->table($lc_tblname);
af6c2665 310
3980d69c 311 my ( $cols, $pks ) = $self->_table_info($table);
a78e3fed 312 carp("$table has no primary key") unless @$pks;
a4a19f3c 313 $table_class->add_columns(@$cols);
314 $table_class->set_primary_key(@$pks) if @$pks;
af6c2665 315
3980d69c 316 warn qq/$table_class->table('$tablename');\n/ if $self->debug;
a78e3fed 317 my $columns = join "', '", @$cols;
3980d69c 318 warn qq/$table_class->add_columns('$columns')\n/ if $self->debug;
a78e3fed 319 my $primaries = join "', '", @$pks;
3980d69c 320 warn qq/$table_class->set_primary_key('$primaries')\n/
321 if $self->debug && @$pks;
af6c2665 322
3980d69c 323 $schema->register_class($table_moniker, $table_class);
324 $self->classes->{$lc_tblname} = $table_class;
325 $self->monikers->{$lc_tblname} = $table_moniker;
a78e3fed 326 }
327}
328
457eb8a6 329=head2 tables
3980d69c 330
8a6b44ef 331Returns a sorted list of loaded tables, using the original database table
332names. Actually generated from the keys of the C<monikers> hash below.
3980d69c 333
457eb8a6 334 my @tables = $schema->loader->tables;
3980d69c 335
336=cut
337
338sub tables {
339 my $self = shift;
340
341 return sort keys %{ $self->monikers };
342}
343
a78e3fed 344# Find and setup relationships
3980d69c 345sub _load_relationships {
346 my $self = shift;
347
348 my $dbh = $self->schema->storage->dbh;
708c0939 349 my $quoter = $dbh->get_info(29) || q{"};
3980d69c 350 foreach my $table ( $self->tables ) {
708c0939 351 my $rels = {};
352 my $sth = $dbh->foreign_key_info( '',
3980d69c 353 $self->db_schema, '', '', '', $table );
708c0939 354 next if !$sth;
355 while(my $raw_rel = $sth->fetchrow_hashref) {
356 my $uk_tbl = lc $raw_rel->{UK_TABLE_NAME};
357 my $uk_col = lc $raw_rel->{UK_COLUMN_NAME};
358 my $fk_col = lc $raw_rel->{FK_COLUMN_NAME};
4ce22656 359 my $relid = lc $raw_rel->{UK_NAME};
708c0939 360 $uk_tbl =~ s/$quoter//g;
361 $uk_col =~ s/$quoter//g;
362 $fk_col =~ s/$quoter//g;
4ce22656 363 $relid =~ s/$quoter//g;
364 $rels->{$relid}->{tbl} = $uk_tbl;
365 $rels->{$relid}->{cols}->{$uk_col} = $fk_col;
708c0939 366 }
367
4ce22656 368 foreach my $relid (keys %$rels) {
369 my $reltbl = $rels->{$relid}->{tbl};
370 my $cond = $rels->{$relid}->{cols};
3980d69c 371 eval { $self->_make_cond_rel( $table, $reltbl, $cond ) };
708c0939 372 warn qq/\# belongs_to_many failed "$@"\n\n/
3980d69c 373 if $@ && $self->debug;
a78e3fed 374 }
375 }
376}
377
65644119 378# Make a moniker from a table
3980d69c 379sub _table2moniker {
380 my ( $self, $db_schema, $table ) = @_;
af6c2665 381
af96f52e 382 my $db_schema_ns;
af6c2665 383
af96f52e 384 if($table) {
385 $db_schema = ucfirst lc $db_schema;
3980d69c 386 $db_schema_ns = $db_schema if(!$self->drop_db_schema);
af96f52e 387 } else {
388 $table = $db_schema;
a78e3fed 389 }
af6c2665 390
65644119 391 my $moniker = join '', map ucfirst, split /[\W_]+/, lc $table;
392 $moniker = $db_schema_ns ? $db_schema_ns . $moniker : $moniker;
af96f52e 393
65644119 394 return $moniker;
a78e3fed 395}
396
397# Overload in driver class
3980d69c 398sub _tables { croak "ABSTRACT METHOD" }
a78e3fed 399
3980d69c 400sub _table_info { croak "ABSTRACT METHOD" }
a78e3fed 401
457eb8a6 402=head2 monikers
403
404Returns a hashref of loaded table-to-moniker mappings for the original
405database table names.
406
407 my $monikers = $schema->loader->monikers;
408 my $foo_tbl_moniker = $monikers->{foo_tbl};
409 # -or-
410 my $foo_tbl_moniker = $schema->loader->monikers->{foo_tbl};
411 # $foo_tbl_moniker would look like "FooTbl"
412
413=head2 classes
414
415Returns a hashref of table-to-classname mappings for the original database
416table names. You probably shouldn't be using this for any normal or simple
417usage of your Schema. The usual way to run queries on your tables is via
418C<$schema-E<gt>resultset('FooTbl')>, where C<FooTbl> is a moniker as
419returned by C<monikers> above.
420
421 my $classes = $schema->loader->classes;
422 my $foo_tbl_class = $classes->{foo_tbl};
423 # -or-
424 my $foo_tbl_class = $schema->loader->classes->{foo_tbl};
425 # $foo_tbl_class would look like "My::Schema::FooTbl",
426 # assuming the schema class is "My::Schema"
427
a78e3fed 428=head1 SEE ALSO
429
18fca96a 430L<DBIx::Class::Schema::Loader>
a78e3fed 431
432=cut
433
4341;