I am trying to display records using PDO LIKE query, but I am getting this error message can I know how to solve this.
This is my code:
$rs = new JSONRecordSet();
$searchbooksSQL = "SELECT Title FROM l_stock WHERE Title LIKE ?";
$params = array("%$term%");
echo $rs->getRecordSet($searchbooksSQL, $params);
This is the getRecordSet code:
class R_RecordSet {
function getRecordSet($sql, $params = null) {
if (is_array($params)) {
$this->stmt = $this->db->prepare($sql);
// execute the statement passing in the named placeholder and the value it'll have
$this->stmt->execute($params);
} else {
$this->stmt = $this->db->query($sql);
}
return $this->stmt;
}
}
class JSONRecordSet extends R_RecordSet {
function getRecordSet($sql, $elementName = "ResultSet", $params = null) {
$stmt = parent::getRecordSet($sql, $params);
$recordSet = $stmt->fetchAll(PDO::FETCH_ASSOC);
$nRecords = count($recordSet);
if ($nRecords == 0) {
$status = 'error';
$message = json_encode(array("text" => "No records found"));
$result = '[]';
} else {
$status = 'ok';
$message = json_encode(array("text" => ""));
$result = json_encode($recordSet);
}
return "{\"status\": \"$status\", \"message\":$message, \"$elementName\" :{\"RowCount\": $nRecords ,\"Result\": $result}}";
}
}
The error message i am getting is "Notice: Array to string conversion"
getRecordSet() is defined as:
function getRecordSet($sql, $elementName = "ResultSet", $params = null) {
however, you are calling it as:
echo $rs->getRecordSet($searchbooksSQL, $params);
You will need to modify your code to pass in an appropriate $elementName parameter. (The default is probably reasonable.)
echo $rs->getRecordSet($searchbooksSQL, 'ResultSet', $params);
Additionally, you should probably use json_encode() to generate the final result from JSONRecordSet::getRecordSet(), rather than building it up with string concatenation. It will make the code easier to read and understand.
Also, your two implementations of getRecordSet() are incompatible with each other, according to the Liskov Substitution Principle due to the change in the semantics of the input parameters, and is likely what led you to the parameter mismatch at your call site in the first place. You probably want to re-order JSONRecordSet::getRecordSet() parameters to:
function getRecordSet($sql, $params = null, $elementName = 'ResultSet') {
Related
We have a bunch of websites using a shared MySQLi class.
PHP 7.4 was working with these and no errors were seen to be generated.
Now, PHP Bug #80837 has been fixed with release of PHP 7.4.18
The problem is that this fix means all our websites are throwing this fatal error and I can't see how to fix the error itself.
The Exception:
[06-May-2021 08:04:19 UTC] PHP Fatal error: Uncaught mysqli_sql_exception: Commands out of sync; you can't run this command
now in /usr/local/lib/php/databaseClass.php:169 Stack trace:
#0 /usr/local/lib/php/databaseClass.php(169): mysqli_stmt->store_result()
#1 /usr/local/lib/php/databaseClass.php(897): Database->fetchResult(Object(mysqli_stmt), true)
#2 /home/account/public_html/.../file.php(45): DatabaseObject->getSelect('SELECT (bad_log...', Array, true)
#3 /home/katemawdsley/public_html/start.php(10): include('/home/account...')
#4 {main} thrown in databaseClass.php on line 169
The Database Class 'fetchResult' method:
/***
* For use with MySQLi->dbiLink Object.
* Returns the result as an array of KEY=>VALUE pairs from the Database.
* #param $result mysqli_result object.
* #return array|mixed
***/
private function fetchResult($result)
{
if ($result instanceof \mysqli_stmt) {
$result->store_result(); // This is line 169
$variables = [];
$data = [];
$meta = $result->result_metadata();
//error_log(print_r(debug_backtrace(),true));
while ($field = $meta->fetch_field()) {
$variables[] = &$data[$field->name]; // pass by reference
}
\call_user_func_array([$result, 'bind_result'], $variables);
$i = 0;
while ($result->fetch()) {
$resultsArray[$i] = [];
foreach ($data as $k => $v) {
$resultsArray[$i][$k] = $v;
if ($this->numericIndexes) {
$resultsArray[$i][] = $v;
}
}
unset($k, $v);
$i++;
}
} elseif ($result instanceof \mysqli_result) {
$rowNumber = 0;
while ($row = $result->fetch_assoc()) {
$resultsArray[$rowNumber] = $row;
if ($this->numericIndexes) {
foreach ($row as $numerical) {
$resultsArray[$rowNumber][] = $numerical;
}
unset($numerical);
}
$rowNumber++;
}
$i = 0;
unset($row, $rowNumber);
}
return $resultsArray;
}
The Database Class 'fetchResult' method called from a 'select' method:
/***
* Function for retrieving SQL query data from the Database
* #param $sql string the SQL select query
* #param null $data string|array the data to check in the query with
* #return array|bool|mixed
***/
public function select($sql, $data = null){
/***
* Now prepare the SQL
***/
try {
$query = $this->databaseObject->prepare($sql);
/****
* $query is prepared here....
***/
...
$query->execute();
}
catch (\mysqli_sql_exception $ex) {
$this->exceptionalError($ex);
return false;
}
catch (\Exception $ex) {
$this->exceptionalError($ex);
return false;
}
if (\mb_strtoupper($reduce, "UTF-8") !== "NORETURN") {
if ($query->field_count > 0 ) {
$query->store_result();
}
$this->rowsFound[] = $query->num_rows;
$output = $this->fetchResult($query); // Call to fetch result method
if ($query->field_count > 0) {
$query->free_result();
}
$query->close();
unset($query);
return $output;
}
/***
* NORETURN value so no result to return.
***/
$successValue = $this->rowsAffected[] = $query->affected_rows;
$query->close();
unset($query);
return $successValue;
}
What is the correct work around for how to construct this fetchResult method in light of the update?
(As you can tell we were completely unaware of this previous issue and it didn't appear on any error logs so was a surprise)
You are calling store_result() twice. You can't do that. Once the results have been buffered in PHP, the connection line is free - there are no more results pending to be fetched. Calling store_result() the second time will throw "Out of sync" error.
You call it first here:
if (\mb_strtoupper($reduce, "UTF-8") !== "NORETURN") {
if ($query->field_count > 0 ) {
$query->store_result();
}
and then here
private function fetchResult($result)
{
if ($result instanceof \mysqli_stmt) {
$result->store_result(); // This is line 169
Remove one usage and you should be fine.
I would recommend working with mysqli_result instead. When you call get_result it will buffer the results and give you a familiar object that you can work with. This could simplify your abstraction class significantly.
Here is how I would refactor this class:
public function select($sql, $data = null)
{
/***
* Now prepare the SQL
***/
try {
$query = $this->databaseObject->prepare($sql);
/****
* $query is prepared here....
***/
$query->execute();
} catch (\mysqli_sql_exception $ex) {
$this->exceptionalError($ex);
return false;
} catch (\Exception $ex) {
$this->exceptionalError($ex);
return false;
}
$result = $query->get_result();
if (\mb_strtoupper($reduce, "UTF-8") !== "NORETURN") {
$this->rowsFound[] = $result->num_rows;
if ($result) {
return $this->fetchResult($result); // Call to fetch result method
}
}
/***
* NORETURN value so no result to return.
***/
$successValue = $this->rowsAffected[] = $query->affected_rows;
return $successValue;
}
private function fetchResult(mysqli_result $result)
{
return $result->fetch_all($this->numericIndexes ? MYSQLI_BOTH : MYSQLI_ASSOC);
}
I have a function in PHP which calls another class function in order to assign a value to one of it's variables $related.
The original function requiring the value uses the requestACLForAccess() function to add a specific variable filter to the $headers variable:
The listRecords function:
public function listRecords($api)
{
header("Access-Control-Allow-Origin: *");
if ($this->authenticationProcedure() !== false) {
$headers = apache_request_headers();
$access = $this->requestACLForAccess($headers['module'], $headers['oauth_token'], $_SERVER['REQUEST_METHOD']);
if(gettype($access) == "array"){
$headers['filter'][0][$access[1]]['$equals'] = $access[2];
}
$listObject = new FilterApi();;
return $listObject->filterList($api, $headers);
}
}
The function requestACLForAccess being called :
protected function requestACLForAccess(
$targetModule,
$token,
$req_type,
$getSpecialFlag = false,
$targetId = null
) {
$row = $this->checkTokenValid("token", $token);
if (!empty($row) || $row !== false) {
$related = \Sugarcrm\Sugarcrm\custom\clients\base\CustomACL::checkRelated($row['module'], $targetModule, $row['id'], $req_type, $getSpecialFlag, $targetId);
die(gettype($related)); // For debugging
return $related;
}
else {
return false;
}
}
The function that was called is in another file called customACL.php. Within that function, a certain conditional statement returns an array $arr which should be assigned to the particular variable above. The function being called:
public static function checkRelated($module, $targetModule, $id, $req_type, $getSpecial, $targetId)
{
$bean = \BeanFactory::retrieveBean($module, $id);
if($bean->designation == self::ADMIN_NAME && !(in_array($targetModule, self::RECORD_RELATION_MODULES))){
return self::ACL_ADMIN;
} else{
//first check if a relational table between modules exists
$query = "select table_name from information_schema.tables where table_name like '%{$module}%{$targetModule}%' OR table_name like '%{$targetModule}%{$module}%'";
$result = $GLOBALS['db']->query($query);
$row = $result->fetch_assoc();
if (empty($row)) { //if no table exists
return self::ACL_ADMIN;
} else { //find out table name
$table_name = $row['table_name'];
if ($req_type == 'PUT' || $req_type == 'DELETE' || $req_type == 'GET') { //check if record has relation
$acl_access = self::checkRecordRelated($table_name, $id, $module, $req_type, $targetId, $targetModule);
}
}
}
}
For my specific call, this function then goes to a checkRecordRelated() function within the same file:
protected static function checkRecordRelated($table_name, $id, $module, $req_type, $targetId, $targetModule)
{
//find out module column name in relational table
$query = "select column_name from information_schema.columns where table_name = '$table_name' and column_name like '%{$module}_id%' or column_name like '%{$module}s_id%'";
$result = $GLOBALS['db']->query($query);
$row = $result->fetch_assoc();
if (empty($row)) {//if no column name
return self::ACL_NON_ADMIN;
} else {//return module column name
$column_name = $row['column_name'];
}
if ($req_type == 'GET') {//if special GET request
//find if related records exist in relational table for listing
$findRelatedRecords = "select * from $table_name where $column_name = '$id' and 'deleted' = 0";
$result = $GLOBALS['db']->query($findRelatedRecords);
$row = $result->fetch_array();
if (empty($row)) {//if NON_ADMIN exist
return self::ACL_NON_ADMIN;
} else {//if exist
$arr = array(self::ACL_ADMIN, $table_name, $id);
return $arr;
}
}
}
}
Up until the point where I have defined the variable $arr, checking with gettype() returns an array and printing the variable gives me (as expected) a complete array with the values I've added. However, as soon as the value is returned to the original function and assigned to the variable $related, the array somehow becomes NULL and the gettype() also returns NULL.
Update: I've added the missing functions to make it clearer, I hope it makes sense
Could anyone tell me what naive mistake am I doing here? Why is this happening?
Well this is embarrassing, I forgot to return the $aclaccess variable in the checkrelated() function which is why it was showing null. Adding the return value was the solution.
I am taking data from many tables. I want to display many objects in different places. I got the data from data base, but I want to put the data into an array for useful purpose, but it's not working.
This my controller code:
public function compare_by_business_sectors() {
//print_r($this->input->post());exit;
if ($this->input->post())
{
$solution_array = array();
//print_r (json_encode($business_sectors)); exit;
$business_sectors=$this->home_model->compare_business_sectors_data($this->input->post());
$tab_child_id = "";
$id="";
foreach ($business_sectors as $key=>$sectors) {
$solution_array[1]=$sectors->solution_name;
$solution_array[2]=$sectors->description;
$solution_array[3]=$sectors->vendor_name;
$solution_array[4]=$sectors->video_presentation;
$solution_array[5]=$sectors->start_free_trail;
$solution_array[6]=$sectors->hardware_package;
$solution_array[7]=$sectors->pos_market_rating;
//$solution_array[$sectors->field_id] = $sectors->value;
$id = "solution".$sectors->tab_child_id;
if ($tab_child_id != $sectors->tab_child_id) {
$id = array();
$id[$sectors->field_id] = $sectors->title;
}
else if ($tab_child_id == $sectors->tab_child_id) {
$id[$sectors->field_id] = $sectors->title;
}
}
//$solution_array[$id]= $id;
}
print_r($id);
//$this->load->view('compare_by_business_sectors.php');
}
This is my model code:
public function compare_business_sectors_data($sectorid) {
$query = $this->db->select('solutions.*,solution_tabs_child_fields.field_id,solution_tabs_child_fields.tab_child_id,solution_tabs_child_fields.title')
->from('solutions')
//->join('solutions', 'business_sector.sector_id = solutions.business_sector_id',"left")
->join('solution_features','solutions.entry_id = solution_features.entry_id',"left")
->join('solution_tabs_child_fields','solution_features.field_id = solution_tabs_child_fields.field_id')
->where('solutions.business_sector_id', $sectorid['id'])
->get();
return $query->result();
//print_r($query->result());exit;
}
change it as follow and try.
$id_string = "";
$id_array = array();
foreach ($business_sectors as $key=>$sectors) {
$solution_array[1]=$sectors->solution_name;
$solution_array[2]=$sectors->description;
$solution_array[3]=$sectors->vendor_name;
$solution_array[4]=$sectors->video_presentation;
$solution_array[5]=$sectors->start_free_trail;
$solution_array[6]=$sectors->hardware_package;
$solution_array[7]=$sectors->pos_market_rating;
//$solution_array[$sectors->field_id] = $sectors->value;
$id_string = "solution".$sectors->tab_child_id;
if ($tab_child_id != $sectors->tab_child_id) {
$id[$sectors->field_id] = $sectors->title;
}
else if ($tab_child_id == $sectors->tab_child_id) {
$id[$sectors->field_id] = $sectors->title;
}
}
print_r($id_string);
print_r($id_array);
First you are assigning string value to $id then, $id will convert to array only if first if() statement execute other wise it will not be a string. So to overcome from this keep $id_array before for loop and you can capture string in another variable.
Here is the failure message I get in Terminal running 'phpunit tests':
1) StylistTest::test_find
null does not match expected type "object".
/Users/evanbutler/Desktop/hairSalonApp/tests/StylistTest.php:163
Here's my test method:
function test_find()
{
//Arrange
$name = "Stylist Jane";
$id = 1;
$name2 = "Stylist Bob";
$id2 = 2;
$test_stylist = new Stylist($name, $id);
$test_stylist->save();
$test_stylist2 = new Stylist($name2, $id2);
$test_stylist2->save();
//Act
$result = Stylist::find($test_stylist->getId());
//Assert
$this->assertEquals($test_stylist, $result);
}
And here's my method:
static function find($search_id)
{
$found_stylist = null;
$stylists = Stylist::getAll();
foreach($stylists as $stylist) {
$stylist_id = $stylist->getId();
if ($stylist_id == $search_id) {
$found_styist = $stylist;
}
}
return $found_stylist;
}
Here's my getAll method:
static function getAll()
{
$returned_stylists = $GLOBALS['DB']->query("SELECT * FROM stylists;");
$stylists = array();
foreach($returned_stylists as $stylist) {
$name = $stylist['name'];
$id = $stylist['id'];
$new_stylist = new Stylist($name, $id);
array_push($stylists, $new_stylist);
}
return $stylists;
}
If you'd like to see all my files here's the link to the git repository:
https://github.com/evanb2/hairSalonApp.git
I've been staring at this for way too long and I'm totally stumped.
Change
$found_styist = $stylist;
to
$found_stylist = $stylist;
You need a better IDE man. Simple static analysis could tell you about the unused variable.
I have a foreach loop that iterates through an array.
In each instance, I organise the array into a query string and use MySQLi to add it to the database.
function storeProperties($data, $db) {
foreach ($data['property'] as $property) {
$query_string = "INSERT INTO table VALUES(..., ..., ...,)"
$db->query($query_string);
echo $db->error;
}
}
Is there a better way I should be doing this?
Obviously, this method uses n database queries one after another so this is memory intensive and time intensive.
Is there a better way to do this?
Should I be concatenating each query into a single string and running it all outside the for loop?
The following method is from my PDO workhorse, used for bulk insertions. It creates a single INSERT statement with multiple VALUES entries.
Use it as
$db->bulkinsert(
'tbl_my_tablename',
array('fieldname_1','fieldname_2', 'fieldname_3'),
array(
/* rec1 */ array('r1f1', 'r1f2', 'r1f3'),
/* rec2 */ array('r2f1', 'r2f2', 'r2f3'),
/* rec3 */ array('r3f1', 'r3f2', 'r3f3')
));
Please note that the method is an snip from a complex class definition, some methods used here are not contained in the code snippet, especially $this->connect() (connects to PDO),$this->begin() (starts transaction), $this->commit()and $this->rollback(), and the static Log class for Logging similar to Apache Commons ;-)
But I'm sure this is what you might need.
/**
* Performs fast bulk insertion
* Parameters:
* $tablename
* $datafields - non-assiciative array of fieldnames
* or propertynames if $data is an array of objects
* $data - array of either non-associative arrays (in the correct order)
* or array of objects with property names matching the $datafields array
*/
const MAX_BULK_DATA = 3000;
public function bulkinsert($tablename, $datafields, &$data) {
$result = 0;
try {
try {
$this->connect();
$datacount = count($data);
// loop until all data has been processed
$start = 0;
$lastbinds = 0;
$this->begin();
while ($start < $datacount) {
$ins = array();
$bindscount = min(self::MAX_BULK_DATA, $datacount - $start);
if ($bindscount != $lastbinds) {
// prepare the binds
$binds = substr(str_repeat(',?', count($datafields)), 1);
$binds = substr(str_repeat(",($binds)", $bindscount), 1);
$lastbinds = $bindscount;
}
for ($go = $start, $last = $start + $bindscount; $go < $last; $go++) {
if (is_object($data[$go])) {
try {
foreach($datafields as $propname) {
$rfl = new ReflectionProperty($data[$go], $propname);
$rfl->setAccessible(true);
$ins[] = $rfl->getValue($data[$go]);
}
}
catch(ReflectionException $e) {
throw new InvalidArgumentException('PDOCONNECT_ERR_SQL_UNKNOWN_PROPERTY', 0, $e);
}
}
else {
foreach($data[$go] as $value) {
$ins[] = $value;
}
}
}
$sql = sprintf('INSERT INTO %s (%s) VALUES %s', $tablename, join(',',$datafields), $binds);
Log::trace($sql);
$stmt = $this->pdo->prepare($sql);
$stmt->execute($ins);
$start = $last;
$result += $bindscount;
}
$this->commit();
}
catch(PDOException $e) {
// do something with the exception if necessary
throw $e;
}
}
catch(Exception $e) {
$this->rollback();
throw $e;
}
return $result;
}
}