Allow translation from parsers other than MySQL, Pg, and Oracle and just
Ken Youens-Clark [Wed, 25 Jun 2003 18:47:45 +0000 (18:47 +0000)]
set up table more manually than using the "set_up_table" method; no reason
to accept "$data" at beginning as it's not being sent anymore; no
longer using "$USER" or "$PASS" as they were only used once; removed
Class::DBI::Join as Michael Schwern (the module maintainer and Author
Emeritus of Class::DBI) says that Class::DBI handles this fine now
that this extra module will be leaving CPAN soon;  only create PK
accessor method if a callback was installed for it;  use
"$table->primary_key" to get PK rather than running through the
constraints to find it;  delay creation of output for each table until
after we've gone through everything so we can better set up the
"has_a" and "has_many" relationships;  intuit "has_many" as the
reverse of "has_a";  cleaned up "link tables" section, but commented
out as I'm not sure if my addition of "has_many" code might not do the
same thing.

lib/SQL/Translator/Producer/ClassDBI.pm

index 2232c55..4763772 100755 (executable)
@@ -1,7 +1,7 @@
 package SQL::Translator::Producer::ClassDBI;
 
 # -------------------------------------------------------------------
-# $Id: ClassDBI.pm,v 1.19 2003-06-25 02:04:33 allenday Exp $
+# $Id: ClassDBI.pm,v 1.20 2003-06-25 18:47:45 kycl4rk Exp $
 # -------------------------------------------------------------------
 # Copyright (C) 2003 Allen Day <allenday@ucla.edu>,
 #                    Ying Zhang <zyolive@yahoo.com>
@@ -23,197 +23,258 @@ package SQL::Translator::Producer::ClassDBI;
 
 use strict;
 use vars qw[ $VERSION $DEBUG ];
-$VERSION = sprintf "%d.%02d", q$Revision: 1.19 $ =~ /(\d+)\.(\d+)/;
+$VERSION = sprintf "%d.%02d", q$Revision: 1.20 $ =~ /(\d+)\.(\d+)/;
 $DEBUG   = 1 unless defined $DEBUG;
 
 use SQL::Translator::Schema::Constants;
 use SQL::Translator::Utils qw(header_comment);
 use Data::Dumper;
 
