Updating DB from grid array - php

I am using DHTMLX javascript library to present a grid in a form. My users could have hundreds of products in their grid but I am only capturing the the rows they add a price to. I then need to add those rows to a MySQL database along with some other information.
Getting standard $POST information and manipulating it in my PHP scripts is about the limit of my skills so far so where I need help is the array created by the updated rows. An example of what is being captured in my $POST is:
Array
(
[product] => 64
[dept] => 000
[submit] => Submit
[gridbox_1_4] => 422
[gridbox_64_4] => 534
[gridbox_175_4] => 1234
[gridbox_180_4] => 645
)
I currently capture the basic $POST variables with:
$itemcat = filter($_POST['product']);
$dept7 = filter($_POST['dept']);
My question is how do I capture the gridbox variables so I can use them in an INSERT statement? The only number that will change is the middle number which represents a primary key in my database for the products I need to INSERT into another table. I am assuming I need to explode the $POST variable somehow maybe? Then how do I INSERT them using something like:
"INSERT INTO submissions
(product_id, user_id,submission_id, sugg_price)
VALUES ('" . $gridbox_id . "','" . $myid . "','NULL', '" . $sugg . "')";
All of my reading today on arrays has given me a good understanding of how they work from a 100ft view but not how to specifically solve my problem or even how to start. I'm hoping the community can send me down the right path.

