bind_param in mysql from JSON - php

I'd like to create a 'one fits them all' function for updating an SQL database.
Imagine I have a database table with columns :
id (int)
col1 (varchar)
col2 (varchar)
col3 (varchar)
col4 (varchar)
col5 (int)
Now the php script receives some JSON like this :
{ id : 5,
col3 : "somevalue",
col5 : 17 }
So I would like to recreate an update function with a clean prepared MySqli statement:
$stmt = $db->prepare("UPDATE `table` SET `col3` = ?, `col5` = ? WHERE `id` = ?;");
$stmt->bind_param("sii", $obj->col3, $obj->col5, $obj->id);
$stmt->execute();
Seems quite simple at first, but I don't get how I'm supposed to dynamically recreate the argument list inside the bind_param method. It does not take an array, but all values in different parameters..
Question : How to recreate the bind_param without knowing on the forehand how many columns and what kind they are.
listing the kind of variable is simple, iterating over all object parameters and creating a string containing "sii" in this case..
json object properties always reflect table's columns.

As suggested by Rajdeep Paul, I switched to PDO.
PDO offers more flexibility, in a way it accepts an array of named parameters. Final code looks like this for anyone who wants to achieve the same :
$data = $_POST['carData'];
$params = [];
$updateString = "";
foreach ($data as $key => $value)
{
if($key != "id")$updateString .= $key." = :".$key.",";
$params[':'.$key] = $value;
}
$updateString = rtrim($updateString, ","); //remove last ,
$stmt = $pdo->prepare("UPDATE cars SET ".$updateString." WHERE id = :id");
$stmt->execute($params);
I just remove the 'id' of the update string as it really makes no sense, the update is on 1 id in particular, but could probably be ignored.

Related

PHP dynamically add new column and value in existing Query

My application performs INSERT queries like this:
INSERT INTO table (`col1`, `col2`, `col3`) VALUES ('oneVal', 'twoVal', 'threeVal')
Now I want to rebuild my application so it will ALWAYS SELECT, INSERT, DELETE and UPDATE with a specific id.
Let's say the unique id is called: companyId
I don't want to rewrite all my queries manually, so I am trying to write a function that rewrites the existing SQL queries with PHP so it will include the companyId inside the query.
Desired outcome if companyId would be '1' (companyId IS NOT ALWAYS '1'!):
INSERT INTO table (`col1`, `col2`, `col3`, `companyId`) VALUES ('oneVal', 'twoVal', 'threeVal', '1')
My question(s) is/are:
Is there a way in PHP so I can dynamically rewrite the query so
it would include the companyId column and the matching id value?
Is there a better way to do this? Like some trick setting MySQL
server to ALWAYS use an extra value (in this case companyId='1'
?
I've tried option (1) by searching for the string
) VALUES
Once I found that string, I add companyId before the ).
Now get to the end of the query, get the most right ) and add the value before that.
But is this for a generic case? I think there might be a better way to solve this.
Thanks in advance community!
EDIT 1 with more clarification
Currently I've already built a function that modifies my SELECT statements.
Function code:
//If current query = SELECT query
if (containsString($sql, 'select')) {
//Check if contains WHERE
if (containsString($sql, 'where')) {
//Yes
//Add companyId after WHERE
$sql = substr_replace($sql, '(companyId=?) AND ', strpos($sql, 'WHERE') + 6, 0);
//Explanation:
//SELECT * FROM table WHERE deleted='No'; becomes -->
//SELECT * FROM table WHERE (companyId=?) AND deleted='No';
}else{
//No
//Get table , and after that INSERT WHERE companyId=?
$tableName = explode(' from ', strtolower($sql))[1]; //Get part AFTER 'from'
//First word after $tableName = tablename
$tableName = explode(' ', $tableName)[0]; //First word after 'from' = tablename
$sql = substr_replace($sql, 'WHERE (companyId=?) ', strpos($sql, $tableName) + strlen($tableName) + 1, 0);
//Explanation:
//SELECT * FROM table ORDER BY id; becomes -->
//SELECT * FROM table WHERE (companyId=?) ORDER BY id;
}
}
So this code dynamically adds an extra condition to the query statement.
This is also easily possible with DELETE and UPDATE statements (same as SELECT)
But Iam trying to come up with something like this for INSERT INTO queries.
How can I modify the original query using the new companyId?
I guess If you have an associative array with the column names and values then you easily can make it more dynamic for future also. Let's say you've an array of column names with value of it e.g
$data = ['col1'=>'val1','col2'=>'val2','col3'=>'val3','companyId'=>1];
$query = "INSERT INTO `yourtable` ( ".implode(' , ', array_keys($data)).") VALUES ('".implode("' , '", array_values($data))."')";
echo $query;
DEMO: https://3v4l.org/udt1i
Then you can do with regex replace way globally to add column and value to all of your 100 query.
<?php
$re = '/\) VALUES (.+?(?=\)))/m';
$str = 'INSERT INTO table (`col1`, `col2`, `col3`) VALUES (\'oneVal\', \'twoVal\', \'threeVal\')';
$subst = ',`col4`) VALUES $1 , \'1\'';
$result = preg_replace($re, $subst, $str);
echo $result;
?>
DEMO: https://3v4l.org/rOQDG