+my %CDBI_auto_pkgs = (
+    MySQL      => 'mysql',
+    PostgreSQL => 'Pg',
+    Oracle     => 'Oracle',
+);
+
+# -------------------------------------------------------------------
 sub produce {
-    my ($translator, $data) = @_;
-    local $DEBUG            = $translator->debug;
-    my $no_comments         = $translator->no_comments;
-    my $schema              = $translator->schema;
-       
-    my $create; 
-    $create .= header_comment(__PACKAGE__, "# ") unless ($no_comments);
-       
-    $create .= "package " . $translator->format_package_name('DBI'). ";\n\n";
-       
-       $create .= "use strict;\n\n";
-    $create .= "my \$USER = '';\n";
-    $create .= "my \$PASS = '';\n\n";
-       
-    my $from = _from($translator->parser_type());
-       
-    $create .= "use base 'Class::DBI::$from';\n\n" .
-        $translator->format_package_name('DBI') . 
-        "->set_db('Main', 'dbi:$from:_', \$USER, \$PASS);\n\n";
-       
+    my $translator    = shift;
+    local $DEBUG      = $translator->debug;
+    my $no_comments   = $translator->no_comments;
+    my $schema        = $translator->schema;
+    my $args          = $translator->producer_args;
+    my $db_user       = $args->{'db_user'} || '';
+    my $db_pass       = $args->{'db_pass'} || '';
+    my $dsn           = $args->{'dsn'}     || 'dbi:$from:_';
+    my $main_pkg_name = $translator->format_package_name('DBI');
+    my $header        = header_comment(__PACKAGE__, "# ");
+    my $sep           = '# ' . '-' x 67;
+
+    my $parser_type   = ( split /::/, $translator->parser_type )[-1];
+    my $from          = $CDBI_auto_pkgs{ $parser_type } || '';
+    
     #
     # Iterate over all tables
     #
+    my ( %packages, $order );
     for my $table ( $schema->get_tables ) {
         my $table_name = $table->name or next;
-        my %pk;
 
-        unless ( $no_comments ) {
-            $create .=
-                "#\n# Package: " .
-                $translator->format_package_name($table_name).
-                "\n#\n"
-        }
+        my $table_pkg_name = $translator->format_package_name($table_name);
+        $packages{ $table_pkg_name } = {
+            order     => ++$order,
+            pkg_name  => $table_pkg_name,
+            base      => $main_pkg_name,
+            table     => $table_name,
+        };
 
-        $create .= "package ".$translator->format_package_name($table_name).";\n\n";
-               $create .= "use strict;\n";
-               $create .= "use base qw(".$translator->format_package_name('DBI').");\n";
-        $create .= "use mixin 'Class::DBI::Join';\n";
-        $create .= "use Class::DBI::Pager;\n\n";
-               
-        $create .= $translator->format_package_name($table_name)."->set_up_table('$table_name');\n\n";
-               
         #
-        # Primary key?
+        # Primary key may have a differenct accessor method name
         #
-        foreach my $constraint ( $table->get_constraints ) {
-            next unless $constraint->type eq PRIMARY_KEY;
-            my $field = ($constraint->fields)[0];
-                       
-            $pk{ $table_name } = $field;
-            $create .= "sub " .$translator->format_pk_name(
-                $translator->format_package_name( $table_name ),
-                $field
-            ) . " { shift->".$field." }\n\n";
+        if ( my $pk_xform = $translator->format_pk_name ) {
+            if ( my $constraint = $table->primary_key ) {
+                my $field          = ($constraint->fields)[0];
+                my $pk_name        = $pk_xform->($table_pkg_name, $field);
+                
+                $packages{ $table_pkg_name }{'pk_accessor'} = 
+                    "#\n# Primary key accessor\n#\n".
+                    "sub $pk_name {\n    shift->$field\n}\n"
+                ;
+            }
         }
-               
+        
         #
-        # Find foreign keys
+        # Use foreign keys to set up "has_a/has_many" relationships.
         #
         foreach my $field ( $table->get_fields ) {
             if ( $field->is_foreign_key ) {
+                my $table_name = $table->name;
                 my $field_name = $field->name;
                 my $fk         = $field->foreign_key_reference;
                 my $ref_table  = $fk->reference_table;
-                my @ref_fields = $fk->reference_fields;
-                my @fields     = $fk->fields;
+                my $ref_pkg    = $translator->format_package_name($ref_table);
+                my $ref_fld    = 
+                    $translator->format_fk_name($ref_table, $field_name);
 
-              $create .= $translator->format_package_name($table_name). 
-                    "->hasa(\n    '" .
-                    $translator->format_package_name($ref_table). 
-                    "' => '$field_name'\n);\n\n";
-              $create .= "sub " .
-                    $translator->format_fk_name($ref_table, $field_name).
-                    " {\n    return shift->$field_name\n}\n\n";
+                push @{ $packages{ $table_pkg_name }{'has_a'} },
+                    "$table_pkg_name->has_a(\n".
+                    "    $field_name => '$ref_pkg'\n);\n\n".
+                    "sub $ref_fld {\n".
+                    "    return shift->$field_name\n}\n\n"
+                ;
 
+                #
+                # If this table "has a" to the other, then it follows 
+                # that the other table "has many" of this one, right?
+                #
+                push @{ $packages{ $ref_pkg }{'has_many'} },
+                    "$ref_pkg->has_many(\n    '$table_name', ".
+                    "'$table_pkg_name' => '$field_name'\n);\n\n"
+                ;
             }
         }
 
-               #
-               # Identify link tables, defined as tables that have only PK and FK
-               # fields
-               #
-               my %linkable;
-               my %linktable;
-               foreach my $table ($schema->get_tables) {
-                 my $is_link = 1;
-                 foreach my $field ($table->get_fields){
-                       $is_link = 0 and last unless $field->is_primary_key or $field->is_foreign_key;
-                 }
-                 
-                 if($is_link){
-                       foreach my $left ($table->get_fields){
-                         next unless $left->is_foreign_key and $schema->get_table($left->foreign_key_reference->reference_table);
-
-
-                         next unless $left->is_foreign_key and 
-                               $schema->get_table(
-                                                                  $left->foreign_key_reference->reference_table
-                                                                 )->get_field(
-                                                                                          ($left->foreign_key_reference->reference_fields)[0]
-                                                                                         )->is_primary_key;
-                         
-                         foreach my $right ($table->get_fields){
-                               #skip the diagonal
-                               next if $left->name eq $right->name;
-
-                               next unless $right->is_foreign_key and $schema->get_table($right->foreign_key_reference->reference_table);
-                               
-                               next unless $right->is_foreign_key and
-                                 $schema->get_table(
-                                                                        $right->foreign_key_reference->reference_table
-                                                                       )->get_field(
-                                                                                                ($right->foreign_key_reference->reference_fields)[0]
-                                                                                               )->is_primary_key;
-                               
-                               
-                               $linkable{$left->foreign_key_reference->reference_table}{$right->foreign_key_reference->reference_table} = $table;
-                               $linkable{$right->foreign_key_reference->reference_table}{$left->foreign_key_reference->reference_table} = $table;
-                               $linktable{$table->name} = $table;
-                               
-                         }
-                       }
-                 }
-                 
-               }
-
-
-               #
-               # Generate many-to-many linking methods for data tables
-               #
-               my $is_data = 0;
-               map {(!$_->is_foreign_key and !$_->is_primary_key) ? $is_data++ : 0} $table->get_fields;
-               my %linked;
-               if($is_data){
-                 foreach my $link (keys %{$linkable{$table->name}}){
-                       my $linkmethodname = "_".$translator->format_fk_name($table->name,$link)."_refs";
-
-
-                       $create .= $translator->format_package_name($table->name).
-                                 "->has_many('$linkmethodname','".
-                                 $translator->format_package_name($linkable{$table->name}{$link}->name)."','".
-                                 ($schema->get_table($link)->primary_key->fields)[0]."');\n";
-#                                $link."');\n";
-                       $create .= "sub ". $translator->format_fk_name($table,$link).
-                                 ### HARDCODED 's' HERE.  ADD CALLBACK FOR PLURALIZATION MANGLING
-                                 "s {\n    my \$self = shift; return map \$_->".$link.
-                                 ", \$self->".$linkmethodname.";\n}\n\n";
-                 }
-               }
-         }
-
-    $create .= '1;';
+        #
+        # Identify link tables, defined as tables that have 
+        # only PK and FK fields.
+        #
+#        my %linkable;
+#        my %linktable;
+#        foreach my $table ( $schema->get_tables ) {
+#            my $is_link = 1;
+#            foreach my $field ( $table->get_fields ) {
+#                unless ( $field->is_primary_key or $field->is_foreign_key ) {
+#                    $is_link = 0; 
+#                    last;
+#                }
+#            }
+#          
+#            if ( $is_link ) {
+#                foreach my $left ( $table->get_fields ) {
+#                    next unless $left->is_foreign_key and $schema->get_table(
+#                        $left->foreign_key_reference->reference_table
+#                    );
+#
+#                    next unless $left->is_foreign_key and $schema->get_table(
+#                        $left->foreign_key_reference->reference_table
+#                    )->get_field(
+#                        ($left->foreign_key_reference->reference_fields)[0]
+#                    )->is_primary_key;
+#              
+#                    foreach my $right ( $table->get_fields ) {
+#                        #skip the diagonal
+#                        next if $left->name eq $right->name;
+#
+#                        next unless $right->is_foreign_key and 
+#                            $schema->get_table(
+#                                $right->foreign_key_reference->reference_table
+#                            )
+#                        ;
+#                
+#                        next unless $right->is_foreign_key and
+#                            $schema->get_table(
+#                                $right->foreign_key_reference->reference_table
+#                            )->get_field(
+#                            ($right->foreign_key_reference->reference_fields)[0]
+#                            )->is_primary_key
+#                        ;
+#                
+#                
+#                        $linkable{
+#                            $left->foreign_key_reference->reference_table
+#                        }{
+#                            $right->foreign_key_reference->reference_table
+#                        } = $table;
+#
+#                        $linkable{
+#                            $right->foreign_key_reference->reference_table
+#                        }{
+#                            $left->foreign_key_reference->reference_table
+#                        } = $table;
+#
+#                        $linktable{ $table->name } = $table;
+#                    }
+#                }
+#            }
+#        }
+#
+#        #
+#        # Generate many-to-many linking methods for data tables
+#        #
+#        my $is_data = 0;
+#        for ( $table->get_fields ) {
+#            $is_data++ if !$_->is_foreign_key and !$_->is_primary_key;
+#        }
+#
+#        my %linked;
+#        if ( $is_data ) {
+#            foreach my $link ( keys %{ $linkable{ $table->name } } ) {
+#                my $linkmethodname = 
+#                    "_".$translator->format_fk_name($table->name,$link)."_refs"
+#                ;
+#
+#                $create .= $translator->format_package_name($table->name).
+#                    "->has_many('$linkmethodname','".
+#                    $translator->format_package_name(
+#                        $linkable{$table->name}{$link}->name
+#                    )."','".
+#                    ($schema->get_table($link)->primary_key->fields)[0]."');\n"
+#                ;
+#
+#                $create .= "sub ". $translator->format_fk_name($table,$link).
+#                    # HARDCODED 's' HERE.  
+#                    # ADD CALLBACK FOR PLURALIZATION MANGLING
+#                    "s {\n    my \$self = shift; return map \$_->".$link.
+#                    ", \$self->".$linkmethodname.";\n}\n\n"
+#                ;
+#            }
+#        }
+    }
 
-    return $create;
-}
+    my $base_pkg = sprintf( 'Class::DBI%s', $from ? "::$from" : '' );
+    my $create = join("\n",
+        "package $main_pkg_name;\n",
+        $header,
+        "use strict;",
+        "use base '$base_pkg';\n",
+        "$main_pkg_name->set_db('Main', '$dsn', '$db_user', '$db_pass');\n\n",
+    ); 
+
+    for my $pkg_name ( 
+        sort { $packages{ $a }{'order'} <=> $packages{ $b }{'order'} }
+        keys %packages
+    ) {
+        my $pkg = $packages{ $pkg_name };
+
+        $create .= join("\n",
+            $sep,
+            "package ".$pkg->{'pkg_name'}.";",
+            "use base '".$pkg->{'base'}."';",
+            "use Class::DBI::Pager;\n\n",
+        );    
 
+        if ( $from ) {
+            $create .= 
+                $pkg->{'pkg_name'}."->set_up_table('".$pkg->{'table'}."');\n\n";
+        }
+        else {
+            my $table       = $schema->get_table( $pkg->{'table'} );
+            my @field_names = map { $_->name } $table->get_fields;
+
+            $create .= join("\n",
+                $pkg_name."->table('".$pkg->{'table'}."');\n",
+                $pkg_name."->columns(All => qw/".
+                join(' ', @field_names)."/);\n\n",
+            );
+        }
 
-sub _from {
-    my $from = shift;
-    my @temp = split(/::/, $from);
-    $from    = $temp[$#temp];
+        if ( my $pk = $pkg->{'pk_accessor'} ) {
+            $create .= $pk;
+        }
 
-    if ( $from eq 'MySQL') {
-        $from = lc($from);
-    } elsif ( $from eq 'PostgreSQL') {
-        $from = 'Pg';
-    } elsif ( $from eq 'Oracle') {
-        $from = 'Oracle';
-    } else {
-        die "__PACKAGE__ can't handle vendor $from";
+        if ( my @has_a = @{ $pkg->{'has_a'} || [] } ) {
+            $create .= $_ for @has_a;
+        }
+
+        if ( my @has_many = @{ $pkg->{'has_many'} || [] } ) {
+            $create .= $_ for @has_many;
+        }
     }
 
-    return $from;
+    $create .= "1;\n";
+
+    return $create;
 }
 
 1;
 
-__END__
+# -------------------------------------------------------------------
+
+=pod
 
 =head1 NAME
 
-SQL::Translator::Producer::ClassDBI - 
-    Translate SQL schemata into Class::DBI classes
+SQL::Translator::Producer::ClassDBI - create Class::DBI classes from schema
 
 =head1 SYNOPSIS