how do i convert a string like this:
"joe,anna,kwame,kofi"
to:
"joe","anna","kwame","kofi"
I am trying to use this in an sql statement with an IN clause but i keep getting the error as shown below:
Last SQL query failed:
SELECT * FROM view_salesreport WHERE deleted =0 AND saletype IN (dropoff,pickup)
Can anyone help
// split string into array based on commas
// result: array("joe","anna","kwame","kofi")
$arr = explode(",","joe,anna,kwame,kofi");
// create a string with a "?" for each item in the array
// result: ?, ?, ?, ?
$in = str_repeat('?,', count($arr) - 1) . '?';
// insert $in in SQL query
// result: SELECT * FROM view_salesreport WHERE deleted = 0 AND saletype IN (?, ?, ?, ?)
$sql = "SELECT * FROM view_salesreport WHERE deleted = 0 AND saletype IN ($in)";
// prepares a statement for execution
$stm = $db->prepare($sql);
// executes a prepared statement with array of parameters
$stm->execute($arr);
http://php.net/manual/de/function.explode.php
try something like this:
$output = ''
foreach (explode(',' , $yourString) as $name) {
$output . = $name
}
you can try this:
$string = "joe,anna,kwame,kofi";
$string = str_replace("'","\\'",$string);
$string = "'".str_replace(',',"','",$string)."'";
You can easily use explode() and implode() to get the string format you want. For example:
$initial_string = "joe,anna,kwame,kofi";
// split string into array based on commas
$names = explode(',', $initial_string);
// combine back into string using implode
$query = "SELECT * FROM view_salesreport WHERE deleted =0 AND saletype IN ('" . implode("','", $names) . "')";
Related
I am trying to generate a single insert statement that will insert multiple rows. I have and array of values, which is what I am wanting to insert into a table that all use the same userkey.
I have tried using a named PDO parameter and binding to that, then passing in the role array during execute but that doesn't work. So I moved on to placeholders, but I can't get that to work either.
I call my function like addUsersRoles(1, [100,101,102]);
And looking at the generated SQL I get:
INSERT user_roles (userkey, roleid) VALUES (?,?),(?,?),(?,?)
Which I think is the correct format for inserting multiple records.
Based up on that, what I am trying to generate is:
INSERT user_roles (userkey, roleid) VALUES (1,100),(1,101),(1,102)
How can I combine the power of PDO's binding to a SQL statement in this manner?
public function addUsersRoles($userkey, $roles = []){
$in = str_repeat('?,', count($roles) - 1) . '?';
$base_user_sql = 'INSERT user_roles (userkey, roleid) VALUES ';
$sql = $base_user_sql;
foreach ($roles as $role) {
//$sql .= "(:USERKEY, $in),"; // Didn't Work
$sql .= "($in),";
}
//Remove trailing comma
$sql = rtrim($sql, ',');
$db = static::getDB();
$stmt = $db->prepare($sql);
//$stmt->bindValue(':USERKEY', $userkey, PDO::PARAM_STR);
return $stmt->execute($roles);
}
You can use placeholders as well. Look at the following example:
public function addUsersRoles(string $userKey, array $roles = []): bool
{
$values = [];
$inputParameters = [':user_key' => $userKey];
foreach ($roles as $index => $role) {
$rolePlaceholder = ':roleid' . $index;
$values[] = sprintf('(:user_key, %s)', $rolePlaceholder);
$inputParameters[$rolePlaceholder] = $role;
}
$sql = 'INSERT INTO user_roles (user_key, roleid) VALUES ';
$sql .= implode(', ', $values);
$db = static::getDB();
$stmt = $db->prepare($sql);
return $stmt->execute($inputParameters);
}
This code will generate a query like this:
INSERT INTO user_roles (user_key, roleid) VALUES (:user_key, :roleid0), (:user_key, :roleid1), (:user_key, :roleid2), (:user_key, :roleid3), (:user_key, :roleid4);
And the $inputParameters will be like this:
[
':user_key' => 'some user key',
':roleid0' => 1,
':roleid1' => 2,
]
You shouldn't use count($roles) when making $in. It's always just ?, ?. You just need the count of roles when repeating that for all the rows. You can use array_fill to create an array of (?, ?) strings, and then implode to put commas between them.
You also need to insert create an array with alternating keys and roles, and use that as the parameters when executing.
public function addUsersRoles($userkey, $roles = []){
$values = implode(',', array_fill(0, count($roles), '(?, ?)'));
$base_user_sql = 'INSERT user_roles (userkey, roleid) VALUES ';
$sql = $base_user_sql . $values;
$keys_and_roles = [];
foreach ($roles as $role) {
$keys_and_roles[] = $userkey;
$keys_and_roles[] = $role;
}
$db = static::getDB();
$stmt = $db->prepare($sql);
return $stmt->execute($keys_and_roles);
}
I've simplified it a bit, but this splits into two parts that are done together. The first is to generate the SQL...
INSERT user_roles (userkey, roleid) VALUES (?,?),(?,?),(?,?)
This code just loops through the roles and adds (?,?), for each one.
The second part is building up the bind data. As the SQL needs a list of the data in the order userkey, roleid pairs, as it's building the SQL, it also adds these values to a data array at the same time.
So the main code comes out as...
public function addUsersRoles($userkey, $roles = []){
$sql = 'INSERT user_roles (userkey, roleid) VALUES ';
$binds = [];
foreach ( $roles as $role ) {
$sql .= "(?,?),";
$binds[] = $userkey;
$binds[] = $role;
}
//Remove trailing comma
$sql = rtrim($sql, ',');
$db = static::getDB();
$stmt = $db->prepare($sql);
return $stmt->execute($binds)
}
(Although I haven't been able to test the execute part).
You should also make sure any errors are being dealt with as well.
I have some SQL queries that use PHP string variables to create the query before PDO prepare().
$connection = new PDO(...);
// Make variable placeholder for each column.
$params = array();
foreach ($row as $col => $value) {
$params[':' . $col] = $value;
}
$columns = implode(', ', array_keys($row));
$values = implode(', ', array_keys($params));
$query = "
INSERT INTO my_table ($columns)
VALUES ($values)
";
$statement = $connection->prepare($query);
$statement->execute($params);
Or something similar with SELECT:
$query = "
SELECT field
FROM my_table
WHERE id IN ($ids)
";
Where the query will become
$query = "
SELECT field
FROM my_table
WHERE id IN (:id0, :id1, :id2)
";
and then the execute() function will pass in the params like array(':id0' => 0, ...).
Is this vulnerable to injection if the part being inserted is just a bunch of placeholders to be used for query preparation? And is there a better way to do this in PHP with PDO?
When binding a dynamic number of parameters, I revert to the ? placeholders. You can do:
$placeholders = implode(',', array_fill(0, count($values), '?'));
$query = "SELECT field FROM my_table WHERE id IN ($placeholders)";
$stmt = $pdo->prepare($query);
$stmt->execute($values);
You still need to do string substitution if the column names are dynamic, as in your INSERT example. Those should be white-listed to prevent injection. But you can use the above mechanism for the values being inserted. You'll need to use
$values = array_values($params);
because ? placeholders can't be filled in from an associative array.
Question:
Loop through a prepared PDO statement, check for duplicates, if no duplicates execute the query?
Current PDO Statement:
$STH = $DBH->prepare("INSERT INTO inbox (id,efrom,subject,msg,eread,date) VALUES ('',:efrom,:esubject,:emsg,:eread,:edate)");
$STH->bindParam(':efrom', $inbox_from[0]);
$STH->bindParam(':esubject', $inbox_subject[0]);
$STH->bindParam(':emsg', $inbox_msg[0]);
$STH->bindParam(':eread', $inbox_read[0]);
$STH->bindParam(':edate', $inbox_date[0]);
Why there needs to be a loop:
I need the arrays $inbox_* to be incremented then queried until it gets to the end of the array.
Example:
$inbox_from('hello','how','are','you');
someloop(somecondition) {
//output would be:
$STH->bindParam(':efrom', $inbox_from[0]);
$STH->bindParam(':efrom', $inbox_from[1]);
$STH->bindParam(':efrom', $inbox_from[2]);
$STH->bindParam(':efrom', $inbox_from[3]);
//It ends at [3] index because its the end of the array.
}
$STH = execute();
//So now it executes and should have put the 4 array indexes into different efrom columns. So column id 1 has 'hello' and id 4 has 'you'.
Maybe:
for($i = 0; //Something to check if array ended; ++$i) {
//Someway to Properly bind the arrays and execute?
}
or some use of a While Loop?
Hope I explained it my best.
Whats the best practice to use here?
Use numbered parameters instead of named parameters, and build the query and parameters dynamically.
$sql = "INSERT INTO inbox (efrom,subject,msg,eread,date) VALUES ";
// array_fill will create an array of N "(?, ?, ?, ?, ?)" strings
// implode will then join them together with comma separators
$sql .= implode(', ', array_fill(0, count($inbox_from), "(?, ?, ?, ?, ?)"));
$STH = $DBH->prepare($sql);
$params = array();
// Populate the $params array with all the input values
foreach ($inbox_from as $i => $from) {
$params[] = $from;
$params[] = $inbox_subject[$i];
$params[] = $inbox_msg[$i];
$params[] = $inbox_read[$i];
$params[] = $inbox_date[$i];
}
$STH->execute($params);
You can leave the id field out of the column list, and it will be filled in automatically using auto-increment.
To remove duplicate messages, you can do:
$check_stmt = $DBH->prepare("SELECT COUNT(*) AS count FROM inbox WHERE msg = :msg");
$check_stmt->bindParam(':msg', $msg);
$messages_seen = array();
foreach ($inbox_msg as $i => $msg) {
// Check if the message is already in the DB
$check_stmt->execute();
$first_row = $check_stmt->fetch(PDO::FETCH_OBJ);
$check_stmt->fetchAll(); // Fetch the rest of the query to get in sync
if ($first_row->count > 0) {
$messages_seen[$msg] = true; // Remember that we already saw this message
} elseif (!isset($messages_seen[$msg])) // If we haven't already seen this message
$params[] = $inbox_from[$i];
$params[] = $inbox_subject[$i];
$params[] = $msg;
$params[] = $inbox_read[$i];
$params[] = $inbox_date[$i];
$messages_seen[$msg] = true; // Remember that we added this message
}
}
$sql = "INSERT INTO inbox (efrom,subject,msg,eread,date) VALUES ";
// There's 1 (...) group for every 5 parameters, so divide the length of $params by 5 to know how many of them to put in the SQL
$sql .= implode(', ', array_fill(0, count($params)/5, "(?, ?, ?, ?, ?)"));
$STH = $DBH->prepare($sql);
$STH->execute($params);
When adding an index on a TEXT datatype, you have to specify the number of bytes of the text to store in the index. So it should be something like:
CREATE INDEX ix_msg ON inbox (msg(200));
You can create one big query if you are using Insert to same table
INSERT INTO table (columns) VALUES (....), (....)...., (...)
Its better than calling to sql for each row. Faster too
I can successfully implement a IN clause within a PDO prepared statement using the following code.
in_array = array(1,2,3);
$in = str_repeat('?,', count($in_array) - 1) . '?';
$sql = "SELECT * FROM my_table WHERE my_value IN ($in)";
$stm = $db->prepare($sql);
$stm->execute($in_array);
$data = $stm->fetchAll();
How can I do the same for multiple $in? For example I've unsuccessfully tried the following:
in_array1 = array(1,2,3);
$in1 = str_repeat('?,', count($in_array) - 1) . '?';
in_array2 = array(4,5,1);
$in2 = str_repeat('?,', count($in_array) - 1) . '?';
$sql = "SELECT * FROM my_table WHERE (my_value1 IN ($in1)) AND (my_value2 IN ($in2))";
$stm = $db->prepare($sql);
$stm->execute($in_array1,$in_array2);
$data = $stm->fetchAll();
I think its got to do with stm->execute but not sure, help appreciated
Your current query comes out as
SELECT * FROM my_table WHERE (my_value1 IN (?,?,?)) AND (my_value2 IN (?,?,?))
So your execute usage is incorrect, http://php.net/manual/en/pdostatement.execute.php. It should only be passing one array with values inside it.
An array of values with as many elements as there are bound parameters in the SQL statement being executed. All values are treated as PDO::PARAM_STR.
I think using array_merge, http://php.net/manual/en/function.array-merge.php, will allow you to accomplish what you are trying
$stm->execute(array_merge($in_array1,$in_array2));
This way the execute is the equivalent of
$stm->execute(array(1,2,3,4,5,1));
This may seem incorrect because the array pairings are now gone but placeholder 1 (the first question mark) will map to 1, placeholder 4 to 4, and so on.
Doesn't make sense.
Look at this:
$query = $db->prepare("SELECT * FROM table WHERE value1 = ? AND value2 = ?");
$query ->execute(array($value1, $value2));
and this:
http://php.net/manual/en/pdo.prepare.php
The following script is used to implode values from a multidimensional array and insert into the mysql table. The table contains field data types of varchar and decimal. Since varchar type requires quotes and to avoid selectively placing quotes, I would like to put all values in quotes. How to implement it?
$values = array();
foreach ($data as $rowValues) {
foreach ($rowValues as $key => $rowValue) {
}
$values[] = "(" . implode(', ', $rowValues) . ",'".$date."')";
}
$query = "INSERT INTO mem (memno,loan,subsc,intst, date)
VALUES " . implode (', ', $values);
$result=mysql_query($query) or die();
I want the sql like this
INSERT INTO mem (memno,loan,subsc,intst, date)
values('value1', 'value2', 'valu3','value4','value5')
Don't use user input to build SQL strings - thats how you get SQL injection attacks.
Instead use a prepared statement:
$mysqli = new mysqli('localhost', 'my_user', 'my_password', 'world');
$stmt = $mysqli->prepare("
INSERT INTO mem( memno, loan, subsc, intst, date )
VALUES (?, ?, ?, ?, ?);
");
$stmt->bind_param('sssss', $memno, $loan, $subsc, intst, $date);
edit in response to the comment:
Dynamically binding an array of columns is pretty easy with PDO.
$db =new PDO("mysql:host=localhost;dbname=database;","root","");
/**
* #param PDO $db
* #param string $table - the table to insert into
* #param array $columns - which columns do we want to insert into
* #param array $data - a key/value array of the data we want to insert
* #return bool
*/
function insert_into($db, $table, array $columns, array $data) {
$rows = implode(', ', $fields);
$placeholders = array_map ( function($key){ return ":$key" }, $fields);
$placeholders = implode(', ', $fields);
$sql = "
INSERT INTO $table ($fields)
VALUES ($placeholders);
";
$stmt = $db->prepare($sql);
foreach( $fields as $field) {
$stmt->bindParam(":$field", $data[$field]);
}
return $sth->execute();
}
$inserted = insertInto(
$db
'mem',
array("memno", "loan", "subsc", "intst", "date"),
$data
);
Notice that the columns to insert are defined separately.
If I had used:
array_keys($data);
It would lead to a mass assigment vulnerability if $data comes from user input and is not whitelisted.
You can accomplish the same thing with mysqli but its a bit trickier.
If $rowValues array is as below then you can do like this also.
$rowValues = array(
"memno"=>"a",
"loan"=>"b",
"subsc"=>"c",
"intst"=>"d"
);
$fldStr = array();
$valStr = array();
foreach($rowValues as $key=>$val) {
array_push($fldStr, $key);
$v2 = "'" . $val . "'";
array_push($valStr, $v2);
}
array_push($fldStr, "date");
array_push($valStr, "'" . $date . "'");
$flds = implode(", ", $fldStr);
$vals = implode(", ", $valStr);
$query = "INSERT INTO mem ($flds) values($vals)";