Ordering Mechanism in Mysql

I have a matter in PHP & Mysql Project.
Simply, I have two tables project and project features,
Every project has as specific features.
CREATE TABLE projects (
ID INT NOT NULL PRIMARY KEY AUTO_INCREMENT,
name varchar(255) NOT NULL
);
CREATE TABLE projects_features (
projectId INT NOT NULL,
name varchar(255) NOT NULL,
value varchar(255) NOT NULL,
weight INT NOT NULL
);
INSERT INTO projects VALUES (NULL,'project1');
INSERT INTO projects VALUES (NULL,'project2');
INSERT INTO projects_features VALUES (1,'Feature1','Feature1 Value',1);
INSERT INTO projects_features VALUES (2,'Feature2','Feature2 Value',2);
INSERT INTO projects_features VALUES (1,'Feature3','Feature3 Value',3);
INSERT INTO projects_features VALUES (2,'Feature4','Feature4 Value',4);
INSERT INTO projects_features VALUES (1,'Feature5','Feature5 Value',5);
I Get the Project features by:
SELECT * FROM projects_features WHERE projectId = 1 ORDER BY weight ASC;
So the bigger weight will be down and lower weight will be Up.
Now,
In My View I have move up and move down buttons, so I can re-sort project features.
I can firstly select the current item weight then select the upper item weight ,
then type two update queries to exchange the weight between the two rows,
but it's not a professional way , I don't like to use four queries.
I need to do it in one query Instead of four queries.
Can anybody help please ?
Here's how I'd tackle this, assuming I've understood the question.
First, I'd add a featureId column to projects_features, and make (projectId, featureId) the composite primary key. This isn't actually necessary to my solution; it just makes the rest a whole lot easier. For the next part you need to be able to reference individual records in projects_features.
Next, I'd have the Move buttons populate an array in PHP, like
$update_list = array();
$update_list[i] = array(':project_id' => $proj_id,
':feature_id' => $feat_id,
':new_weight' => $weight);
Finally, I'd do the update through a PHP function that encapsulates the UPDATE statements, like this:
function updateWeights($update_array, $dbh)
{
$sql = 'UPDATE project_features
SET weight = :new_weight
WHERE projectId = :project_id
AND featureId = :feature_id'
$stmt = $dbh->prepare($sql);
foreach ($update_array as $update_item)
{
$stmt->execute($update_item);
}
}
Note I'm using PDO here; it could also be done with mysqli, although mysqli doesn't support named bind parameters so the syntax would be slightly different, something like
function updateWeights($update_array, $dbh)
{
$sql = 'UPDATE project_features
SET weight = ?
WHERE projectId = ?
AND featureId = ?'
$stmt = $dbh->prepare($sql);
foreach ($update_array as $update_item)
{
$stmt->bind_param('i', $update_item[':new_weight']);
$stmt->bind_param('i', $update_item[':project_id']);
$stmt->bind_param('i', $update_item[':feature_id']);
$stmt->execute();
}
}
Also note that I haven't actually run this code, and so make no representation that it is free of syntax or other errors.
I hope that helps.

