Efforts to re-enable Allen's many-to-many linktable code. I have no idea
[dbsrgits/SQL-Translator.git] / lib / SQL / Translator / Producer / ClassDBI.pm
CommitLineData
f42e7027 1package SQL::Translator::Producer::ClassDBI;
2
3# -------------------------------------------------------------------
fe77d758 4# $Id: ClassDBI.pm,v 1.25 2003-06-27 02:59:25 kycl4rk Exp $
f42e7027 5# -------------------------------------------------------------------
5f054727 6# Copyright (C) 2003 Allen Day <allenday@ucla.edu>,
7# Ying Zhang <zyolive@yahoo.com>
f42e7027 8#
9# This program is free software; you can redistribute it and/or
10# modify it under the terms of the GNU General Public License as
11# published by the Free Software Foundation; version 2.
12#
13# This program is distributed in the hope that it will be useful, but
14# WITHOUT ANY WARRANTY; without even the implied warranty of
15# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16# General Public License for more details.
17#
18# You should have received a copy of the GNU General Public License
19# along with this program; if not, write to the Free Software
20# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
21# 02111-1307 USA
22# -------------------------------------------------------------------
23
24use strict;
25use vars qw[ $VERSION $DEBUG ];
fe77d758 26$VERSION = sprintf "%d.%02d", q$Revision: 1.25 $ =~ /(\d+)\.(\d+)/;
f42e7027 27$DEBUG = 1 unless defined $DEBUG;
28
6c45056f 29use SQL::Translator::Schema::Constants;
5ee19df8 30use SQL::Translator::Utils qw(header_comment);
f42e7027 31use Data::Dumper;
32
bf4629e7 33my %CDBI_auto_pkgs = (
34 MySQL => 'mysql',
35 PostgreSQL => 'Pg',
36 Oracle => 'Oracle',
37);
38
39# -------------------------------------------------------------------
f42e7027 40sub produce {
390292d3 41 my $t = shift;
42 local $DEBUG = $t->debug;
43 my $no_comments = $t->no_comments;
44 my $schema = $t->schema;
45 my $args = $t->producer_args;
bf4629e7 46 my $db_user = $args->{'db_user'} || '';
47 my $db_pass = $args->{'db_pass'} || '';
390292d3 48 my $main_pkg_name = $t->format_package_name('DBI');
bf4629e7 49 my $header = header_comment(__PACKAGE__, "# ");
390292d3 50 my $parser_type = ( split /::/, $t->parser_type )[-1];
bf4629e7 51 my $from = $CDBI_auto_pkgs{ $parser_type } || '';
d8b3bc7e 52 my $dsn = $args->{'dsn'} || sprintf( 'dbi:%s:_',
53 $CDBI_auto_pkgs{ $parser_type }
54 ? $CDBI_auto_pkgs{ $parser_type } : $parser_type
55 );
56 my $sep = '# ' . '-' x 67;
fe77d758 57
58 #
59 # Identify "link tables" (have only PK and FK fields).
60 #
61 my %linkable;
62 my %linktable;
63 foreach my $table ( $schema->get_tables ) {
64 my $is_link = 1;
65 foreach my $field ( $table->get_fields ) {
66 unless ( $field->is_primary_key or $field->is_foreign_key ) {
67 $is_link = 0;
68 last;
69 }
70 }
71
72 next unless $is_link;
73
74 foreach my $left ( $table->get_fields ) {
75 next unless $left->is_foreign_key;
76 my $lfk = $left->foreign_key_reference or next;
77 my $lr_table = $schema->get_table( $lfk->reference_table )
78 or next;
79 my $lr_field_name = ($lfk->reference_fields)[0];
80 my $lr_field = $lr_table->get_field($lr_field_name);
81 next unless $lr_field->is_primary_key;
82
83 foreach my $right ( $table->get_fields ) {
84 next if $left->name eq $right->name;
85
86 my $rfk = $right->foreign_key_reference or next;
87 my $rr_table = $schema->get_table( $rfk->reference_table )
88 or next;
89 my $rr_field_name = ($rfk->reference_fields)[0];
90 my $rr_field = $rr_table->get_field($rr_field_name);
91 next unless $rr_field->is_primary_key;
92
93 $linkable{ $lr_table }{ $rr_table } = $table;
94 $linkable{ $rr_table }{ $lr_table } = $table;
95 $linktable{ $table->name } = $table;
96 }
97 }
98 }
bf4629e7 99
6c45056f 100 #
101 # Iterate over all tables
102 #
bf4629e7 103 my ( %packages, $order );
6c45056f 104 for my $table ( $schema->get_tables ) {
105 my $table_name = $table->name or next;
6c45056f 106
390292d3 107 my $table_pkg_name = $t->format_package_name($table_name);
bf4629e7 108 $packages{ $table_pkg_name } = {
109 order => ++$order,
110 pkg_name => $table_pkg_name,
111 base => $main_pkg_name,
112 table => $table_name,
113 };
6c45056f 114
6c45056f 115 #
bf4629e7 116 # Primary key may have a differenct accessor method name
6c45056f 117 #
390292d3 118 if ( my $pk_xform = $t->format_pk_name ) {
bf4629e7 119 if ( my $constraint = $table->primary_key ) {
120 my $field = ($constraint->fields)[0];
b75f2b12 121 my $pk_name = $pk_xform->($table_pkg_name, $field);
bf4629e7 122
123 $packages{ $table_pkg_name }{'pk_accessor'} =
124 "#\n# Primary key accessor\n#\n".
390292d3 125 "sub $pk_name {\n shift->$field\n}\n\n"
bf4629e7 126 ;
127 }
6c45056f 128 }
bf4629e7 129
6c45056f 130 #
bf4629e7 131 # Use foreign keys to set up "has_a/has_many" relationships.
6c45056f 132 #
fe77d758 133 my $is_data = 0;
6c45056f 134 foreach my $field ( $table->get_fields ) {
fe77d758 135 $is_data++ if !$field->is_foreign_key and !$field->is_primary_key;
6c45056f 136 if ( $field->is_foreign_key ) {
bf4629e7 137 my $table_name = $table->name;
6c45056f 138 my $field_name = $field->name;
390292d3 139 my $fk_method = $t->format_fk_name($table_name, $field_name);
6c45056f 140 my $fk = $field->foreign_key_reference;
141 my $ref_table = $fk->reference_table;
390292d3 142 my $ref_pkg = $t->format_package_name($ref_table);
143 my $ref_field = ($fk->reference_fields)[0];
6c45056f 144
bf4629e7 145 push @{ $packages{ $table_pkg_name }{'has_a'} },
146 "$table_pkg_name->has_a(\n".
147 " $field_name => '$ref_pkg'\n);\n\n".
390292d3 148 "sub $fk_method {\n".
bf4629e7 149 " return shift->$field_name\n}\n\n"
150 ;
52fbac6a 151
bf4629e7 152 #
153 # If this table "has a" to the other, then it follows
154 # that the other table "has many" of this one, right?
155 #
156 push @{ $packages{ $ref_pkg }{'has_many'} },
390292d3 157 "$ref_pkg->has_many(\n '${table_name}_${field_name}', ".
bf4629e7 158 "'$table_pkg_name' => '$field_name'\n);\n\n"
159 ;
6c45056f 160 }
161 }
df602cfb 162
fe77d758 163 my %linked;
164 if ( $is_data ) {
165 foreach my $link ( keys %{ $linkable{ $table_name } } ) {
166 my $linkmethodname =
167 "_".$t->format_fk_name($table->name,$link)."_refs"
168 ;
169
170 push @{ $packages{ $table_name }{'has_many'} },
171 "$table_pkg_name->has_many(\n ".
172 "'$linkmethodname', ".
173 $t->format_package_name(
174 $linkable{ $table->name }{ $link }->name
175 )."','".
176 ($schema->get_table($link)->primary_key->fields)[0].
177 "');\n\n"
178 ;
179
180 #
181 # I'm not sure what to do with this code. - ky
182 #
183
390292d3 184# $create .= "sub ". $t->format_fk_name($table,$link).
bf4629e7 185# # HARDCODED 's' HERE.
186# # ADD CALLBACK FOR PLURALIZATION MANGLING
187# "s {\n my \$self = shift; return map \$_->".$link.
188# ", \$self->".$linkmethodname.";\n}\n\n"
189# ;
fe77d758 190 }
191 }
bf4629e7 192 }
6c45056f 193
fe77d758 194 #
195 # Now build up text of package.
196 #
bf4629e7 197 my $base_pkg = sprintf( 'Class::DBI%s', $from ? "::$from" : '' );
198 my $create = join("\n",
199 "package $main_pkg_name;\n",
200 $header,
201 "use strict;",
202 "use base '$base_pkg';\n",
203 "$main_pkg_name->set_db('Main', '$dsn', '$db_user', '$db_pass');\n\n",
204 );
205
206 for my $pkg_name (
207 sort { $packages{ $a }{'order'} <=> $packages{ $b }{'order'} }
208 keys %packages
209 ) {
210 my $pkg = $packages{ $pkg_name };
211
212 $create .= join("\n",
213 $sep,
214 "package ".$pkg->{'pkg_name'}.";",
215 "use base '".$pkg->{'base'}."';",
216 "use Class::DBI::Pager;\n\n",
217 );
f42e7027 218
bf4629e7 219 if ( $from ) {
220 $create .=
221 $pkg->{'pkg_name'}."->set_up_table('".$pkg->{'table'}."');\n\n";
222 }
223 else {
224 my $table = $schema->get_table( $pkg->{'table'} );
225 my @field_names = map { $_->name } $table->get_fields;
226
227 $create .= join("\n",
228 $pkg_name."->table('".$pkg->{'table'}."');\n",
229 $pkg_name."->columns(All => qw/".
230 join(' ', @field_names)."/);\n\n",
231 );
232 }
52fbac6a 233
bf4629e7 234 if ( my $pk = $pkg->{'pk_accessor'} ) {
235 $create .= $pk;
236 }
df602cfb 237
bf4629e7 238 if ( my @has_a = @{ $pkg->{'has_a'} || [] } ) {
239 $create .= $_ for @has_a;
240 }
241
242 if ( my @has_many = @{ $pkg->{'has_many'} || [] } ) {
243 $create .= $_ for @has_many;
244 }
6c45056f 245 }
df602cfb 246
bf4629e7 247 $create .= "1;\n";
248
249 return $create;
f42e7027 250}
251
2521;
253
bf4629e7 254# -------------------------------------------------------------------
255
256=pod
f42e7027 257
258=head1 NAME
259
bf4629e7 260SQL::Translator::Producer::ClassDBI - create Class::DBI classes from schema
f42e7027 261
262=head1 SYNOPSIS
263
1eee27d3 264Use this producer as you would any other from SQL::Translator. See
265L<SQL::Translator> for details.
f42e7027 266
1eee27d3 267This package utilizes SQL::Translator's formatting methods
268format_package_name(), format_pk_name(), format_fk_name(), and
269format_table_name() as it creates classes, one per table in the schema
270provided. An additional base class is also created for database connectivity
271configuration. See L<Class::DBI> for details on how this works.
f42e7027 272
6c45056f 273=head1 AUTHORS
f42e7027 274
6c45056f 275Allen Day E<lt>allenday@ucla.eduE<gt>
5f054727 276Ying Zhang E<lt>zyolive@yahoo.comE<gt>,
6c45056f 277Ken Y. Clark E<lt>kclark@cpan.org<gt>.