rogueFalcon has asked for the wisdom of the Perl Monks concerning the following question:

Ok... I have a terrible way of making this work
The goal is to take a prospect object
and store it in a MySQL database.Is there a better way?

sub InsertProspect { # Get the new prospect my $prospect = shift; # Connect to the database my $dbh = DBI->connect('DBI:mysql:menagerie', 'menagerie') or die +"Couldn't connect to database: " . DBI->errstr; # Set all of the variables that we need to pass to MySQL my $name = $prospect->name; my $address = $prospect->address; my $address2 = $prospect->address2; my $state = $prospect->state; my $city = $prospect->city; my $phone = $prospect->phone; my $phone2 = $prospect->phone2; my $phone3 = $prospect->phone3; my $phone4 = $prospect->phone4; my $phoneType = $prospect->phone; my $phone2Type = $prospect->phone2; my $phone3Type = $prospect->phone3; my $phone4Type = $prospect->phone4; my $email = $prospect->email; my $url = $prospect->url; my $interestLevel = $prospect->interestLevel; my $designFirm = $prospect->designFirm; my $hostingFirm = $prospect->hostingFirm; my $companyName = $prospect->companyName; # Insert the new prospect in the database my $sth = $dbh->prepare("INSERT INTO prospect VALUES (NULL, \"$nam +e\", \"$address\", \"$address2\", \"$state\", \"$city\", \"$phone\", +\"$phone2\", \"$phone3\", \"$phone4\", \"$phoneType\", \"$phone2Type\ +", \"$phone3Typ +e\", \"$phone4Type\", \"$email\", \"$url\", \"$interestLevel\", \"$de +signFirm\", \"$hostingFi +rm\", \"$companyName\")"); # Execute the statement $sth->execute or die print DBI->errstr; # Lets get the prospect id that we just added back $sth = $dbh->prepare("SELECT last_insert_id() from prospect"); $sth->execute or die print DBI->errstr; my @id = $sth->fetchrow_array; my $ID = $id[[0]]; # Return the new prospects ID return $ID; } I want to be good... really I do. I just don't know how :-)<p>
-- rogueFalcon

Why do you people insist on doing things sdrawkcab?

Edit by tye

Replies are listed 'Best First'.
Re: MySQL / DBI / OOP
by runrig (Abbot) on Sep 05, 2001 at 23:48 UTC
    First, look in the DBD::mysql docs and use mysql_insertid instead of doing another select to get the last id inserted.

    Second, you should be using the DBI quote method or placeholders (my preference is placeholders), instead of pasting your variables directly into the sql statement. Placeholders would save you from all that backwhacking of quotes, and in some databases you'd use single instead of double quotes to quote the args, so if you ever migrated, it'd save one extra headache. Placeholders don't get you alot efficiency-wise in mysql, its true, but in combination with prepare_cached can save a bit in statement handle creation (of course then the dbh would have to be persistent also using connect_cached or a package global or as an object attribute or something...).

    Third, I think its kind of pointless to copy all those parameters to local args, just to paste them into the sql statement. I'd do something like the insert_hash example in the DBI docs. If this InsertProspect method is already in your main Prospect (or whatever you're calling it) class/package, then I'd not use the accessor methods and just use a hash slice (assuming your object IS a hash underneath) to get all the names and values for the insert statement.

(Ovid) Re: MySQL / DBI / OOP
by Ovid (Cardinal) on Sep 06, 2001 at 00:26 UTC

    If you can update the prospect object's methods, I would recommend adding a method that will add the prospect to the database for you. Another solution would be a method that returns all of the data for you in an array or hash. Barring that, the following really ugly hack could work:

    { # Get the new prospect my $prospect = shift; # Connect to the database my $dbh = DBI->connect('DBI:mysql:menagerie', 'menagerie') or die "Couldn't connect to database:" . DBI->errstr; # Set all of the variables that we need to pass to MySQL my @data = qw/ name address address2 state c +ity phone phone2 phone3 phone4 phoneType phone2Type phone3Type phone4Type email url interestLevel designFirm hostingFirm companyName /; my @insert_data; my $place_holders = '?,' x @data; chop $place_holders; # remove trailing comma foreach ( @data ) { no strict; my $method = $data[$_]; push @insert_data, $prospect->$method; } # Insert the new prospect in the database my $sth = $dbh->prepare("INSERT INTO prospect VALUES ( $place_ +holders )" ); # Execute the statement $sth->execute( @insert_data ) or die print DBI->errstr; # Lets get the prospect id that we just added back my $ID = $dbh->{'mysql_insertid'}; return $ID; }

    The problem, of course, is when you turn off strict for the method calls, you run the risk of unintentionally calling the wrong method if you misspelled it. Further, if you have inherited the misspelled method, you could be in big trouble. The solution would be to write an autoload routine that detects whether or not the method exists and croaks rather than allowing you to inherit something. Of course, if you can write the autoload method, you could write the other methods :)

    If you can't change the prospect methods, subclass it and add your own method. Much safer that way!

    Other comments: you don't have a password in your connect string. If you left it off because you don't want us to see it, good for you! If you left it off because there is no password, shame on you! :)

    I also noticed that you have repetitive phone data. As soon as someone has a fifth phone, your schema breaks. If you need to have multiple phones, create a separate table for them.

    Cheers,
    Ovid

    Vote for paco!

    Join the Perlmonks Setiathome Group or just click on the the link and check out our stats.

