I have a function to update up to 3 fields in a mysql table. The function can receive all 3 fields to be updated or just 1 or 2
Right now I am doing it like this (it works) to construct MySQL statement.
if ($foo1){
$mysql_set = '`foo1` = :foo1';}
if ($foo2){
if ($mysql_set){$mysql_set .= ', ';}
$mysql_set .= '`foo2` = :foo2';}
if ($foo3){
if ($mysql_set){$mysql_set .= ', ';}
$mysql_set .= '`foo3` = :foo3';}
$update = $db->prepare("UPDATE `bar` SET $mysql_set WHERE `id` = :id");
if ($foo1){
$update->bindValue(':foo1', $foo1, PDO::PARAM_STR);}
if ($foo2){
$update->bindValue(':foo2', $foo2, PDO::PARAM_STR);}
if ($foo3){
$update->bindValue(':foo3', $foo3, PDO::PARAM_STR);}
$update->bindValue(':id', $id, PDO::PARAM_INT);
$update->execute();
As you can see I am repeating "if ($foo1 - $foo3){}" twice to construct this MySQL query. It looks redundant and wondering if there's a better way to handle this scenario.
You can give an associative array to execute(), instead of calling bindValue() separately for each parameter.
$param_array = array(':id' => $id);
$set_array = array();
if ($foo1) {
$param_array[':foo1'] = $foo1;
$set_array[] = "foo1 = :foo1";
}
if ($foo2) {
$param_array[':foo2'] = $foo2;
$set_array[] = "foo2 = :foo2";
}
if ($foo3) {
$param_array[':foo3'] = $foo3;
$set_array[] = "foo3 = :foo3";
}
if (!empty($set_array)) {
$set_string = implode(", ", $set_array);
$sql = "UPDATE bar SET $set_string WHERE id = :id";
$update = $db->prepare($sql);
$update->execute($param_array);
}
Try.
if ($foo1){
$mysql_set = '`foo1` = :foo1';
$update = prepareStmt($db, $mysql_set);
$update->bindValue(':foo1', $foo1, PDO::PARAM_STR);
}
if ($foo2){
if ($mysql_set){$mysql_set .= ', ';}
$mysql_set .= '`foo2` = :foo2';
$update = prepareStmt($db, $mysql_set);
$update->bindValue(':foo2', $foo2, PDO::PARAM_STR);
}
if ($foo3){
if ($mysql_set){$mysql_set .= ', ';}
$mysql_set .= '`foo3` = :foo3';
$update = prepareStmt($db, $mysql_set);
$update->bindValue(':foo3', $foo3, PDO::PARAM_STR);
}
$update->bindValue(':id', $id, PDO::PARAM_INT);
$update->execute();
function prepareStmt($db,$mysql_set){
return $db->prepare("UPDATE `bar` SET $mysql_set WHERE `id` = :id");
}
Related
Trying to get a function working to create simple CRUD "Select" with multiple parameters to any table. I think I got the hardest part, but couldn't fetch the data right now. Maybe I'm doing something wrong I can't figure out.
My prepared statement function:
function prepared_query($mysqli, $sql, $params, $types = ""){
$types = $types ?: str_repeat("s", count($params));
if($stmt = $mysqli->prepare($sql)) {
$stmt->bind_param($types, ...$params);
$stmt->execute();
return $stmt;
} else {
$error = $mysqli->errno . ' ' . $mysqli->error;
error_log($error);
}
}
The query creator:
function create_select_query($table, $condition = "", $sort = "", $order = " ASC ", $clause = ""){
$table = escape_mysql_identifier($table);
$query = "SELECT * FROM ".$table;
if(!empty($condition)){
$query .= create_select_query_where($condition,$clause);
}
if(!empty($sort)){
$query .= " ORDER BY ".$sort." $order";
}
return $query;
}
The helper function to create the WHERE clause:
function create_select_query_where($condition,$clause){
$query = " WHERE ";
if(is_array($condition)){
$pair = array();
$size = count($condition);
$i = 0;
if($size > 1){
foreach($condition as $field => $val){
$i++;
if($size-1 == $i){
$query .= $val." = ? ".$clause. " ";
}else{
$query .= $val." = ? ";
}
}
}else{
foreach($condition as $field => $val){
$query .= $val." = ? ";
}
}
}else if(is_string($condition)){
$query .= $condition;
}else{
$query = "";
}
return $query;
}
The select function itself:
function crud_select($conn, $table, $args, $sort, $order, $clause){
$sql = create_select_query($table, array_keys($args),$sort, $order, $clause);
print_r($sql);
if($stmt = prepared_query($conn, $sql, array_values($args))){
return $stmt;
}else{
$errors [] = "Something weird happened...";
}
}
When I create the query, it seems to be OK but can't fetch the data. If I create an array with only one argument the query translates into:
SELECT * FROM `teste_table` WHERE id = ?
If I create with multiple parameters, it turns like this:
SELECT * FROM `teste_table` WHERE id = ? AND username = ?
So, how can I properly fetch the data from the select. This should be used for multiple purposes, so I could get more than one result, so the best way would be fetch data as array I guess.
I guess I'm close, but can't figure it out. Thanks
I told you to limit your select function to a simple primary key lookup. And now you opened a can of worms. As a result you are getting entangled implementation code and unreadable application code.
$table, $args, $sort, $order, $clause
What all these variables are for? How you're going to call this function - a list of gibberish SQL stubs in a random order instead of plain and simple SQL string? And how to designate a list of columns to select? How to use JOINS? SQL functions? Aliases? Why can't you just write a single SQL statement right away? You already have a function for selects, though without this barbaric error reporting code you added to it:
function prepared_query($mysqli, $sql, $params, $types = ""){
$types = $types ?: str_repeat("s", count($params));
$stmt = $mysqli->prepare($sql)) {
$stmt->bind_param($types, ...$params);
$stmt->execute();
return $stmt;
}
Just stick to it and it will serve you all right.
$sql = "SELECT * FROM `teste_table` WHERE id = ? AND username = ?";
$stmt = prepared_query($mysqli, $sql, [$id, $name]);
$row = $stmt->get_result()->fetch_assoc();
The only specific select function could be, again, a simple primary key lookup:
function crud_find($conn, $table, $id)
{
$table = escape_mysql_identifier($table);
$sql = "SELECT * FROM $table WHERE id=?";
$stmt = prepared_query($conn, $sql, [$id], "i");
return $stmt->get_result()->fetch_assoc();
}
And for the everything else just use a generic function with native SQL.
so far I've come to this.
My goal is to be able to apply optional search filters such as a.chainid and a.branchid if needed, for that to happen I need dynamic bind_param.
The code seems to be fine, but in fact it's not working for some reason.
Care to tell me what's wrong?
The fetch returns me nothing instead of 5 rows that should.
Thanks in advance
$sql = "SELECT a.COUPONID, a.TRUSTANDUSEID FROM `custom_redemptions` a WHERE a.couponid = 3";
$types = '';
$params = array(&$types);
if ($branchid != null) {
$sql .= "AND a.branchid = ?";
$types .= 's';
$params[] = $branchid;
}
if ($chainid != null) {
$sql .= "AND a.chainid = ?";
$types .= 's';
$params[] = $chainid;
}
if ($stmt = $this->dbCon->prepare($sql)) {
call_user_func_array(array($stmt, 'bind_param'), $params);
$stmt->execute();
$stmt->bind_result($couponid, $trustanduseid);
while ($stmt->fetch()) { echo $couponid; }
$stmt->close();
}
This solution might help you
$sql = "SELECT a.COUPONID, a.TRUSTANDUSEID FROM `custom_redemptions` a WHERE a.couponid = 3";
$types = '';
$params = array(&$types);
if ($branchid != null) {
$sql .= " AND a.branchid = ?";
$types .= 's';
$params[] = $branchid;
}
if ($chainid != null) {
$sql .= " AND a.chainid = ?";
$types .= 's';
$params[] = $chainid;
}
if ($stmt = $this->dbCon->prepare($sql)) {
call_user_func_array(array($stmt, 'bind_param'), $params);
$stmt->execute();
$stmt->bind_result($couponid, $trustanduseid);
while ($stmt->fetch()) { echo $couponid; }
$stmt->close();
}
Finally what was needed was &$chainid instead of $chainid.
Spent 8 hours researching about it. LOL
I've made a function to query the database. This function takes an array, the id of the user I want to update
and a query operation.
if the query operation is UPDATE
if you look at the code below, would this be a good coding practice or is this bad code?
public function query($column, $search_value, $query_operation = "SELECT"){
if(strtoupper($query_operation == "UPDATE")){
$query = "UPDATE users SET ";
if(is_array($column)){
$counter = 1;
foreach($column as $key => $value){
if($counter < count($column)){
$query .= $key . ' = ?, ';
}else{
$query .= $key . ' = ? ';
}
$counter++;
}
$query .= "WHERE id = ?";
$stmt = $this->database->prepare($query);
$counter = 1;
foreach($column as $key => &$value){
$stmt->bindParam($counter, $value);
$counter++;
}
$stmt->bindParam($counter, $search_value);
if($stmt->execute()){
$stmt = $this->database->prepare("SELECT* FROM
users WHERE id = ?");
$stmt->bindParam(1, $search_value, PDO::PARAM_INT);
$stmt->execute();
return $this->build_array($stmt);
}
}
}
}
would love to hear some feedback.
I would NOT mix SELECT and UPDATE in the same function.
The following update function uses arrays for column names and values $columnNames & $values using unnamed parameters.
function update($tableName,$columnNames,$values,$fieldName,$fieldValue){
$sql = "UPDATE `$tableName` SET ";
foreach($columnNames as $field){
$sql .= $field ." = ?,";
}
$sql = substr($sql, 0, -1);//remove trailing ,
$sql .= " WHERE `$fieldName` = ?";
return $sql;
}
As table and column names cannot be passed as parameters in PDO I have demonstrated whitelistng of table names.
$tables = array("client", "Table1", "Table2");// Array of allowed table names.
Also array_push()to add value for last parameter (WHERE) into $values array
Use
if (in_array($tableName, $tables)) {
$sql = update($tableName,$columnNames,$values,$fieldName,$fieldValue);
array_push($values,$fieldValue);
$STH = $DBH->prepare($sql);
$STH->execute($values);
}
You can use similar technique for SELECT
I get data from a querystring like below, where all 3 country, state and sub may not be set.
Sometimes it may be
http://www.example.com/index.php?country=US&state=california&sub=sanjose
http://www.example.com/index.php?country=US&state=california
http://www.example.com/index.php?country=US
Then I do:
$stmt = $conn->prepare('select username from arraytest where country = :country and state = :state and sub = :sub');
$stmt->bindParam(':country', $_GET['country']);
$stmt->bindParam(':state', $_GET['state']);
$stmt->bindParam(':sub', $_GET['sub'])
$stmt->execute();
while($rows = $stmt->fetch()) {
echo $rows['username'];
echo '<br>';
}
This'll work only if all the three are bound. If any one isn't received, no results are returned.
Is it possible to get it to work even if all the three where not bound?
Example
http://www.example.com/index.php?country=US would display results for
select username from arraytest where country = US
http://www.example.com/index.php?country=US&state=california would display results for
select username from arraytest where country = US and state = california
http://www.example.com/index.php?country=US&state=california&sub=sanjose would display results for
select username from arraytest where country = US and state=california and sub=sanjose
Something like this should do it;
$sql = 'select username from arraytest where country = :country';
if(isset($_GET['state')) {
$sql .= ' and state = :state';
if(isset($_GET['sub')) {
$sql .= ' and sub = :sub';
$stmt = $conn->prepare($sql);
$stmt->bindParam(':country', isset($_GET['country']) ? $_GET['country'] : 'US');
if(isset($_GET['state')) {
$stmt->bindParam(':state', $_GET['state']);
if(isset($_GET['sub')) {
$stmt->bindParam(':sub', $_GET['sub']);
EDIT: If it's used in many places, you may want to make a simple function, something like;
function buildstmt($conn, $base, $params, $arr) {
$prefix = ' WHERE ';
foreach($params as $param=>$value) {
if(isset($arr[$param])) $value = $arr[$param];
if($value != null) {
$base .= $prefix.$param.'=:'.$param;
$prefix = ' AND ';
}
}
$stmt = $conn->prepare($sql);
foreach($params as $param=>$value) {
if(isset($arr[$param])) $value = $arr[$param];
if($value != null)
$stmt->bindParam(':'.$param, $value);
}
return $stmt;
}
Then you can call it as;
$stmt = buildstmt($conn, 'select username from arraytest',
array('country'=>'US', 'state'=>null, 'sub'=>null), $_GET);
$stmt->execute();
Prepare sql with condition
$sql = 'SELECT `username` FROM `arraytest` WHERE 1';
if(!empty($_GET['country'])) {
$sql .= ' AND country = :country ';
}
if(!empty($_GET['state'])) {
$sql .= ' AND state = :state ';
}
if(!empty($_GET['sub'])) {
$sql .= ' AND sub = :sub ';
}
$stmt = $conn->prepare($sql);
And bindParam like this :
if(!empty($_GET['country'])) {
$stmt->bindParam(':country', $_GET['country']);
}
if(!empty($_GET['state'])) {
$stmt->bindParam(':state', $_GET['state']);
}
if(!empty($_GET['sub'])) {
$stmt->bindParam(':sub', $_GET['sub']);
}
$stmt->execute();
I came up with this:
$sql = 'select userName from arraytest where ';
if(!empty($_GET['country'])){
echo 'Country Set <br>';
$sql .= 'country = :country';
$exe[':country'] = $_GET['country'];
} else { echo 'Country not set';}
if(!empty($_GET['state'])){
echo 'State Set <br>';
$sql .= ' and state = :state';
$exe[':state'] = $_GET['state'];
} else { echo 'State not set';}
if(!empty($_GET['sub'])){
echo 'Sub Set <br>';
$sql .= ' and sub = :sub';
$exe[':sub'] = $_GET['sub'];
}else{ echo 'Sub not set';}
print_r($exe);
$stmt = $conn->prepare($sql);
$stmt->execute($exe);
while($rows = $stmt->fetch()) {
echo $rows['userName'];
echo '<br>';
}
Just can't seem to print the binded values without executing the query. Looking to debug the query before execution. Any tips? I know I'm overlooking something simple, ugh...
$field1 = 'one';
$field2 = 'two';
$field3 = 'three';
$fields = 'SET ';
$fields .= 'field1 = ?, ';
$fields .= 'field2 = ?, ';
$fields .= 'field3 = ? ';
$vals[] = $field1;
$vals[] = $field2;
$vals[] = $field3;
$sql = 'UPDATE table_name '.$fields.' WHERE id = 123';
$dbh = $db->prepare($sql);
// this binds and executes the query but I would like to print the query with the bind values before executing
$results = $db->execute($dbh, $vals);
UPDATE:
I would do something like this with sprinf
$field1 = 'one';
$field2 = 'two';
$field3 = 'three';
$fields = 'SET ';
$fields .= 'field1 = %s, ';
$fields .= 'field2 = %s, ';
$fields .= 'field3 = %s ';
$vals[] = $field1;
$vals[] = $field2;
$vals[] = $field3;
$sql = 'UPDATE table_name '.$fields.' WHERE id = 123';
$query = sprintf($sql, $field1, $field2, $field3);
echo "Query before execution: ".$query."<br />";
You can't get the values inside the query like that. The way the server handles prepared queries is different.
The best you could do is:
echo $sql;
print_r($vals);
Retrieve (or simulate) full query from PDO prepared statement