This question already has an answer here:
bulk updating a list of values from a list of ids
(1 answer)
Closed 9 years ago.
Is this the only way to use two foreach statements with arrays going into a MySQL database?
The first one will update the ot_hours field, and the second foreach will update the lieu_hours field. I tried to combine both to do one query but it kept updating with wrong values.
This is what I have right now that works but is ugly.
foreach($_POST['overtimehours'] as $key => $value) {
dbQuery("UPDATE $TABLE SET ot_hours='$value', ot_status=1, ot_submitdate='$ot_submitdate' WHERE trans_num=$key AND uid='$contextUser' AND (ot_status=0 OR ot_status=1 OR ot_status=3)");
}
foreach($_POST['lieutimehours'] as $key2 => $value2) {
dbQuery("UPDATE $TABLE SET lieu_hours='$value2', ot_status=1, ot_submitdate='$ot_submitdate' WHERE trans_num=$key2 AND uid='$contextUser' AND (ot_status=0 OR ot_status=1 OR ot_status=3)");
}
I'm sure there's much better ways to do this. This is why I'm hoping someone can help me :)
Thanks in advance for all responses
Applied to your case, here is the adapted answer of Danny:
<?php
//first query:
$arrk = array_keys($_POST['overtimehours']);
$arrv = array_values($_POST['overtimehours']);
$id_list = implode(',', $arrk);
$whens = implode(
"\n ",
array_map(
function ($id, $value) {
return "WHEN {$id} THEN {$value}";
},
$arrk,
$arrv
)
);
$sql1 = "
UPDATE $TABLE
SET ot_hours = CASE trans_num
{$whens}
END,
ot_status=1,
ot_submitdate='$ot_submitdate'
WHERE id IN ({$id_list})
AND uid='$contextUser'
AND (ot_status=0 OR ot_status=1 OR ot_status=3)
";
//second query:
$arrk = array_keys($_POST['lieutimehours']);
$arrv = array_values($_POST['lieutimehours']);
$id_list = implode(',', $arrk);
$whens = implode(
"\n ",
array_map(
function ($id, $value) {
return "WHEN {$id} THEN {$value}";
},
$arrk,
$arrv
)
);
$sql2 = "
UPDATE $TABLE
SET lieu_hours = CASE trans_num
{$whens}
END,
ot_status=1,
ot_submitdate='$ot_submitdate'
WHERE id IN ({$id_list})
AND uid='$contextUser'
AND (ot_status=0 OR ot_status=1 OR ot_status=3)
";
//now use pdo to run sql1 and sql2
?>
At least you're willing to learn new things, that's good.
Don't assume that everything you expect to be posted is actually posted.
Use the ternary operation and the isset function to check if your posts are actually in place:
$overTimeHours = isset($_POST['overtimehours']) ? $_POST['overtimehours'] : false;
$lieuTimeHours = isset($_POST['lieutimehours']) ? $_POST['lieutimehours'] : false;
if($overTimeHours != false && $lieuTimeHours != false)
{
// Proceed ; checkpoint #1
}
else
{
// The values were not posted, do some error handling.
}
So at this point, inside of checkpoint #1, you would be doing this:
foreach($overTimeHours as $key => $value)
{
dbQuery("UPDATE $TABLE SET ot_hours='$value', ot_status=1, ot_submitdate='$ot_submitdate' WHERE trans_num=$key AND uid='$contextUser' AND (ot_status=0 OR ot_status=1 OR ot_status=3)");
}
foreach($lieuTimeHours as $key2 => $value2)
{
dbQuery("UPDATE $TABLE SET lieu_hours='$value2', ot_status=1, ot_submitdate='$ot_submitdate' WHERE trans_num=$key2 AND uid='$contextUser' AND (ot_status=0 OR ot_status=1 OR ot_status=3)");
}
You would probably not find it ugly to run through a single for loop if you only had one array to parse through.
Now you have two arrays (obviously), so if the minimum amount of loops for one array is one for loop, then the minimum amount of loops that you need for two arrays have to be two. The arrays are UNRELATED so you can't use one to make parsing the other one easier.
Parsing through $overTimeHours with
$overTimeHours as $key => $value
assuming that you really need the keys and the values inside of the array, is the shortest thing you can do. Same story goes for lieuTimeHours
Your code is vonurable to SQL-injections.
Don't insert variables into your query like this:
SET lieu_hours='$value2'
A decent programmer (or a 12-year old kid) could easily enter something like this into your database:
yo';DROP TAbLE users;--
Or something similar, to delete data from your database. You must use prepared statements in order to prevent from being attacked with basic SQL-injections.
Prepared statements are available in most situations there are, but I highly recommend using either PDO or the mysqli syntax.
Here's an example of how you can create a PDO connection:
// Usage: $db = connectToDatabase($dbHost, $dbName, $dbUsername, $dbPassword);
// Pre: $dbHost is the database hostname,
// $dbName is the name of the database itself,
// $dbUsername is the username to access the database,
// $dbPassword is the password for the user of the database.
// Post: $db is an PDO connection to the database, based on the input parameters.
function connectToDatabase($dbHost, $dbName, $dbUsername, $dbPassword)
{
try
{
return new PDO("mysql:host=$dbHost;dbname=$dbName;charset=UTF-8", $dbUsername, $dbPassword);
}
catch(PDOException $PDOexception)
{
exit("<p>An error ocurred: Can't connect to database. </p><p>More preciesly: ". $PDOexception->getMessage(). "</p>");
}
}
Now you can init the database variables:
$host = 'localhost';
$user = 'root';
$databaseName = 'databaseName';
$pass = '';
And now you can access your database via
$db = connectToDatabase($host, $databaseName, $user, $pass); // You can make it be a global variable if you want to access it from somewhere else.
Now you can create a query that accepts prepared statements:
$query = "UPDATE :table SET ot_hours=:ot_hours, ot_status=1, ot_submitdate=:ot_submitdate WHERE trans_num=:key AND uid=:contextUser AND (ot_status=0 OR ot_status=1 OR ot_status=3);";
And you can now easily prepare it, execute your variables INTO the query WITHOUT being vonurable to sql injections (The difference is really: the non-prepared queries run COMMANDS, meanwhile the prepared ones are plain STRINGS):
$statement = $db->prepare($query); // Prepare the query.
$success = $statement->execute(array(
':table' => $TABLE,
':ot_hours' => $ot_hours,
':ot_submitdate ' => $ot_submitdate ,
':key' => $key,
':contextUser' => $contextUser
)); // Here you insert the variable, by executing it 'into' the prepared query.
if($success)
{
// Update was successful.
]
else
{
// Update was not successful, feel free to catch an PDOException $PDOexception
}
Also I note that I added a ";" at the end of your script, which is not REQUORED but I feel that it's safer, to make sure to tell that your execution is finished and you don't want anything related to follow (even though It's not from you).
I hope that I answered your question/s (and hopefully way beyond that), I hope that you'll consider what I said :) Feel free to ask if there are any questions.
Also, don't hesitate to correct me if I may have said anything incorrectly meanwhile writing this answer.
I would recommend you to use prepared statement. You should at least correctly encode other variables you use ($TABLE, ...)
$firstUpdate =
"UPDATE $TABLE
SET ot_hours=:value, ot_status=1, ot_submitdate='$ot_submitdate'
WHERE trans_num=:key
AND uid='$contextUser'
AND (ot_status=0 OR ot_status=1 OR ot_status=3)";
$secondUpdate =
"UPDATE $TABLE
SET lieu_hours=':value', ot_status=1, ot_submitdate='$ot_submitdate'
WHERE trans_num=:key
AND uid='$contextUser'
AND (ot_status=0 OR ot_status=1 OR ot_status=3)";
$db = PDO(...); // I assume here a connection managed by PDO
$stmt = $db->prepare($firstUpdate);
foreach($_POST['overtimehours'] as $key => $value) {
$stmt->execute(array(":key"=>$key,":value"=>$value);
}
$stmt = $db->prepare($secondUpdate);
foreach($_POST['lieutimehours'] as $key => $value) {
$stmt->execute(array(":key"=>$key,":value"=>$value);
}
In my opinion first thing you have to do is reduce the number of opening the connections with the DB like the following:
$query = "";
foreach($_POST['overtimehours'] as $key => $value) {
$query .="UPDATE $TABLE SET ot_hours='$value', ot_status=1, ot_submitdate='$ot_submitdate' WHERE trans_num=$key AND uid='$contextUser' AND (ot_status=0 OR ot_status=1 OR ot_status=3) ; ";
}
foreach($_POST['lieutimehours'] as $key2 => $value2) {
$query .= "UPDATE $TABLE SET lieu_hours='$value2', ot_status=1, ot_submitdate='$ot_submitdate' WHERE trans_num=$key2 AND uid='$contextUser' AND (ot_status=0 OR ot_status=1 OR ot_status=3); ";
}
if ($query) dbQuery($query);
second and it is important, like you said combine the two arrays in one try and debug your code untill you succeed .
Related
This question already has answers here:
Single result from database using mysqli
(6 answers)
Closed 2 years ago.
I am trying to write a function that will check for a single value in the db using mysqli without having to place it in an array. What else can I do besides what I am already doing here?
function getval($query){
$mysqli = new mysqli();
$mysqli->connect(HOST, USER, PASS, DB);
$result = $mysqli->query($query);
$value = $mysqli->fetch_array;
$mysqli->close();
return $value;
}
How about
$name = $mysqli->query("SELECT name FROM contacts WHERE id = 5")->fetch_object()->name;
The mysql extension could do this using mysql_result, but mysqli has no equivalent function as of today, afaik. It always returns an array.
If I didn't just create the record, I do it this way:
$getID = mysqli_fetch_assoc(mysqli_query($link, "SELECT userID FROM users WHERE something = 'unique'"));
$userID = $getID['userID'];
Or if I did just create the record and the userID column is AI, I do:
$userID = mysqli_insert_id($link);
Always best to create the connection once at the beginning and close at the end. Here's how I would implement your function.
$mysqli = new mysqli();
$mysqli->connect(HOSTNAME, USERNAME, PASSWORD, DATABASE);
$value_1 = get_value($mysqli,"SELECT ID FROM Table1 LIMIT 1");
$value_2 = get_value($mysqli,"SELECT ID FROM Table2 LIMIT 1");
$mysqli->close();
function get_value($mysqli, $sql) {
$result = $mysqli->query($sql);
$value = $result->fetch_array(MYSQLI_NUM);
return is_array($value) ? $value[0] : "";
}
Here's what I ended up with:
function get_col($sql){
global $db;
if(strpos(strtoupper($sql), 'LIMIT') === false) {
$sql .= " LIMIT 1";
}
$query = mysqli_query($db, $sql);
$row = mysqli_fetch_array($query);
return $row[0];
}
This way, if you forget to include LIMIT 1 in your query (we've all done it), the function will append it.
Example usage:
$first_name = get_col("SELECT `first_name` FROM `people` WHERE `id`='123'");
Even this is an old topic, I don't see here pretty simple way I used to use for such assignment:
list($value) = $mysqli->fetch_array;
you can assign directly more variables, not just one and so you can avoid using arrays completely. See the php function list() for details.
This doesn't completely avoid the array but dispenses with it in one line.
function getval($query) {
$mysqli = new mysqli();
$mysqli->connect(HOST, USER, PASS, DB);
return $mysqli->query($query)->fetch_row()[0];
}
First and foremost,
Such a function should support prepared statements
Otherwise it will be horribly insecure.
Also, such a function should never connect on its own, but accept an existing connection variable as a parameter.
Given all the above, only acceptable way to call such a function would be be like
$name = getVal($mysqli, $query, [$param1, $param2]);
allowing $query to contain only placeholders, while the actual data has to be added separately. Any other variant, including all other answers posted here, should never be used.
function getVal($mysqli, $sql, $values = array())
{
$stm = $mysqli->prepare($sql);
if ($values)
{
$types = str_repeat("s", count($values));
$stm->bind_param($types, ...$values);
}
$stm->execute();
$stm->bind_result($ret);
$stm->fetch();
return $ret;
}
Which is used like this
$name = getVal("SELECT name FROM users WHERE id = ?", [$id]);
and it's the only proper and safe way to call such a function, while all other variants lack security and, often, readability.
Try something like this:
$last = $mysqli->query("SELECT max(id) as last FROM table")->fetch_object()->last;
Cheers
edit I changed the code to the suggestion answer, all snippets now updated
currently I am playing around with PHP. Therefore I am trying to build a programm which can execute SQL commands. so, what I am trying is to write some functions which will execute the query. But I came to a point where I coundn't help myself out. My trouble is, for the INSERT INTO command, I want to give an array, containing the Data that shall be inserted but I simply can't figure out how to do this.
Here is what I got and what I think is relevant for this operation
First, the function I want to create
public function actionInsert($data_values = array())
{
$db = $this->openDB();
if ($db) {
$fields = '';
$fields_value = '';
foreach ($data_values as $columnName => $columnValue) {
if ($fields != '') {
$fields .= ',';
$fields_value .= ',';
}
$fields .= $columnName;
$fields_value .= $columnValue;
}
$sqlInsert = 'INSERT INTO ' . $this->tabelle . ' (' . $fields . ') VALUES (' . $fields_value . ')';
$result = $db->query($sqlInsert);
echo $sqlInsert;
if ($result) {
echo "success";
} else {
echo "failed";
}
}
}
and this is how I fil the values
<?php
require_once 'funktionen.php';
$adresse = new \DB\Adressen();
$adresse->actionInsert(array('nachname'=>'hallo', 'vorname'=>'du'));
My result
INSERT INTO adressen (nachname,vorname) VALUES (hallo,du)failed
What I wish to see
success
and of course the freshly insertet values in the database
There are a few things to consider when you are working with relational databases without using PDO:
What is the database that you are using.
It's your decision to choose from MySQL, postgreSQL, SQLite and etc., but different DBs generally have different syntax for inserting and selecting data, as well as other operations. Also, you may need different classes and functions to interact with them.
That being said, did you checkout the official manual of PHP? For example, An overview of a PHP application that needs to interact with a MySQL database.
What is the GOAL you are trying to accomplish?
It's helpful to construct your SQL first before you are messing around with actual codes. Check if your SQL syntax is correct. If you can run your SQL in your database, then you can try to implement your code next.
What's the right way to form an SQL query in your code?
It's okay to mess around in your local development environment, but you should definitely learn how to use prepared statements to prevent possible SQL injection attacks.
Also learn more about arrays in PHP: Arrays in PHP. You can use key-value pairs in a foreach loop:
foreach ($keyed_array as $key => $value) {
//use your key and value here
}
You don't need to construct your query in the loop itself. You are only using the loop to construct the query fields string and VALUES string. Be very careful when you are constructing the VALUES list because your fields can have different types, and you should add double quotes around string field values. And YES, you will go through all these troubles when you are doing things "manually". If you are using query parameters or PDO or any other advanced driver, it could be much easier.
After that, you can just concatenate the values to form your SQL query.
Once you get more familiar with the language itself and the database you are playing with, you'll definitely feel more comfortable. Good luck!
Is this inside of a class? I assume the tabelle property is set correctly.
That said, you should correct the foreach loop, that's not used correctly:
public function actionInsert($data_values) //$data_values should be an array
{
$db = $this->openDB();
if ($db) {
foreach ($data_values as $data){
// $data_values could be a bidimensional array, like
// [
// [field1=> value1, field2 => value2, field3 => value3],
// [field1=> value4, field2 => value5, field3 => value6],
// [field1=> value7, field2 => value8, field3 => value9],
// ]
$fields = Array();
$values = Array();
foreach($data as $key => $value){
array_push($fields,$key);
array_push($values,"'$value'");
}
$sqlInsert = 'INSERT INTO ' . $this->tabelle . ' (' . join(',',$fields) . ') VALUES (' . join(',',$values) . ')';
$result = $db->query($sqlInsert);
echo $sqlInsert;
if ($result) {
echo "success";
} else {
echo "failed";
}
}
}
This is a rather basic approach, in which you cycle through you data and do a query for every row, but it isn't very performant if you have big datasets.
Another approach would be to do everything at once, by mounting the query in the loop and sending it later (note that the starting array is different):
public function actionInsert($data_values) //$data_values should be an array
{
$db = $this->openDB();
if ($db) {
$vals = Array();
foreach ($data_values['values'] as $data){
// $data_values could be an associative array, like
// [
// fields => ['field1','field2','field3'],
// values => [
// [value1,value2,value3],
// [value4,value5,value6],
// [value7,value8,value9]
// ]
// ]
array_push('('.join(',',"'$data'").')',$vals);
}
$sqlInsert = 'INSERT INTO ' . $this->tabelle . ' (' . join(',',$data_values['fields']) . ') VALUES '.join(' , ',$vals);
$result = $db->query($sqlInsert);
echo $sqlInsert;
if ($result) {
echo "success";
} else {
echo "failed";
}
}
By the way dragonthought is right, you should do some kind of sanitizing for good practice even if you don't make it public.
Thanks to #Eagle L's answer, I figured a way that finally works. It is diffrent from what I tryed first, but if anyone having similar troubles, I hope this helps him out.
//get the Values you need to insert as required parameters
public function actionInsert($nachname, $vorname, $plz, $wohnort, $strasse)
{
//database connection
$db = $this->openDB();
if ($db) {
//use a prepared statement
$insert = $db->prepare("INSERT INTO adressen (nachname, vorname, plz, wohnort, strasse) VALUES(?,?,?,?,?)");
//fill the Values
$insert->bind_param('ssiss', $nachname, $vorname, $plz, $wohnort, $strasse);
//but only if every Value is defined to avoid NULL fields in the Database
if ($vorname && $nachname && $plz && $wohnort && $strasse) {
edited
$inserted = $insert->execute(); //added $inserted
//this is still clumsy and user unfriendly but serves my needs
if ($inserted) {//changed $insert->execute() to $inserted
echo 'success';
} else {
echo 'failed' . $inserted->error;
}
}
}
}
and the Function call
<?php
require_once 'funktionen.php';
$adresse = new \DB\Adressen();
$adresse->actionInsert('valueWillBe$nachname', 'valueWillBe$vorname', 'valueWillBe$plz', 'valueWillBe$wohnort', '$valueWillBe$strasse');
First off when answering please try and explain simply as possible as I am fairly new to php. Anyway my problem is is that I do not understand why my associative array to string conversion is not working. I am basically using the same model as described here: PHP 5 arrays Scroll down to see example for associative arrays. Anyway the output I always get is this when I submit "Adam" into the textbox:
Notice: Undefined index:
queryStr in C:\xampp\htdocs\practice\src\fetchigndatausingpdo.php on line 24
PID = 80 = 8 AND FirstName = adam AND 1 = adam AND LastName = preston AND 2 = preston AND Age = 17 AND 3 = 17 AND
Below is the code if you have any suggestions please notify me, thankyou :). Also $user and $pass have been deliberately blanked for security reasons.
<form action="fetchigndatausingpdo.php" method="post">
<input type="text" name="name">
<input type="submit" name="submit" value="submit">
</form>
<?php
$user = "adam";
$pass = "**********";
if(isset($_POST['name'])){
try{
$dbh = new PDO('mysql:host=localhost;dbname=my_db', $user, $pass, array(PDO::ATTR_PERSISTENT=>true));
$stmt = $dbh->prepare("SELECT * FROM persons WHERE FirstName LIKE ?");
$stmt->execute(array($_POST['name']));
if($stmt->rowCount() > 0){
$result = $stmt->fetchAll();
$terms = count($result);
foreach($result as $person){
foreach ($person AS $field => $value){
$terms--;
$GLOBALS['queryStr'].= $field.' = '.$value;
if($terms){
$GLOBALS['queryStr'].=' AND ';
}
}
}
echo $queryStr;
}
}catch(PDOException $e){
echo $e->getMessage();
}
}
?>
If you will be only receiving exactly one row from your query, you can change your $result = $stmt->fetchAll(); row to use the fetch() method of your database handler instead. That will give you exactly one array for your $result variable. That way, you'll like not have to make any further changes.
Change is required, because fetchAll() will put arrays into your $result variable (even if there's only one result). If you don't change the fetchAll() to fetch(), then you could try changing your foreach($result as $field => $value){ and below part to something like this:
..
foreach($result as $person){
foreach ($person AS $field => $value){
$terms--;
$GLOBALS['queryStr'].= $field.' = '.$value;
if($terms){
$GLOBALS['queryStr'].=' AND ';
}
echo $queryStr;
}
}
..
Please elaborate if this is the desired effect.
Some additional comments on the code:
You are already using the PDO library and its prepare method for efficient and safe use of queries. As you are using parameterbinding, it is not necessary to do manual sanitization of the received data, as PDO will handle that for you. Talking about this line here:
$name = mysql_real_escape_string($_POST['name']);
and this one:
$stmt->execute(array($name))
You could change the execute part to
$stmt->execute(array($_POST['name']))
instead. Also, regular way of checking if any results were returned when using PDO would be the rowCount() method of the statement you're executing. PDO would return true even if there weren't any matches in your query resultset. The above all combined, you would have that part of your code look like this:
..
if(isset($_POST['name'])){
try{
$dbh = new PDO('mysql:host=localhost;dbname=my_db', $user, $pass, array(PDO::ATTR_PERSISTENT=>true));
$stmt = $dbh->prepare("SELECT * FROM persons WHERE FirstName LIKE ?");
$stmt->execute(array($_POST['name']));
$resultsSize = $stmt->rowCount();
if($resultsSize > 0){
$queryStr = '';
$result = $stmt->fetchAll();
foreach($result as $person){
$terms = sizeof(array_keys($person));
foreach ($person AS $field => $value){
$terms--;
$queryStr .= $field.' = '.$value;
if($terms){
$queryStr .=' AND ';
}
$queryStr .= '<br/>'; // easier HTML visual evaluation
}
}
echo $queryStr;
}
}catch(PDOException $e){
echo $e->getMessage();
}
}
With the above refactoring (you are first checking if the $_POST['name'] is set) you are avoiding the extra resources allocated to the database connection (even if you are not using it). Putting your connection initialization along with your other executed code is not a problem in this case, you're good with one catch block handling your errors (as in this case they would be of similar nature, either problematic connection to the database.
Changes made to the query: when comparing string values, the equals sign (=) would result in a byte-by-byte comparison for exact value, while the LIKE operator would give the advantage of not stumbling upon any encoding fancyness - and using LIKE is the normally accepted way of comparing string values (also, with LIKE you could make use of wildcards (% character, etc.)).
**Edit:
I've updated the above code based on your comment - I've also moved the output of the string after the outer foreach loop (so it will print when all of the returned rows have been processed). I've changed your variable from the $GLOBALS superglobal variable to a local one (current context doesn't imply you would need access to it outside this file or page generation, if so, look into the possibility of using the $_SESSION variable).
I have an array like this
$a = array( 'phone' => 111111111, 'image' => "sadasdasd43eadasdad" );
When I do a var-dump I get this ->
{ ["phone"]=> int(111111111) ["image"]=> string(19) "sadasdasd43eadasdad" }
Now I am trying to add this to the DB using the IN statement -
$q = $DBH->prepare("INSERT INTO user :column_string VALUES :value_string");
$q->bindParam(':column_string',implode(',',array_keys($a)));
$q->bindParam(':value_string',implode(',',array_values($a)));
$q->execute();
The problem I am having is that implode return a string. But the 'phone' column is an integer in the database and also the array is storing it as an integer. Hence I am getting the SQL error as my final query look like this --
INSERT INTO user 'phone,image' values '111111111,sadasdasd43eadasdad';
Which is a wrong query. Is there any way around it.
My column names are dynamic based what the user wants to insert. So I cannot use the placeholders like :phone and :image as I may not always get a values for those two columns. Please let me know if there is a way around this. otherwise I will have to define multiple functions each type of update.
Thanks.
Last time I checked, it was not possible to prepare a statement where the affected columns were unknown at preparation time - but that thing seems to work - maybe your database system is more forgiving than those I am using (mainly postgres)
What is clearly wrong is the implode() statement, as each variable should be handled by it self, you also need parenthesis around the field list in the insert statement.
To insert user defined fields, I think you have to do something like this (at least that how I do it);
$fields=array_keys($a); // here you have to trust your field names!
$values=array_values($a);
$fieldlist=implode(',',$fields);
$qs=str_repeat("?,",count($fields)-1);
$sql="insert into user($fieldlist) values(${qs}?)";
$q=$DBH->prepare($sql);
$q->execute($values);
If you cannot trust the field names in $a, you have to do something like
foreach($a as $f=>$v){
if(validfield($f)){
$fields[]=$f;
$values[]=$v;
}
}
Where validfields is a function that you write that tests each fieldname and checks if it is valid (quick and dirty by making an associative array $valfields=array('name'=>1,'email'=>1, 'phone'=>1 ... and then checking for the value of $valfields[$f], or (as I would prefer) by fetching the field names from the server)
SQL query parameters can be used only where you would otherwise put a literal value.
So if you could see yourself putting a quoted string literal, date literal, or numeric literal in that position in the query, you can use a parameter.
You can't use a parameter for a column name, a table name, a lists of values, an SQL keyword, or any other expressions or syntax.
For those cases, you still have to interpolate content into the SQL string, so you have some risk of SQL injection. The way to protect against that is with whitelisting the column names, and rejecting any input that doesn't match the whitelist.
Because all other answers allow SQL injection. For user input you need to filter for allowed field names:
// change this
$fields = array('email', 'name', 'whatever');
$fieldlist = implode(',', $fields);
$values = array_values(array_intersect_key($_POST, array_flip($fields)));
$qs = str_repeat("?,",count($fields)-1) . '?';
$q = $db->prepare("INSERT INTO events ($fieldlist) values($qs)");
$q->execute($values);
I appreciated MortenSickel's answer, but I wanted to use named parameters to be on the safe side:
$keys = array_keys($a);
$sql = "INSERT INTO user (".implode(", ",$keys).") \n";
$sql .= "VALUES ( :".implode(", :",$keys).")";
$q = $this->dbConnection->prepare($sql);
return $q->execute($a);
You actually can have the :phone and :image fields bound with null values in advance. The structure of the table is fixed anyway and you probably should got that way.
But the answer to your question might look like this:
$keys = ':' . implode(', :', array_keys($array));
$values = str_repeat('?, ', count($array)-1) . '?';
$i = 1;
$q = $DBH->prepare("INSERT INTO user ($keys) VALUES ($values)");
foreach($array as $value)
$q->bindParam($i++, $value, PDO::PARAM_STR, mb_strlen($value));
I know this question has be answered a long time ago, but I found it today and have a little contribution in addition to the answer of #MortenSickel.
The class below will allow you to insert or update an associative array to your database table. For more information about MySQL PDO please visit: http://php.net/manual/en/book.pdo.php
<?php
class dbConnection
{
protected $dbConnection;
function __construct($dbSettings) {
$this->openDatabase($dbSettings);
}
function openDatabase($dbSettings) {
$dsn = 'mysql:host='.$dbSettings['host'].';dbname='.$dbSettings['name'];
$this->dbConnection = new PDO($dsn, $dbSettings['username'], $dbSettings['password']);
$this->dbConnection->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
}
function insertArray($table, $array) {
$fields=array_keys($array);
$values=array_values($array);
$fieldlist=implode(',', $fields);
$qs=str_repeat("?,",count($fields)-1);
$sql="INSERT INTO `".$table."` (".$fieldlist.") VALUES (${qs}?)";
$q = $this->dbConnection->prepare($sql);
return $q->execute($values);
}
function updateArray($table, $id, $array) {
$fields=array_keys($array);
$values=array_values($array);
$fieldlist=implode(',', $fields);
$qs=str_repeat("?,",count($fields)-1);
$firstfield = true;
$sql = "UPDATE `".$table."` SET";
for ($i = 0; $i < count($fields); $i++) {
if(!$firstfield) {
$sql .= ", ";
}
$sql .= " ".$fields[$i]."=?";
$firstfield = false;
}
$sql .= " WHERE `id` =?";
$sth = $this->dbConnection->prepare($sql);
$values[] = $id;
return $sth->execute($values);
}
}
?>
dbConnection class usage:
<?php
$dbSettings['host'] = 'localhost';
$dbSettings['name'] = 'databasename';
$dbSettings['username'] = 'username';
$dbSettings['password'] = 'password';
$dbh = new dbConnection( $dbSettings );
$a = array( 'phone' => 111111111, 'image' => "sadasdasd43eadasdad" );
$dbh->insertArray('user', $a);
// This will asume your table has a 'id' column, id: 1 will be updated in the example below:
$dbh->updateArray('user', 1, $a);
?>
public function insert($data = [] , $table = ''){
$keys = array_keys($data);
$fields = implode(',',$keys);
$pre_fields = ':'.implode(', :',$keys);
$query = parent::prepare("INSERT INTO $table($fields) VALUES($pre_fields) ");
return $query->execute($data);
}
Sample code:
$infoArray = array();
require_once("connectAndSelect.php");
// Connects to mysql and selects the appropriate database
$sql = "SOME SQL";
if($results = mysql_query($sql))
{
while($result = mysql_fetch_array($results, MYSQL_ASSOC))
{
$infoArray[] = $result;
}
}
else
{
// Handle error
}
echo("<pre>");
print_r($infoArray);
echo("</pre>");
In this sample code, I simply want to get the result of my query in $infoArray. Simple task, simple measures... not.
I would have enjoyed something like this:
$sql = "SOME SQL";
$infoArray = mysql_results($sql);
But no, as you can see, I have two extra variables and a while loop which I don't care for too much. They don't actually DO anything: I'll never use them again. Furthermore, I never know how to call them. Here I use $results and $result, which kind of represents what they are, but can also be quite confusing since they look so much alike. So here are my questions:
Is there any simpler method that I
don't know about for this kind of
task?
And if not, what names do you
give those one-use variables? Is
there any standard?
The while loop is really only necessary if you are expecting multiple rows to be returned. If you are just getting one row you can simply use mysql_fetch_array().
$query = "SOME SQL";
$result = mysql_query($query);
$row = mysql_fetch_array($result);
For single line returns is the standard I use. Sure it is a little clunky to do this in PHP, but at least you have the process broken down into debug-able steps.
Use PDO:
<?php
/*** mysql hostname ***/
$hostname = 'localhost';
/*** mysql username ***/
$username = 'username';
/*** mysql password ***/
$password = 'password';
try {
$dbh = new PDO("mysql:host=$hostname;dbname=mysql", $username, $password);
$sql = "SELECT * FROM myTable";
$result = $dbh->query($sql)
//Do what you want with an actual dataset
}
catch(PDOException $e) {
echo $e->getMessage();
}
?>
Unless you are legacied into it by an existing codebase. DONT use the mysql extension. Use PDO or Mysqli. PDO being preferred out of the two.
Your example can be come a set of very consise statements with PDO:
// create a connection this could be done in your connection include
$db = new PDO('mysql:host=localhost;dbname=your_db_name', $user, $password);
// for the first or only result
$infoArray = $db->query('SOME SQL')->fetch(PDO::FETCH_ASSOC);
// if you have multiple results and want to get them all at once in an array
$infoArray = $db->query('SOME SQL')->fetchAll(PDO::FETCH_ASSOC);
// if you have multiple results and want to use buffering like you would with mysql_result
$stmt = $db->query('SOME SQL');
foreach($stmt as $result){
// use your result here
}
However you should only use the above when there are now variables in the query. If there are variables they need to be escaped... the easiest way to handle this is with a prepared statement:
$stmt = $db->prepare('SELECT * FROM some_table WHERE id = :id');
$stmt->execute(array(':id' => $id));
// get the first result
$infoArray = $stmt->fetch(PDO::FETCH_ASSOC);
// loop through the data as a buffered result set
while(false !== ($row = $stmt->fetch(PDO::FETCH_ASSOC))){
// do stuff with $row data
}