Getting call_user_func_array() to work with prepared sql statements - php

I have to build up a sql statement and the params from a $place object that has a variable number of properties. When I use prepared sql statements by building the sql statement and params the long and bad practice way it works (returns all the rows from the database that it should):
<?
function buildSQLWhereClause($query, $conn, $place) {
if ($place['suburb']){
if($place['city'] && $place['province'] && $place['country']) {
$query .= "s.country = ? and
s.province = ? and
s.city = ? and
s.suburb = ?";
// prepare and bind
$stmt = $conn->prepare($query);
$stmt->bind_param("ssss", $place['country'], $place['province'], $place['city'], $place['suburb']);
} else if ($place['province'] && $place['country']) {
$query .= "s.country = ? and
s.province = ? and
s.suburb = ?";
// prepare and bind
$stmt = $conn->prepare($query);
$stmt->bind_param("sss", $place['country'], $place['province'], $place['suburb']);
} else if ($place['city'] && $place['province']) {
$query .= "s.province = ? and
s.city = ? and
s.suburb = ?";
// prepare and bind
$stmt = $conn->prepare($query);
$stmt->bind_param("sss", $place['province'], $place['city'], $place['suburb']);
} else if ($place['city'] && $place['country']) {
$query .= "s.country = ? and
s.city = ? and
s.suburb = ?";
// prepare and bind
$stmt = $conn->prepare($query);
$stmt->bind_param("sss", $place['country'], $place['city'], $place['suburb']);
} else if ($place['city']) {
$query .= "s.city = ? and
s.suburb = ?";
// prepare and bind
$stmt = $conn->prepare($query);
$stmt->bind_param("ss", $place['city'], $place['suburb']);
} else if ($place['province']) {
$query .= "s.province = ? and
s.suburb = ?";
// prepare and bind
$stmt = $conn->prepare($query);
$stmt->bind_param("ss", $place['province'], $place['suburb']);
} else if ($place['country']) {
$query .= "s.country = ? and
s.suburb = ?";
// prepare and bind
$stmt = $conn->prepare($query);
$stmt->bind_param("ss", $place['country'], $place['suburb']);
} else {
$query .= "s.suburb = ?";
// prepare and bind
$stmt = $conn->prepare($query);
$stmt->bind_param("s", $place['suburb']);
}
//////////////////////////// NO SUBURB ///////////////////////////////////////////////////
} else if ($place['city']) {
if ($place['province'] && $place['country']) {
$query .= "s.country = ? and
s.province = ? and
s.city = ?";
// prepare and bind
$stmt = $conn->prepare($query);
$stmt->bind_param("sss", $place['country'], $place['province'], $place['city']);
} else if ($place['province']) {
$query .= "s.province = ? and
s.city = ?";
// prepare and bind
$stmt = $conn->prepare($query);
$stmt->bind_param("ss", $place['province'], $place['city']);
} else if ($place['country']) {
$query .= "s.country = ? and
s.city = ?";
// prepare and bind
$stmt = $conn->prepare($query);
$stmt->bind_param("ss", $place['country'], $place['city']);
} else {
$query .= "s.city = ?";
// prepare and bind
$stmt = $conn->prepare($query);
$stmt->bind_param("s", $place['city']);
}
//////////////////////// NO SUBURB OR CITY ////////////////////////////////////////////////////////
} else if ($place['province']) {
if ($place['country']) {
$query .= "s.country = ? and
s.province = ?";
// prepare and bind
$stmt = $conn->prepare($query);
$stmt->bind_param("ss", $place['country'], $place['province']);
} else {
$query .= "s.province = ?";
// prepare and bind
$stmt = $conn->prepare($query);
$stmt->bind_param("s", $place['province']);
}
//////////////////////////////// NO SUBURB, CITY, OR PROVINCE ///////////////////////////////
} else if ($place['country']) {
$query .= "s.country = ?";
// prepare and bind
$stmt = $conn->prepare($query);
$stmt->bind_param("s", $place['country']);
}
return $stmt;
}
function queryDbForProducts($conn, $place)
{
$query = "SELECT p.*, s.*
FROM product p
INNER JOIN product_shop ps
ON ps.p_id = p.p_id
INNER JOIN shop s
ON s.s_id = ps.s_id
WHERE ";
$stmt = buildSQLWhereClause($query, $conn, $place);
$stmt->execute();
$meta = $stmt->result_metadata();
while ($field = $meta->fetch_field()) {
$parameters[] =& $row[$field->name];
}
When I use sql prepared statements by building up the sql statement and params the much better way, it doesn't work:
<?
function buildSQLWhereClause($place) {
$query = "SELECT p.*, s.* FROM product p INNER JOIN product_shop ps ON ps.p_id = p.p_id INNER JOIN shop s ON s.s_id = ps.s_id WHERE ";
$queryParams = [];
$queryParamTypes = "";
$i = 0;
$len = count($place);
foreach ($place as $key => $value) {
if ($i == $len - 1) {
$query .= "$key = ?";
$queryParams[] = $value;
$queryParamTypes .= "s";
} else {
$query .= "$key = ? AND ";
$queryParams[] = $value;
$queryParamTypes .= "s";
}
$i++;
}
return array(
"query" => $query,
"queryParams" => $queryParams,
"queryParamTypes" => $queryParamTypes
);
}
function queryDbForProducts($conn, $place)
{
$queryObject = buildSQLWhereClause($place);
$query = $queryObject['query'];
$queryParams = $queryObject['queryParams'];
$queryParamTypes = $queryObject['queryParamTypes'];
// prepare and bind
$stmt = $conn->prepare($query);
$stmt->bind_param($queryParamTypes, $queryParams);
$stmt->execute();
$meta = $stmt->result_metadata();
Hovering over the $stmt in the debugger shows:
affected_rows:-1
insert_id:0
num_rows:0
param_count:4
field_count:13
errno:2031
error:"No data supplied for parameters in prepared statement"
error_list:array(1)
sqlstate:"HY000"
id:1
No data supplied? Hovering over the $queryParams parameter in the debugger shows:
0:"Grey Lynn"
1:"Auckland"
2:"Auckland"
3:"New Zealand"
So I did provide the query parameters to the $stmt->bind_param() function. Did I provide them in the wrong format?
Hovering over $QueryParamTypes shows:
"ssss"
Hovering over $query shows:
"SELECT p.*, s.* FROM product p INNER JOIN product_shop ps ON ps.p_id = p.p_id INNER JOIN shop s ON s.s_id = ps.s_id WHERE suburb = ? AND city = ? AND province = ? AND country = ?"
How come it works when done with the code at the top of the question and it doesn't work when done with the code without all the is statements?

bind_param does not take an array as an argument, it takes varargs. You will need to use call_user_func_array if you want to call it with a dynamic number of arguments.
i.e.
$params = array_unshift($queryParams, $queryParamTypes);
call_user_func_array(array($stmt, "bind_param"), $params);

The params passed to call_user_func_array, parameter 2, need to be referenced.
This is the working solution:
function makeValuesReferenced($arr){
$refs = array();
foreach($arr as $key => $value)
$refs[$key] = &$arr[$key];
return $refs;
}
$stmt = $conn->prepare($query);
//$stmt->bind_param($queryParamTypes, $queryParams);
call_user_func_array(array($stmt, 'bind_param'), makeValuesReferenced($queryParams));
$stmt->execute();
As per this answer
Not knowing about the reference thing mucked me around for a long time. Hope this helps some people.

Related

Prepared statements in DataTables

I want to use prepared statements inside DataTables for the code stated below. It has two queries (query and query1).
Query has two "where conditions" which variables are $classid and $searchfield.
Query1 is a "Limit" statement and also has two variables $start and $end.
The results of both queries should be stored in the $data array. At the moment I don't get any results. How do I bind the paramteres correctly in this case and store the results in the array so DataTables gets a working json?
My try:
$connect = mysqli_connect("localhost", "xxxx", "xxx", "xxx");
$columns = array('itemOverview.id', 'itemOverview.Display_lang');
// Variables to bind
$classid = 4;
$searchfield = $_POST["is_searchfield"];
$start = $_POST['start'];
$end = $_POST['length'];
$query = "SELECT * FROM itemOverview INNER JOIN item ON item.id = itemOverview.id";
$query.= " WHERE item.ClassID = ? AND ";
if(!empty($searchfield)) {
$query .= "itemOverview.Display_lang = ? AND ";
}
$query1 = '';
if($_POST["length"] != -1) {
$query1 = 'LIMIT ?, ?';
}
$stmt = mysqli_stmt_init($connect);
if (!mysqli_stmt_prepare($stmt, $query. $query1)) {
echo "SQL Failed";
} else {
// bind paramaters to the placeholder
mysqli_stmt_bind_param($stmt, "ssss", $classid, $searchfield, $start, $end);
// run parameters inside database
mysqli_stmt_execute($stmt);
$result = mysqli_stmt_get_result($stmt);
$data = array();
while($row = mysqli_fetch_array($result))
{
$sub_array = array();
$sub_array[] = $row["id"];
$sub_array[] = $row["Display_lang"];
$data[] = $sub_array;
}
}
$output = array(
"draw" => intval($_POST["draw"]),
"data" => $data
);
echo json_encode($output);

PHP PDO UPDATE variable fields using IF

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");
}

Database table is not updating

Hi I'm trying to update my tbl_jadwal, it said success, but database is not updated, anybody can please help me finding where the problem is ? Thankyou
<?php
if (isset($_GET['id'])) {
$ID = $_GET['id'];
} else {
$ID = "";
}
// create array variable to store category data
$category_data = array();
$sql_query = "SELECT Category_ID, Category_name
FROM tbl_category
ORDER BY Category_ID ASC";
$stmt_category = $connect->stmt_init();
if ($stmt_category->prepare($sql_query)) {
// Execute query
$stmt_category->execute();
// store result
$stmt_category->store_result();
$stmt_category->bind_result($category_data['Category_ID'],
$category_data['Category_name']
);
}
$sql_query = "SELECT Menu_image FROM tbl_jadwal WHERE Menu_ID = ?";
$stmt = $connect->stmt_init();
if ($stmt->prepare($sql_query)) {
// Bind your variables to replace the ?s
$stmt->bind_param('s', $ID);
// Execute query
$stmt->execute();
// store result
$stmt->store_result();
$stmt->bind_result($previous_menu_image);
$stmt->fetch();
$stmt->close();
}
$stmt = $connect->stmt_init();
if ($stmt->prepare($sql_query)) {
// Execute query
$stmt->execute();
// store result
$stmt->store_result();
$stmt->fetch();
$stmt->close();
}
if (isset($_POST['btnEdit'])) {
$nama_lokasi = $_POST['nama_lokasi'];
$category_ID = $_POST['category_ID'];
$longitude = $_POST['longitude'];
$latitude = $_POST['latitude'];
$phone = $_POST['phone'];
$email = $_POST['email'];
$description = $_POST['description'];
// get image info
$menu_image = $_FILES['menu_image']['name'];
$image_error = $_FILES['menu_image']['error'];
$image_type = $_FILES['menu_image']['type'];
// create array variable to handle error
$error = array();
// updating all data
$sql_query = "UPDATE tbl_jadwal
SET Nama_Lokasi = ? , Category_ID = ?, Latitude = ?, Longitude = ?, Phone = ?, Email = ?, Menu_image = ?, Description = ?
WHERE Menu_ID = ?";
$upload_image = 'upload/images/' . $menu_image;
$stmt = $connect->stmt_init();
if ($stmt->prepare($sql_query)) {
// Bind your variables to replace the ?s
$stmt->bind_param('ssssssss',
$nama_lokasi,
$category_ID,
$longitude,
$latitude,
$phone,
$email,
$upload_image,
$description,
$ID);
// Execute query
$stmt->execute();
// store result
$update_result = $stmt->store_result();
$stmt->close();
}
} else {
updating all data except image file
$sql_query = "UPDATE tbl_jadwal
SET Nama_Lokasi = ? , Category_ID = ?,
Longitude = ?, Latitude = ?, Phone = ?, Email = ?, Description = ?
WHERE Menu_ID = ?";
$stmt = $connect->stmt_init();
if ($stmt->prepare($sql_query)) {
// Bind your variables to replace the ?s
$stmt->bind_param('sssssss',
$nama_lokasi,
$category_ID,
$longitude,
$latitude,
$phone,
$email,
$description,
$ID);
// Execute query
$stmt->execute();
// store result
$update_result = $stmt->store_result();
$stmt->close();
}
}
check update result
if ($update_result) {
$error['update_data'] = " <span class='label label-primary'>Success update</span>";
} else {
$error['update_data'] = " <span class='label label-danger'>failed update</span>";
}
This my database structure
replace bind_param() with bindParam(':data', $data);
or try $stmt->execute(array(':data' => $data))
Hi Just write the simple query firstly and add EXPLAIN before it.
For example:
EXPLAIN update table set name='test' where id=1;
This statement will show all the possible error. In this way you will be able to resolve the problem.

PHP issue... output stops after loop

ive got this code and somehow it stops after the first entry even though there should be way more.
If i take both foreach loops out, its doing fine. So the issue should be there but im either blind or stupid :(
// Main Agenda
$stmt = $mysqli->prepare("
SELECT
agenda.id,
agenda.title,
agenda.timeStart,
agenda.timeEnd,
agenda.description,
agenda.speakers,
agenda.moderators,
agenda_locations.name,
agenda_days.dayDate,
agenda_categories.name
FROM agenda
LEFT JOIN agenda_locations ON agenda.location = agenda_locations.id
LEFT JOIN agenda_days ON agenda.dayDate = agenda_days.id
LEFT JOIN agenda_categories ON agenda.category = agenda_categories.id
WHERE agenda.active = '1'
AND agenda.deleted = '0'
ORDER BY agenda.timeStart ASC
");
$stmt->execute();
$stmt->store_result();
$stmt->bind_result($agendaId, $agendaTitle, $agendaStart, $agendaEnd, $agendaDescription, $agendaSpeakers, $agendaModerators, $agendaLocation, $agendaDate, $agendaCategory);
while ($stmt->fetch()) {
$speakersText = "";
$moderatorsText = "";
if( strlen($agendaSpeakers)>0 ){
foreach(explode('###', $agendaSpeakers) as $speakerId) {
$stmt = $mysqli->prepare("
SELECT
name
FROM speakers
WHERE id = ?
");
$stmt->bind_param('i', $speakerId);
$stmt->execute();
$stmt->store_result();
$stmt->bind_result($speakerName);
$stmt->fetch();
$speakersText .= $speakerName.", ";
}
$speakersText = substr_replace($speakersText, "", -2);
}
if( strlen($agendaModerators)>0 ){
foreach(explode('###', $agendaModerators) as $moderatorId) {
$stmt = $mysqli->prepare("
SELECT
name
FROM speakers
WHERE id = ?
");
$stmt->bind_param('i', $moderatorId);
$stmt->execute();
$stmt->store_result();
$stmt->bind_result($moderatorName);
$stmt->fetch();
$moderatorsText .= $moderatorName.", ";
}
$moderatorsText = substr_replace($speakersText, "", -2);
}
$var0 = $agendaId;
$var1 = stripslashes($agendaTitle);
$var2 = strtotime($agendaStart);
$var3 = strtotime($agendaEnd);
$var4 = stripslashes($agendaDescription);
$var5 = $speakersText;
$var6 = $moderatorsText;
$var7 = $agendaLocation;
$var8 = strtotime($agendaDate);
$var9 = $agendaCategory;
$agendaMain[] = array ($var0,$var1,$var2,$var3,$var4,$var5,$var6,$var7,$var8,$var9);
}
You have to rename you $stmt variable in your foreach loops.

php sql multi bind_param

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

Categories