0

For a PDO execution statement I am trying to make any static information such as column names and array strings to a dynamic array which contains every column from the MySQL table.

The original code was:

$stmt = $conn->prepare("INSERT into data (`username,` `password`, `email`) VALUES username = :username , password = :password , email = :email ");

$stmt->execute(array(
    ':username' => $entry_username,
    ':password' => $entry_password,
    ':email' => $entry_email
));

So far I have been able to change the sql statement to

$sql = "INSERT into DATA (`"  . implode('`,`', $columns) . "`) values (:" . implode(',:', $columns) . ")";
$stmt = $conn->prepare($sql);

but have been unable to do a similar thing to the execution array to make it dynamically variating like the statement.

I have tried adding a for statement in the array

for ($i = 0; $i < count($columns); $i++) {
    ':'.$columns[$i] => ${'entry_'.$columns[$i]};
}

but this hasn't worked.

Any help would be much appreciated. Thanks in advance!

SteppingHat
  • 1,199
  • 4
  • 19
  • 50

3 Answers3

0

This is a perfect situation to make good use of a prepared statement.

Try this: I am kind of assuming what the varuables will be called in the $columns array here.

$stmt = $conn->prepare("INSERT into data 
      (username, password, email) VALUES( :username , :password, :email )");

$stmt->bindParam(':username', $username, PDO::PARAM_STR);
$stmt->bindParam(':password', $password, PDO::PARAM_STR);
$stmt->bindParam(':email', $email, PDO::PARAM_STR);

foreach ( $columns as $column ) {

    $username = $column['username'];
    $password = $column['password'];
    $email    = $column['email'];

    $result = $stmt->execute();

    if ( ! $result ) {
       // add some error checking code here
    }
}
RiggsFolly
  • 93,638
  • 21
  • 103
  • 149
  • Correct me, if wrong, but the author wants also the executed array to be dynamically created. Btw. I think there's no need of `VALUES (username = ...)` it can be simply `VALUES (:username, :password, :email)` – Royal Bg Aug 19 '13 at 12:06
  • This isn't what I am trying to achieve. What I am trying to do is completely get rid of the words username, password and email and replace them with $column which is an array which contains those words and possible less or more, to make the query dynamic to suit the table. – SteppingHat Aug 19 '13 at 12:07
  • @SteppingHat But like I said THATS NOT ALLOWED – RiggsFolly Aug 19 '13 at 12:11
  • @RiggsFolly What do you mean? They are column names. There is a function earlier in the script that fetches all of the column names from the table and creates the $columns array. The script then generates more/less input fields and the code to handle the data. – SteppingHat Aug 19 '13 at 12:14
  • The names of columns cannot be used in the substitution. only the values. So `(username, password, email )` must be coded like that not using `?` or `:aname' – RiggsFolly Aug 19 '13 at 12:18
0

Basically, your code would look like this.

$entry = array(
    'username'  =>  $_POST['username'], //assuming it's comming from the post data or for instance $row['username'] if from previous select statement 
    'password'  =>  $_POST['password'],
    'email'     =>  $_POST['email']
    );
$sth = $dbh->prepare('INSERT into data (`username,` `password`, `email`) VALUES (:username, :password, :email)');
$sth->bindValue(':username', $entry['username'], PDO::PARAM_INT);
$sth->bindValue(':password', $entry['password'], PDO::PARAM_STR);
$sth->bindValue(':email', $entry['email'], PDO::PARAM_STR);
$sth->execute();

If you want the bound variables to be dynamically created, then you need to create with a loop the bindValue rows:

$entry = array(
    'username'  =>  $_POST['username'], //assuming it's comming from the post data or for instance $row['username'] if from previous select statement 
    'password'  =>  $_POST['password'],
    'email'     =>  $_POST['email']
    );
$sth = $dbh->prepare('INSERT into data (`username,` `password`, `email`) VALUES (:username, :password, :email)');
foreach($entry as $key => $value) {
    $sth->bindValue(':'.$key, $entry[$key], PDO::PARAM_STR);
}
$sth->execute();

or inside the foreach

$sth->bindValue(':'.$key, $value, PDO::PARAM_STR);

Since your keys are (username, password, email) their keynames will be initiated to $key variable, and their values to the $value variable. in the first case it will produce:

$sth->bindValue(':username', $entry['username'], PDO::PARAM_INT);
$sth->bindValue(':password', $entry['password'], PDO::PARAM_STR);
$sth->bindValue(':email', $entry['email'], PDO::PARAM_STR);

Which will be evaluated to:

$sth->bindValue(':username', $_POST['username'], PDO::PARAM_INT);
$sth->bindValue(':password', $_POST['password'], PDO::PARAM_STR);
$sth->bindValue(':email', $_POST['email'], PDO::PARAM_STR);

In the second case it will be directly evaluated.


Have in mind it's completely unacceptable to dynamically create the column names in the query. And you have to reason to do it. However, not a full query is also hard to be read from the other developers. It's enough for you to dynamically create the bound values. You can make a method do it for you. For instance, if you column names in the query are the same way aliased, as the names of the input fields, you will have nothing more to do, but to execute the query.

Let's say you have that helper method:

Class DBConnect {

    private $_driver     = "mysql";
    private $_dbname     = "xxxx";
    private $_host       = "xxxx";
    private $_user       = "xxxx";
    private $_password   = "xxxx";
    private $_port       = 3306;

    private $_dbh;

    public function __construct($driver = NULL, $dbname = NULL, $host = NULL, $user = NULL, $pass = NULL, $port = NULL) {
    $driver     = $driver   ?: $this->_driver;
    $dbname     = $dbname   ?: $this->_dbname;
    $host       = $host     ?: $this->_host;
    $user       = $user     ?: $this->_user;
    $pass       = $pass     ?: $this->_password;
    $port       = $port     ?: $this->_port;
    try {
        $this->_dbh  = new PDO("$driver:host=$host;port=$port;dbname=$dbname", $user, $pass);
        $this->_dbh->exec("set names utf8");
    } catch(PDOException $e) {
        echo    $e->getMessage();  
    }
}

    public function query($sql) {
        $sth = $this->_dbh->prepare($sql);

        foreach ($_REQUEST as $key => $value) {  
            if(is_int($value)) {
                 $param = PDO::PARAM_INT;
             } elseif(is_bool($value)) {
                 $param = PDO::PARAM_BOOL;
             } elseif(is_null($value)) {
                 $param = PDO::PARAM_NULL;
             } elseif(is_string($value)) {
                 $param = PDO::PARAM_STR;
             } else {
                 $param = FALSE;   
             }
             $sth->bindValue(":$key", $value, $param);
        }

        $sth->execute();
        $result = $sth->fetchAll();

        return $result;

    }
}

So, lets say in another class you have a lot of queries, separated by methods:

public function getFirstQuery() {
    $sql = "SELECT 
        col1, col2 
        FROM table1
        WHERE col3 = :col3;";
    $query = $this->_db->query($sql);
    return $query;
}

public function inserSecondquery() {
    $sql = "INSERT INTO 
        `table1` 
        (col1, col2) 
        VALUES
        ((SELECT 
        id 
        FROM table2 
        WHERE col8 = :col8), :post_field_5);";
    $query = $this->_db->query($sql);
    return $query;
}

Assuming you have called these queries the query() method which also fetches the data, the select one you can foreach to retrieve the data, and the insert one you can just call, to insert data. The only rule here is the post fields should be named same way, for example <input name="post_field_5" />


You can also take a look here: PDO Dynamic Query Building


OK, it seems you need to find library for active record like the ones CodeIgniter uses, or... use CodeIgniter.

From the official documentation:

http://ellislab.com/codeigniter/user-guide/database/helpers.html

$this->db->insert_string();

This function simplifies the process of writing database inserts. It returns a correctly formatted SQL insert string. Example: $data = array('name' => $name, 'email' => $email, 'url' => $url);

$str = $this->db->insert_string('table_name', $data);

The first parameter is the table name, the second is an associative array with the data to be inserted. The above example produces: INSERT INTO table_name (name, email, url) VALUES ('Rick', 'rick@example.com', 'example.com')

So, in your case, you can have something like this:

<form action="" method="post">
    <input type="text" name="username" value="testUser123" />
    <input type="password" name="password" value="yourPass666" />
    <input type="text" name="email" value="email@example.com" />
    <input type="submit" value="submit" />
</form>
<?php
//... extending CI
//... opening a method
$table = 'data';
//comming from somewhere, let's dynamically populated array but for testing purpose I will hardcode:
$columns('username', 'password', 'email');
foreach($columns as $column) {
    $data[$column] = $_POST[$column]; // this will produce $data=array('username'=>$_POST['username'],password=....);
}
$str = $this->db->insert_string($table, $data); 
?>

If you submit the form in the beginning, you will have:

INSERT INTO data (username, password, email) VALUES ('testUser123', 'yourPass666', 'email@example.com');

The whole active record class doc (insert chosen here) http://ellislab.com/codeigniter/user-guide/database/active_record.html#insert

Community
  • 1
  • 1
Royal Bg
  • 6,988
  • 1
  • 18
  • 24
  • Sorta but not really what I'm trying to accomplish. Essentially the keynames username, password and email wont even appear in the code as they could be completely different things. That's what the $columns array is for. It contains all of the keywords. If it would work, I would put a for loop in the execution array (first block of code in the question) and print out `':'.$columns[$i] => ${'entry_'.$columns[$i]}` if that could work. But it can't and I can't figure out a way to do that with a valid syntax/logic. – SteppingHat Aug 19 '13 at 12:22
  • inject your columns array in $entry array, let me provide another example – Royal Bg Aug 19 '13 at 12:33
  • Take a look here: http://stackoverflow.com/questions/11838068/pdo-dynamic-query-building – Royal Bg Aug 19 '13 at 12:41
  • It's still not as dynamic as I want it to be. $where is still static (as defined by :userid1 and :userid2) and is the complete opposite as to what I want to do. I want to use the $columns array which contains the name of each column and use it to create what $where would be. – SteppingHat Aug 19 '13 at 12:48
  • You can't get that far dynamic in PDO – Royal Bg Aug 19 '13 at 12:52
  • Can't I use something like a for loop to simply state which array items (such as :foo) get assigned to which variable? ( => $foo) where foo is an array entry in $columns? – SteppingHat Aug 19 '13 at 12:56
  • You can manipulate the execution, but you cannot manipulate the where clause the way you want. You have to write your own wrapper. But is it really necessary to have so dynamically built where clause. Once you have more than 10 queries you will have to spend 5 mins each to understand what were the where clause. – Royal Bg Aug 19 '13 at 12:58
  • Well all of the queries in the program use the same general layout. A $stmt (UPDATE... INSERT... etc) and all of them use `$stmt->execute(array(...));` to assign the array items from the statement their values. And what do you mean by writing my own wrapper cause I don't know what that means – SteppingHat Aug 19 '13 at 13:02
  • Let's make it more clear. Dynamic `execute(array...));` is same as dynamic `bindValue(...);`, but you want one more thing - dynamic where clause i.e.: (`SELECT implode(',', $dynamicColumns) FROM $dynamicTable WHERE $column[$i] = :$key AND {repeat}`), right? In which program using PDO do you have that kind of queries? And more interesting - why do you need that? – Royal Bg Aug 19 '13 at 13:05
  • I don't understand your question..sorry – SteppingHat Aug 19 '13 at 13:07
  • All of your problem is you have a statement `INSERT INTO $table ($columns) VALUES ($values)` where $table, $columns and $values are each time different things, and you need with one statement, but with different things, to make a lot of queries, right? So you want only to change the values of the 3 variables, without writting again INSERT INTO blabla...? – Royal Bg Aug 19 '13 at 13:10
  • Well trying to focus onto one statement/execution. The statement is `INSERT INTO data ($columns) VALUES (:$columns)` note the table name is static and $columns is used to both select the columns and the array keywords for the values. After the statement, it then goes to assigning the values to the array keywords. The values are already defined as variables which are `$entry_(column name)`. It's just a matter of creating a loop where the values are assigned to the matching array keyword. – SteppingHat Aug 19 '13 at 13:15
  • Say one of the columns in the array is called _foo_. The array execution would appear as `:foo => $entry_foo` – SteppingHat Aug 19 '13 at 13:16
  • Take a look at my last edit. It's the closes I can go. You need active record library, or use awhole framework as CodeIgniter. The AR lib was the wrapper I was talking about. You will have hard time to code it yourself. Not impossible, but maybe you will have some fails with escaping, etc. Better use the one that's already present. – Royal Bg Aug 19 '13 at 13:23
  • Thanks! Although this wasn't exactly what I wanted this would definitely help me get closer to what I want to do. Thank you for your time. I really appreciate it :) – SteppingHat Aug 19 '13 at 13:45
  • Glad to hear it. Wish you good luck in achieving your final purpose :) – Royal Bg Aug 19 '13 at 13:46
-1

If you don't have to stick to the for loop, I would suggest a foreach, which should be easier (I know the little problems with for too).

foreach ($element in $array) { code execution here }

Your array element is then stored in the $element (or as you like to name it) and you can execute the command found there.

Is this what you're looking for or did I get you wrong?

Mäx Müller
  • 233
  • 1
  • 3
  • 15