(PDO PHP) The fastest way to update or insert multiple rows?

I don't know how to update or insert multiple rows by using PDO. Please help me.
Something that is in my mind is:
$stmt = $dbh->query("update_line_1; update_line_2; update_line_3");
//update_line_1: update table a set a.column1 = "s1" where a.id = 1
//update_line_2: update table a set a.column1 = "s2" where a.id = 2
//....
$stm = $dbh->query("insert_line_1; insert_line_3; insert_line_3");
//something is like the update line above.
I don't know this way works or not. And If you have another way, please let me know. Thank you so so much.
And if I use prepare statement, I just update each row each time. (This is much more safe than above)
$stmt = $dbh->prepare("update table a set a.colum1 = :column1 where a.id = :id");
$stmt->bindParam(":column1","s1");
$stmt->bindparam(":id",1);
$stmt->execute();
The most hate thing I don't want to do is using a loop goes through all elements in an array, and update or insert each element each time
Is another way to mass safely update or insert multiple rows to database? thank for your help.
Sorry about my English.
For inserts, you can insert multiple rows worth of data with the following syntax:
INSERT INTO table (col1, col2, col3)
VALUES
('1', '2', '3'),
('a', 'b', 'c'),
('foo', 'bar', 'baz')
For updates, the update will by default effect as many rows as meet the criteria of the query. So something like this would update an entire table
UPDATE table SET col = 'a'
If you are trying to update different values for each row, you don't really have much of a choice other than to do a query for each operation. I would suggest however that, building on your PDO example, you could do something like this:
$update_array = array(
1 => 'foo',
2 => 'bar',
10 => 'baz'
); // key is row id, value is value to be updated
$stmt = $dbh->prepare("UPDATE table SET column1 = :column1 where id = :id");
$stmt->bindParam(":column1",$column_value);
$stmt->bindparam(":id",$id);
foreach($update_array as $k => $v) {
$id = $k
$column_value = $v;
$stmt->execute();
// add error handling here
}
With this approach you are at least leveraging the use of the prepared statement to minimize query overhead.

PDO MYSQL Update Only If Different