Re: MySQL / DBI / OOP
by blakem (Monsignor) on Sep 05, 2001 at 23:47 UTC
    Perhaps placeholders would make your life easier:
    my $sth = $dbh->prepare("INSERT INTO prospect (name, address, address2 +, state, city, [snip]) VALUES (?,?,?,?,?,[snip])"; $sth->execute($prospect->name,$prospect->address,$prospect->address2,[ +snip]) or die print DBI->errstr;

    -Blake

Re: MySQL / DBI / OOP
by princepawn (Parson) on Sep 05, 2001 at 23:45 UTC
    Have a look at this paper for an overview of the available frameworks for this type of thing.
Re: MySQL / DBI / OOP
by mr.dunstan (Monk) on Sep 05, 2001 at 23:49 UTC
    What I recommend is to make a function that encapsulates inserting the new object in the database. That looks good, but it would be even more convenient to make a function you can call like so:

    my $new_prospect_id = &insert_new_object($param1, $param2, $param3);

    OR

    If you want to stay OO, give the prospect object a method insert() that you can call to finally execute the sql statement at the end!

    $new_prospect_id = $prospect->insert();



    -mr.dunstan
Re: MySQL / DBI / OOP
by lachoy (Parson) on Sep 06, 2001 at 07:57 UTC
    <plug> SPOPS will help you out much here -- your $prospect object can have serialization behavior built-in, so once you've set all your properties all you need to do is:

    # Save the prospect object eval { $prospect->save }; if ( $@ ) { die "Cannot save prospect:", "$SPOPS::Error::system_msg\n"; } print "Prospect ID: ", $prospect->id, "\n";
    You'd need to give it some configuration information (list your fieldnames, name your primary key and table, etc.), but that's about it. Ping me for more info if you're interested. </plug>

    Chris
    M-x auto-bs-mode

      After spending sometime with the other perl monks here is my revised version of the same function above... please let me know what you think

      sub InsertProspect { # Get the new prospect my $prospect = shift; # Connect to the database my $dbh = DBI->connect('DBI:mysql:menagerie', 'menagerie') or die +DBI->errstr; # Insert the new prospect in the database my $sth = $dbh->prepare("INSERT INTO prospect VALUES (NULL, ?, ?, +?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"); # Execute the statement $sth->execute($prospect->name, $prospect->address, $prospect->address2, $prospect->state, $prospect->city, $prospect->phone, $prospect->phone2, $prospect->phone3, $prospect->phone4, $prospect->phoneType, $prospect->phone2Type, $prospect->phone3Type, $prospect->phone4, $prospect->email, $prospect->url, $prospect->interestLevel, $prospect->designFirm, $prospect->hostingFirm, $prospect->companyName) or die DBI->errstr; # Lets get the prospect id that we just added back $sth = $dbh->prepare("SELECT last_insert_id() from prospect"); $sth->execute or die DBI->errstr; my @id = $sth->fetchrow_array; my $ID = $id[0]; # Return the new prospects ID return $ID; }
      -- rogueFalcon

      Why do you people insist on doing things sdrawkcab?

Re (tilly) 1: MySQL / DBI / OOP
by tilly (Archbishop) on Dec 10, 2001 at 10:38 UTC
    Something important that nobody said.

    It is a really bad idea to rely on knowing the order of columns in a database. You have a table with 20 columns. You have an insert with 20 values. If the order is off slightly, have fun debugging! As columns are added or dropped, this will be a constant source of bugs. (Just imagine if some future DBA has to do a database migration and happens to reorder the columns. Then your scripts all have to work against that..?)

    This is typically an issue any time you use positional based logic. Associating things by name works much better.

    Therefore if the code is not performance critical, I would strongly suggest storing the data structure to insert in an anonymous hash, and then dynamically construct the insert statement from that, walking the keys to get the field names, and values to get the values, and using the syntax:

    my $sql = qq( insert into foo (bar, baz) values (?, ?) );
    so that there is no possibility of a synchronization error.