You can loop over the $_POST array and perform a regular expression match on each key:
// PDO will make your life so much easier
$dbh = new PDO('mysql:dbname=testdb;host=127.0.0.1', $user, $password);
// prepared statements are great - prepare once, use many
$stmt = $dbh->prepare('
INSERT INTO submissions
( product_id, user_id, submission_id, sugg_price)
VALUES
(:product_id, :user_id, NULL , :sugg_price)
');
// avoid SQL injection by passing your variables in as parameters
// see http://bobby-tables.com to understand why
$stmt->bindValue('user_id', $myid);
foreach ($_POST as $key => $val) {
if (preg_match("/^gridbox_(\d+)_4$/", $key, $matches)) {
$stmt->execute(array(
':product_id' => $matches[1],
':sugg_price' => $val
));
}
}

Related

how to store php multi dimensional array data into database in their fields [duplicate]

This question already has answers here:
Best way to INSERT many values in mysqli?
(4 answers)
Closed 4 years ago.
i have Multi array data like "cars name & cars modal"
Car name match with car model. both are different column in database (cars_name,cars_model). I want to store data from this array into database in their fields
Output:
Array
(
[car_name] => Array
(
[0] => Honda
[1] => Ford Mustang
[2] => Volvo
)
[car_modal] => Array
(
[0] => 2015
[1] => 2016
[2] => 2014
)
)
i want to store array values into single column in each row using "mysql". For this purpose i like query like this but it shows error.
$sql = "INSERT INTO cars_data (cars_name,cars_modal)
VALUES ($cars_name,$cars_modal)";
nothing happened. But errors show like this...
Notice: Array to string conversion in
E:\xampp\htdocs\car_records\modal_data.php on line 45 Error: INSERT INTO cars_data (cars_name,cars_model)
VALUES (Array,Array)Unknown column 'Array' in 'field list'
The question is how to fix it. Please help me
To use one statement and mysqli prepared statements (comments in code)...
$cars_name = ["Honda", "Volvo"];
// Create an entry for each name
$params = str_repeat("(?),", count($cars_name));
// Build a bind for a list of strings
$binds = str_repeat("s", count($cars_name));
// Add the params to the insert (remove the last ,)
$sql = "INSERT INTO car_data (cars_name)
VALUES ".rtrim($params, ",");
$insert = $conn->prepare ( $sql );
// Bind the parameters, using ... is the argument unpacking operator
$insert->bind_param($binds, ...$cars_name);
// Execute the SQL
$insert->execute();
Update:
If you had two data items in the array, you would be able to adapt the above to something like...
// Source data - ensure that the two sets of data have the same number of entries
$car_data = [ 'cars_name' => ["Honda", "Volvo"],
'cars_modal' => [ '2015', '2016' ]];
$car_count = count($car_data['cars_name']);
// Create an entry for each name (2 binds per entry)
$params = str_repeat("(?,?),", $car_count);
// Build a bind for a list of strings
$binds = str_repeat("ss", $car_count);
// Reformat data for binding (needs to be a single list of the data
// with cars_name followed by cars_modal for each entry)
$merged_data = [];
foreach ( $car_data['cars_name'] as $key => $name ) {
$merged_data[] = $name;
$merged_data[] = $car_data['cars_modal'][$key];
}
// Add the params to the insert (remove the last ,)
$sql = "INSERT INTO car_data (cars_name,car_model)
VALUES ".rtrim($params, ",");
$insert = $conn->prepare ( $sql );
// Bind the parameters, using ... is the argument unpacking operator
$insert->bind_param($binds, ...$merged_data);
// Execute the SQL
$insert->execute();
you can insert multiple elements in one row, you just need to bring it in the right format :
insert into x (columns) VALUES (x1),(x2),(x3)
$string = "";
foreach($cars_name as $car){
$string .= "(" . $car . "), ";
}
$sql = "INSERT INTO car_data (cars_name) VALUES $string";
Pleas note that you should never accept user input without sanitizing it.
When I want to do so, I first implode this array to get a normal string separated by (,) then when I retrieve data I implode them again.
$cars_name = implode(',', $_POST['cars_name']);
The result will be
Honda,Volvo,Mercedes,Toyota,BMW
Then if you want to get back the array from database again just do as following:
$cars_array = explode(',', $databaseObject['cars']);
The result will be the same as the first array of yours.

PHP how to extract keys names and values from associative array for mysql query

Hello I've tried both these questions solutions (final goal added at bottom)
INSERT array - PDO
Binding values from arrays?
but I don't get the expected variables content in $fields and $newdata
so I kindly print here some var_dump and cast to kindly ask your support.
My array derivate from an html table
For simplicity in my learning experiment I'm working with a dummy table of just 5 fields, as you see they are: selected, user_id, user_name, user_company and user_email.
Finally I have inserted just 2 rows of values.
The table content is posted as JSON.stringify.
Here you my results
Using the usual
print_r ( $Arr );
I can see this output
Array (
[0] => Array ( [selected] => [user_id] => 3 [user_name] => nome3 [user_company] => azien3 [user_email] => email3 )
[1] => Array ( [selected] => 1 [user_id] => 6 [user_name] => nome6 [user_company] => azien6 [user_email] => email6 )
)
next I try to apply the code of from the two above questions
24 $fields = implode(",", array_keys($Arr));
25 $newdata = "'" . implode("','", $Arr) . "'";
26
27 var_dump($fields);
28 echo "<br><br>";
29 var_dump($newdata);
But something is wrong in my interpretation or in my code , because the output is
Notice: Array to string conversion in D:\xampp\htdocs\ajax-json\post.php on line 25
Notice: Array to string conversion in D:\xampp\htdocs\ajax-json\post.php on line 25
string(3) "0,1"
string(15) "'Array','Array'"
can you kindly point out what's wrong?
e.g. is my array properly formed?
the final goal is to build a query where they are bind the keys names and key values taken from the associative array directly to columns and values for an INSERT into a mysql table.
In other words since the array's keys names are identical to the database table's columns names, I'm wondering how to make an automatism that creates the query like in the two questions in the opening of this question.
With "automatism" is meant to HAVE variables and maybe cycles to build a query INSTEAD than writing the single columns names and the same for the columns values to be inserted
Edit: from the accepted answer, this is the working code.
$my_keys = array_keys($Arr[0]);
// ---- This prevents PDO SQL Injection
$stmt=$pdo->prepare("DESC my_table");
$stmt->execute();
$whitelist_columns=$stmt->fetchAll(PDO::FETCH_COLUMN);
foreach($my_keys as $key){
if(!array_search($key,$whitelist_columns)){ echo "ERROR!"; }
}
// ---- End of prevention
$field_names = implode(",", $my_keys); // build column list
/** #JBH this foreach is needed otherwise the $q_markers will result not PDO placeholders like.
If this is missing, the query inserts all "" values, no matter if you'll adopt bindValue or bindParam**/
foreach($my_keys as &$key){
$key = ":".$key;
}
$q_markers = implode(",", $my_keys); // build PDO value markers
$stmt = $pdo->prepare("INSERT INTO my_table (".$field_names.") VALUES (".$q_markers.")");
foreach($Arr as $key => $val){
foreach($val as $bind_marker => &$bind_val){ /** # JBH Without "&" here, it will work
only bindValue. Instead with "&", they work both bindParam and bindValue **/
$stmt->bindParam($bind_marker, $bind_val);
}
$stmt->execute();
}
You can implode an associative array, but you cannot implode a multi-dimensional array. That's what the error is telling you. For example...
$my_array = array('a'=>'1', 'b'=>'2', 'c'=>'3');
echo "\n\n".implode(',',array_keys($my_array));
echo "\n\n".implode(',',$my_array)."\n\n";
Results in...
a,b,c
1,2,3
But...
$my_array = array(
array('a'=>'1', 'b'=>'2', 'c'=>'3'),
array('d'=>'4', 'e'=>'5', 'f'=>'6')
);
echo "\n\n".implode(',',array_keys($my_array));
echo "\n\n".implode(',',$my_array)."\n\n";
results in...
0,1
PHP Notice: Array to string conversion in /test.php on line 9
Fixing your code means dealing with the individual data elements. Echo'd out they'd look like this:
selected, user_id, user_name, user_company, user_email
,3,nome3,azien3,email3
1,6,nome6,azien6,email6
So, the basic code would look something like...
$fields = implode(",", array_keys($Arr));
echo $fields."\n";
foreach($Arr as $key=>$val){
$newdata = "'" . implode("','", $Arr[$key]) . "'";
echo $newdata."\n";
}
And a PDO INSERT statement would be built like this...
$my_keys = array_keys($Arr[0]);
$stmt=$pdo->prepare("DESC my_table");
$stmt->execute();
$whitelist_columns=$stmt->fetchAll(PDO::FETCH_COLUMN);
foreach($my_keys as $key){
if(!array_search($key,$whitelist_columns)){ echo "ERROR!"; }
}
$field_names = implode(",", $my_keys); // build column list
$q_markers = implode(",", $my_keys); // build PDO value markers
$stmt = $pdo->prepare("INSERT INTO my_table (".$field_names.") VALUES (".$q_markers.")");
foreach($Arr as $key => $val){
foreach($val as $bind_marker => $bind_val){
$stmt->bindParam($bind_marker, $bind_val);
}
$stmt->execute();
}
Note the section of code with the whitelist variables. The purpose of that code is to protect against SQL injection due to creating the query with unbound column references. PDO does not allow you to bind column names in the same way it does cell data. To protect yourself you must prove that the incoming data matches the columns in the table. If they don't, do something about it (echo "ERROR";). Usually you want to stop that INSERT completely and log the issue somewhere.
$my_keys = array_keys($Arr[0]);
$q_marks = array();
$stmt=$pdo->prepare("DESC my_table");
$stmt->execute();
$whitelist_columns=$stmt->fetchAll(PDO::FETCH_COLUMN);
foreach($my_keys as $key){
if(!array_search($key,$whitelist_columns)){ echo "ERROR!"; }
array_push($q_marks, "?");
}
$field_names = implode(",", $my_keys); // build column list
$field_markers = implode(",", $q_marks);
$stmt = $pdo->prepare("INSERT INTO my_table (".$field_names.") VALUES (".$field_markers.")");
foreach($Arr as $key => $val){
$stmt->execute($val);
}
The above code is an example of using PDO without bindParam or bindValue. It comes with a price, though usually there's no actual cost. bindParam and bindValue allow you to specifically identify the data type. E.G., bindParam('myval', $myval, PDO::PARAM_INT). When variables are passed as above, you can't do this. Most of the time this is a non-issue as PHP correctly identifies the data type. When PHP does become confused (or if you simply want to impose a check that the data is what you were expecting), then you must use bindParam or bindValue.

PHP/Mysql: Array instead of stdClass Object

I have the below code:
$result = $conn->query($sql)->fetch(PDO::FETCH_COLUMN);
$myArray = explode(',', $result);
$sql = ("SELECT username,status,location FROM Users WHERE username IN ('" . implode("','",$myArray) . "') AND status > 0 ORDER BY username");
$data = $conn->query($sql)->fetchAll(PDO::FETCH_OBJ);
print_r ($data);
FYI $result is "kk,bb"
The output is as follows:
Array ( [0] => stdClass Object ( [username] => bb [status] => 1 [location] => ) [1] => stdClass Object ( [username] => kk [status] => 1 [location] => ) )
But I really just want to see a two-dimensional array without it saying stdClass Object in front of each item. I tried different kinds of fetching, but this is the only one I could make work. I know there are functions for converting object to array, but I´d rather if there´s a way to do this right, if there is such a thing, in the pasted code.
I hope someone can help, thanks in advance!
You requested objects by using PDO::FETCH_OBJECT. If you want to fetch as arrays, use another fetch type:
$data = $conn->query($sql)->fetchAll(PDO::FETCH_ASSOC);
or
$data = $conn->query($sql)->fetchAll(PDO::FETCH_NUM);
The other choices are documented here: http://php.net/manual/en/pdostatement.fetch.php
By the way, why do you run one query to get the values in $myArray, then use these in an SQL-injection vulnerable way in a second query? Wouldn't it be easier to use a JOIN?
Re your comment:
Fist of all, I strongly urge you to code safely the first time, don't rely on "going back later." You probably won't have time to go back later, because once the code seems to work "good enough," the temptation is to go live immediately. Even if you do go back later to fix security flaws, you might miss one. Either way, your development process creates a high risk that you will go live with insecure code.
A piece of old advice from famous computer scientist Andrew S. Tanenbaum: "Security, like correctness, is not an add-on feature."
Now about joins. If you use SQL, you should understand how to do a joins. Not knowing joins in SQL is like thinking you know PHP even though you don't know how to use foreach(). Technically, you can still do some stuff, but you're missing out on a major part of the language.
Joins allow you to query multiple tables in one query, and find rows from each table that match.
In your example, I don't know your first query, but I'll take a guess that it queries some other table to get a list of usernames. Maybe it queries a list of users who are recipients of a email, like this:
SELECT username FROM EmailRecipients WHERE EmailId = 1234
Then your code uses the result list of usernames in a second query. But you can do it all in one query:
SELECT Users.username, Users.status, Users.location
FROM Users JOIN EmailRecipients
ON Users.username = EmailRecipients.username
WHERE EmailRecipients.EmailId = 1234
The SQL engine searches the EmailRecipients table and say it finds six rows for the recipients of email 1234. For each of the six rows, it looks up the corresponding row in the Users table with the matching username. Then it uses columns from those rows in the SELECT result.
When you join tables, there's a possibility both tables might have some columns with the same name. That's why I showed column named by qualifying them as belonging to one table or the other. This removes the ambiguity about which table you meant in each case.
It doesn't matter which table you list first. This type of join is the same left-to-right as it is right-to-level, like some expressions in algebra (2 + 4 is the same as 4 + 2, etc.). MySQL automatically figures out which table is most efficient to read first.
There's more to learn about joins, but that should get you started. I suggest you pick up a tutorial or a book on SQL sometime.
Re your comment:
Where is the "SQL-injection vulnerable way in a second query"?
In your code:
$result = $conn->query($sql)->fetch(PDO::FETCH_COLUMN);
$myArray = explode(',', $result);
$sql = ("SELECT username,status,location FROM Users WHERE username IN ('" . implode("','",$myArray) . "') AND status > 0 ORDER BY username");
$data = $conn->query($sql)->fetchAll(PDO::FETCH_OBJ);
There's no guarantee that the elements in $myArray are safe to use in an SQL statement. Just because they came out of the database doesn't make them safe. What if they contain names like "O'Reilly"? That will upset your quote-implosion.
You can never be 100% sure the data is safe, so it's better to use query parameters. Then you don't care if it's safe, because bound parameters are never combined with the SQL query string.
Here's how to create a dynamic list for the IN ( ) predicate from an array, and bind parameter values to the query using PDO:
$result = $conn->query($sql)->fetch(PDO::FETCH_COLUMN);
$myArray = explode(',', $result);
$placeholders = implode(',', array_fill(0, count($myArray), '?'));
$sql = "
SELECT username,status,location FROM Users
WHERE username IN ($placeholders) AND status > 0
ORDER BY username");
$stmt = $conn->prepare($sql);
$stmt->execute($myArray);
$data = $stmt->fetchAll(PDO::FETCH_OBJ);
While both answers are perfectly valid, I just wanted to add in that there is another solution to your issue.
You can change the default fetch style by defining it where you connect to the database:
$host = 'localhost';
$user = 'user';
$pass = 'pass';
$mydb = 'mydbname';
$conn = new PDO("mysql:host=$host;dbname=$mydb", $user, $pass, [
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
]);
Or you can change it by using setAttribute() using the existing connection:
$conn->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC);
Then you can simply use:
$conn->query($sql)->fetchAll();
And you still have the ability to overwrite the default value:
$conn->query($sql)->fetchAll(PDO::FETCH_NUM);
using PDO::FETCH_ASSOC you can make it as an array. So
$data = $conn->query($sql)->fetchAll(PDO::FETCH_ASSOC);
Documentation link:
PDO Statement