I have a web program which allows the administrator to update a user's information... With that being said, I only want columns updated which have indeed been 'updated'...
I have done quite a bit of researching on this and it seems that all methods use outdated querys, which do not make use of the prepare statement to escape input...
Can someone please help me with the statement?
Essentially in psuedocode:
Update FIRSTNAME if $editedUserdata['firstname'] != FIRSTNAME, LASTNAME if $editedUserData['lastname'] != LASTNAME ...etc...
Here is what I have for the post code...
$password = sha1($password);
$editedUserData = array(
'firstname' => $firstname,
'lastname' => $lastname,
'username' => $username,
'password' => $password,
'cellphone' => $cellphone,
'security_level' => $seclvl,
'email' => $email,
'direct_phone' => $direct,
'ext_num' => $extension,
'is_active' => $userflag
);
Then it should be something like
$query = $this->db->prepare('UPDATE FIRSTNAME if(?) IS NOT FIRSTNAME, LASTNAME if(?) IS NOT LASTNAME, USERNAME if (?) IS NOT USERNAME.... VALUES (:firstname, :lastname, :username).....'
if ($query -> execute($editedUserData)) {
more code....
According to MySQL documentation -
Ref: (http://dev.mysql.com/doc/refman/5.0/en/update.html)
"If you set a column to the value it currently has,
MySQL notices this and does not update it."
Maybe I'm not understanding the problem which you're trying to solve but you don't have to test if field value did change.
If field value is "A" and you put there an "A" it will remain the same otherwise, if you put there a "B" it will be updated as expected
The prepared statement would be something like
$stmt = $dbh->prepare("
UPDATE table_name
SET
field1 = :value1,
field2 = :value2
WHERE
field0 = :key
");
$stmt->bindParam(':value1', $value1, PDO::PARAM_STR);
$stmt->bindParam(':value2', $value2, PDO::PARAM_STR);
$stmt->bindParam(':key', $key, PDO::PARAM_INT);
$stmt->execute()
Run a single statement to update the row.
Firstly, what's the unique identifier for a row in the users table, is there a unique userid or username? You'll want a WHERE clause on the UPDATE statement so that only that row will be updated.
The normative pattern for an UPDATE statement to update several columns in a single row is like this:
UPDATE users
SET col2 = 'value'
, col3 = 'another value'
, col4 = 'fi'
WHERE idcol = idvalue ;
To use a prepared statement with PDO, the SQL text could look something like this, if you use named placeholders:
UPDATE users
SET col2 = :col2_value
, col3 = :col3_value
, col4 = :col4_value
WHERE idcol = :id_value
Or this, if you use positional notation for the placeholders:
UPDATE users
SET col2 = ?
, col3 = ?
, col4 = ?
WHERE idcol = ?
(My personal preference is to use the named placeholders, rather than positional, but either will work.)
This is how I'd do it, run the prepare, then the bind_param, and then the execute.
$sql = "UPDATE users
SET col2 = :col2_value
, col3 = :col3_value
, col4 = :col4_value
WHERE idcol = :id_value ";
$stmt = $dbh->prepare($sql);
$stmt->bindParam(':col2_value', $col2_val, PDO::PARAM_STR);
$stmt->bindParam(':col3_value', $col3_val, PDO::PARAM_STR);
$stmt->bindParam(':col4_value', $col4_val, PDO::PARAM_STR);
$stmt->bindParam(':id_value' , $id_val, PDO::PARAM_STR);
$stmt->execute();
To do something different, to dynamically create the SQL text, and adjust the bindParam calls, that would add unnecessary complexity to the code. There's no performance advantage to doing that; when that UPDATE statement runs, MySQL has to lock the row, store a new copy of the row. It doesn't really save anything (aside from a few bytes of data transfer) to avoid sending a column value that hasn't changed.
If you realy want to use cases, read this.
There is no reason to do it in your case, as stated from #spencer7593 in the comments:
That's WAY more overhead... roundtrips to the database, parsing the
statement, developing an execution plan, executing the statement,
obtaining locks, returning a status, client checking the status, etc.
That's just seems an all-around inefficient approach.
I assume that any RDBMS is smart enough, to notice, that Caches etc should not be recalculated (if nothing changes), if that is the problem.

What is the best way to loop through string and insert into mysql column?

I have a variable which displays the following string:
$item_value = itemOne,itemTwo,itemThree
I would like to take this string and have insert each item as a separate row entry for a single column. Additionally, I need it to insert an auto increment key value for each entry. So to complete this example, here is what I would want the mysql table to look like when complete:
ID || item_value || comments
----------------------------------------
1 || itemOne || --------------
2 || itemTwo || --------------
3 || itemThree || --------------
My feeling is that I need to explode the string around the comma and then insert it into the table. I have attempted this but am having some issues getting each item as separate row entries. Any assistance is much appreciated.
For the auto-increment i suggest letting the database handle it, for mysql just declare it with AUTO_INCREMENT on the id field, for postgres you can set the data type to serial, as for separating each line use the php explode function
here's a little example
<?php
$dbh = new PDO('mysql:host=localhost;dbname=database', 'username', 'password');
$query = "INSERT into my_table (item_value) VALUES (?)";
$data = 'itemOne,itemTwo,itemThree,itemFour';
$st = $dbh->prepare($query);
foreach(explode(',', $data) as $r) {
// user array($r) for php 5.3 or lower
$st->execute([$r]);
}
This uses PDO which is the recommended method for handling database connections
Something like this should split them up and give the option for insertion in the db:
<?php
$item_value = 'itemOne,itemTwo,itemThree';
$item_array = explode(",",$item_value);
foreach($item_array as $key => $value){
// insert into the db here
$query = "INSERT INTO table_name set item_value = '".mysql_real_escape_string($value)."', ID = '".($key + 1)."'";
// however you choose to connect and insert into the database goes here :)
}
?>

Categories