Insert large amount of array data into DB

I have a multi-dimensional array which contains ten thousands of data. A lot... The array is structured like this:
Array (
[0] => Array ( [0] => city [1] => code [2] => country )
[1] => Array ( [0] => city [1] => code [2] => country )
)
What I am trying to do is to insert the array values city, code and country into a table in a mysql database. I found posts that match exactly what I want to do, but for some reason it is not working with me. When I say it is not working I mean that the php doesn't even start. If I remove the here below code, the file runs normaly. So the problem really comes from that code portion. Hope someone will not mind helping me. Thank you in advance. Cheers. Marc.
//some code to build the array
//db_connect code
$sql = array();
foreach( $myarray as $row )
{
$sql[] = '("'.$row[0].'", "'.$row[1]).'","'.$row[2].'")';
}
mysql_query('INSERT INTO test (t_city, t_code, t_country) VALUES '.implode(',', $sql));
As said before, the error in building the sql array, is a surplus bracket. Change
$sql[] = '("'.$row[0].'", "'.$row[1]).'","'.$row[2].'")';
to
$sql[] = '("'.$row[0].'", "'.$row[1].'","'.$row[2].'")';
As ashein noted in comments, the query length is limited by the "max_allowed_paket" variable. If the query is larger than this, an error is raised and connection gets closed.
There is a bracket after $row[1] :)
Use this (remove the bracket):
$sql[] = '("'.$row[0].'", "'.$row[1].'","'.$row[2].'")';
You can try inserting every array record as separate sql-query.
foreach( $myarray as $row )
{
mysql_query('INSERT INTO test (t_city, t_code, t_country) VALUES ("'.$row[0].'", "'.$row[1]).'","'.$row[2].'");
}
but there would be a lot of queries
INSERT INTO tbl_name (a,b,c) VALUES(1,2,3),(4,5,6),(7,8,9);
You want to dynamically build such a query by submitting multiple of the value pairs at once and not running into limits.
So what you do is to build the insert query while adding iteratively one row after the other. If adding a row would trigger the limit, the query is send and the query is reset:
# sample data
$data = array(
array('city1', 'code', 'country'),
array('city2', 'code', 'country'),
array('city3', 'code', 'country'),
array('city4', 'code', 'country'),
array('city5', 'code', 'country'),
array('city6', 'code', 'country'),
array('city7', 'code', 'country'),
);
$max_allowed_packet = 1048576; # mysql default value
$max_allowed_packet = 128; # for demonstration purposes
$sql = new SQLInsertQuery('INSERT INTO test (t_city, t_code, t_country) VALUES ', $max_allowed_packet);
foreach($data as $row) {
$sql->addRow($row);
}
$sql->query(); # manually query any potential left-over query.
This example outputs the following:
Running: INSERT INTO test (t_city, t_code, t_country) VALUES ('city1','code','country'),('city2','code','country');
Running: INSERT INTO test (t_city, t_code, t_country) VALUES ('city3','code','country'),('city4','code','country');
Running: INSERT INTO test (t_city, t_code, t_country) VALUES ('city5','code','country'),('city6','code','country');
Running: INSERT INTO test (t_city, t_code, t_country) VALUES ('city7','code','country');
Demo, Gist
You might want to add a counter for the queries run as well so you can validate after the loop and the final query if at all a query was sent (the limit can be too low so that no query is send at all - depending on your data - so it's worth to have a sanity check for this edge-case).
I hope this example is helpful.

php+mysql: insert a php array into mysql

I have an array with 30000 plus entries that need to go into a MySQL table.
What is the best practice? From here? Lets say [0], [1] and [2] in the database would be 'title', 'type' and 'customer'
Is it add key names matching the column names in the table and call som "magic" function? Or build the query manually...
Array
(
[0] => Array
(
[0] => 2140395946
[1] => 1SAP
[2] => 0041451463
)
[1] => Array
(
[0] => 2140411607
[1] => 2SAP
[2] => 0041411940
)
[2] => Array
(
[0] => 2140706194
[1] => 4SAP
[2] => 0041411943
)
etc. etc.
UPDATE - based on answers
Thanks for the answers.
The solution would normally be to manually create the SQL-string and all rows can be inserted as one:
INSERT INTO `tx_opengate_table` (`machine` ,`customer` ,`type`)
VALUES
('m123', 'dfkj45', 'A'),
('m137', 'kfkj49', 'A'), "repeat this line for each entry in the array"
... ... ...
('m654321', '34dgf456', 'C4') "end with out comma, or remove last comma"
;
Special for TYPO3
I happen to do this in the CMS TYPO3 and just came across a new function added not that long ago:
//Insert new rows
$table = 'tx_opengate_stuff';
$fields = array('machine','type','customer');
$lines = "array as given above"
$GLOBALS['TYPO3_DB']->exec_INSERTmultipleRows($table,$fields,$lines);
I would say just build it yourself. You can set it up like this:
$query = "INSERT INTO x (a,b,c) VALUES ";
foreach ($arr as $item) {
$query .= "('".$item[0]."','".$item[1]."','".$item[2]."'),";
}
$query = rtrim($query,",");//remove the extra comma
//execute query
Don't forget to escape quotes if it's necessary.
Also, be careful that there's not too much data being sent at once. You may have to execute it in chunks instead of all at once.
Magic function? I'm guessing you mean some sort of DB abstraction layer? IMHO that would just double your work.
Just build the query manually looping through array[] INSERT'ing values as you go.
$statement = "INSERT INTO table (title, type, customer) VALUES ";
foreach( $data as $row) {
$statement .= ' ("' . implode($row, '","') . '")';
}
UPDATE: Changed explode to implode (I always get those confused).
You will have to build the query manually if you want the best performance during this operation. If you would iteratively add everything using PDO or some abstarction layer, you will have 30000+ insert queries.
Use foreach to iterate over the arraay, build one nested INSERT query that does all the work at once, and just send it to the server.

Categories