How to get results from prepared statment without using getresult? - php

You may think this question is not up to the mark. But i have to post it here... :(
$stmt->get_results
is not working in my hosting server as the mysqlind is not available there.
i have completed my projects without knowing this and its killing me now...
To overcome this i have created the below function and its working fine. As you can see this function is returning array. Now i am trying to create the exact object which $stmt->get_results return. ie i need to use fetch_assoc() with the object. Other wise i have to edit a large number of files that will kill my time :(.. if somebody can help me pls...
private function getMyResult($stmt)
{
$result = array();
$metadata = $stmt->result_metadata();
$data['result']=$metadata; echo "<br/>-------------<br/>1";
/*while ($myrow = $data['result']->fetch_assoc())
{
print_r($myrow);
}*/
$fields = $metadata->fetch_fields();
print_r($metadata);
for (;;)
{
$pointers = array();
$row = new stdClass();
$pointers[] = $stmt;
foreach ($fields as $field)
{
$fieldname = $field->name;
$pointers[] = &$row->$fieldname;
}
call_user_func_array('mysqli_stmt_bind_result', $pointers);
if (!$stmt->fetch())
break;
$result[] = $row;
}
$metadata->free();
return $result;
}

Related

MySQL PDO resultSet as object list performance

I'm using the following code to get records from DB:
$stmt->setFetchMode(PDO::FETCH_CLASS, MyClass::class);
$resultSet = $stmt->fetchAll();
return $resultSet;
This works fine.
However sometimes the PHP class property names and the database field names are different. In this case we make a mapping as follows:
$stmt->setFetchMode(PDO::FETCH_ASSOC);
$resultSet = $stmt->fetchAll();
foreach ($resultSet as $record) {
$resultList[] = $this->convertDbArrayToObject($record, MyClass::class, $mapping);
}
return $resultList;
where
public function convertDbArrayToObject($record, $class, $mapping = null)
{
$object = new $class();
foreach ($record as $key => $value) {
if (array_key_exists($key, $mapping)) {
$fieldName = $mapping[$key];
$setterName = 'set' . ucfirst($fieldName);
$object->$setterName($value);
}
}
return $object;
}
The problem is that the second solution is about ~10-20 times slower that is way too much.
Any idea how could we improve the performance?

Pulling NHL Standings from XML Table with PHP

I'm working on a project in which I pull various statistics about the NHL and inserting them into an SQL table. Presently, I'm working on the scraping phase, and have found an XML parser that I've implemented, but I cannot for the life of me figure out how to pull information from it. The table can be found here -> http://www.tsn.ca/datafiles/XML/NHL/standings.xml.
The parser supposedly generates a multi-dimmensional array, and I'm simply trying to pull all the stats from the "info-teams" section, but I have no idea how to pull that information from the array. How would I go about pulling the number of wins Montreal has? (Solely as an example for the rest of the stats)
This is what the page currently looks like -> http://mattegener.me/school/standings.php
here's the code:
<?php
$strYourXML = "http://www.tsn.ca/datafiles/XML/NHL/standings.xml";
$fh = fopen($strYourXML, 'r');
$dummy = fgets($fh);
$contents = '';
while ($line = fgets($fh)) $contents.=$line;
fclose($fh);
$objXML = new xml2Array();
$arrOutput = $objXML->parse($contents);
print_r($arrOutput[0]); //This print outs the array.
class xml2Array {
var $arrOutput = array();
var $resParser;
var $strXmlData;
function parse($strInputXML) {
$this->resParser = xml_parser_create ();
xml_set_object($this->resParser,$this);
xml_set_element_handler($this->resParser, "tagOpen", "tagClosed");
xml_set_character_data_handler($this->resParser, "tagData");
$this->strXmlData = xml_parse($this->resParser,$strInputXML );
if(!$this->strXmlData) {
die(sprintf("XML error: %s at line %d",
xml_error_string(xml_get_error_code($this->resParser)),
xml_get_current_line_number($this->resParser)));
}
xml_parser_free($this->resParser);
return $this->arrOutput;
}
function tagOpen($parser, $name, $attrs) {
$tag=array("name"=>$name,"attrs"=>$attrs);
array_push($this->arrOutput,$tag);
}
function tagData($parser, $tagData) {
if(trim($tagData)) {
if(isset($this->arrOutput[count($this->arrOutput)-1]['tagData'])) {
$this->arrOutput[count($this->arrOutput)-1]['tagData'] .= $tagData;
}
else {
$this->arrOutput[count($this->arrOutput)-1]['tagData'] = $tagData;
}
}
}
function tagClosed($parser, $name) {
$this->arrOutput[count($this->arrOutput)-2]['children'][] = $this->arrOutput[count($this- >arrOutput)-1];
array_pop($this->arrOutput);
}
}
?>
add this search function to your class and play with this code
$objXML = new xml2Array();
$arrOutput = $objXML->parse($contents);
// first param is always 0
// second is 'children' unless you need info like last updated date
// third is which statistics category you want for example
// 6 => the array you want that has wins and losses
print_r($arrOutput[0]['children'][6]);
//using the search function if key NAME is Montreal in the whole array
//result will be montreals array
$search_result = $objXML->search($arrOutput, 'NAME', 'Montreal');
//first param is always 0
//second is key name
echo $search_result[0]['WINS'];
function search($array, $key, $value)
{
$results = array();
if (is_array($array))
{
if (isset($array[$key]) && $array[$key] == $value)
$results[] = $array;
foreach ($array as $subarray)
$results = array_merge($results, $this->search($subarray, $key, $value));
}
return $results;
}
Beware
this search function is case sensitive it needs modifications like match to
a percentage the key or value changing capital M in montreal to lowercase will be empty
Here is the code I sent you working in action. Pulling the data from the same link you are using also
http://sjsharktank.com/standings.php
I have actually used the same exact XML file for my own school project. I used DOM Document. The foreach loop would get the value of each attribute of team-standing and store the values. The code will clear the contents of the table standings and then re-insert the data. I guess you could do an update statement, but this assumes you never did any data entry into the table.
try {
$db = new PDO('sqlite:../../SharksDB/SharksDB');
$db->setAttribute(PDO::ATTR_ERRMODE,PDO::ERRMODE_EXCEPTION);
} catch (Exception $e) {
echo "Error: Could not connect to database. Please try again later.";
exit;
}
$query = "DELETE FROM standings";
$result = $db->query($query);
$xmlDoc = new DOMDocument();
$xmlDoc->load('http://www.tsn.ca/datafiles/XML/NHL/standings.xml');
$searchNode = $xmlDoc->getElementsByTagName( "team-standing" );
foreach ($searchNode as $searchNode) {
$teamID = $searchNode->getAttribute('id');
$name = $searchNode->getAttribute('name');
$wins = $searchNode->getAttribute('wins');
$losses = $searchNode->getAttribute('losses');
$ot = $searchNode->getAttribute('overtime');
$points = $searchNode->getAttribute('points');
$goalsFor = $searchNode->getAttribute('goalsFor');
$goalsAgainst = $searchNode->getAttribute('goalsAgainst');
$confID = $searchNode->getAttribute('conf-id');
$divID = $searchNode->getAttribute('division-id');
$query = "INSERT INTO standings ('teamid','confid','divid','name','wins','losses','otl','pts','gf','ga')
VALUES ('$teamID','$confID','$divID','$name','$wins','$losses','$ot','$points','$goalsFor','$goalsAgainst')";
$result= $db->query($query);
}

Array within an array using Facebook Graph APi

I have been working on this problem for the last 2 days, searched over and over again .. nothing. Understanding that I am not an expert here - it's good! lol
I am trying to get the information found in the link below;
https://graph.facebook.com/570215713050551_4508656/comments/?fields=likes.fields(id,username,name,profile_type)
to then export into a csv.
Now I have current numerous other api tools, but this one has stumped me.
Basically, need to get the data foreach then run that again plus do the "next" paging etc.
Totally lost here.
My current code is here.
<?php
//Export and Download the Liker Data from each comment here ..
$id = $_GET['data'];
$commentor = $_GET['commentor'];
$toget = 'https://graph.facebook.com/'.$id.'/comments/?fields=likes.fields(id,username,name,profile_type)';
$data = #file_get_contents($toget);
$data = json_decode($data,true);
if($data['data'] == FALSE){
echo "gay!";
die;
}
$alldata = array();
function moredata($data){
global $alldata;
foreach ($data["data"] as $eachdata){
$onedata['id'] = $eachdata['id'];
foreach ($eachdata["likes"] as $ex){
$onedata['uid'] = $$ex['data'][0]['id'];
$onedata['name'] = $ex['data'][0]['name'];
$onedata['username'] = $ex['data'][0]['username'];
$onedata['profile_type'] = $ex['data'][0]['profile_type'];
//$onedata['link'] = $eachdata['link'];
}
$alldata[] = $onedata;
$onedata = array();
}
if (array_key_exists('next', $data['paging'])) {
$nextpagelink = $data['paging']['next'];
$nextdata = json_decode(file_get_contents($nextpagelink),true);
moredata($nextdata);
}
}
moredata($data);
... ETC ETC to get out the csv
Any help here would be amazing! Thanks guys.
It was little tricky but can be solved the issue with nested recursion.
I have tried your code and made few changes and it worked. Check the code below
$alldata = array();
$arrlikedata = array();
function moredata($data){
global $alldata;
global $arrlikedata;
foreach ($data["data"] as $eachdata)
{
$onedata['id'] = $eachdata['id'];
if(isset($eachdata["likes"])){
$onedata['likes'] = more_like_data($eachdata["likes"]);
}
else{
$onedata['likes'] = array();
}
$alldata[] = $onedata;
$arrlikedata = array();
}
if (array_key_exists('next', $data['paging'])) {
$nextpagelink = $data['paging']['next'];
$nextdata = json_decode(file_get_contents($nextpagelink),true);
moredata($nextdata);
}
}
function more_like_data($likedata)
{ global $alldata;
global $arrlikedata;
if(isset($likedata["data"])){
foreach ($likedata["data"] as $ex){
if(isset($ex)){
$onedata1['uid'] = $ex['id'];
$onedata1['name'] = $ex['name'];
$onedata1['username'] = (isset($ex['username']))?$ex['username']:'';
$onedata1['profile_type'] = $ex['profile_type'];
$arrlikedata[] = $onedata1;
$onedata1 = array();
}
}
}
if(isset($likedata['paging'])){
if (array_key_exists('next', $likedata['paging']))
{
$nextpagelink = $likedata['paging']['next'];
$nextlikedata = json_decode(file_get_contents($nextpagelink),true);
return more_like_data($nextlikedata);
}
else{
return $arrlikedata;
}
}
else{
return $arrlikedata;
}
}
moredata($data);
print "<pre>";
print_r($alldata);
print "</pre>";

php function not returning all results from a MySQL query in a foreach

Hey guys I have a little issue with a function that retrieves data from a MySQL Database and then I iterate over the results with a foreach loop, checking a value to see if it is null and if it is, replacing it with another value.
The problem with this function is this, that after returning the data I'm only able to view one record retrieved from the database. Probably something simple but it's beyond me.
I would like to do this before passing it to the controller or view. Maybe this isn't possible with the foreach loop? What am I missing?
Here is an example of my code.
public function get_basic_user_data(){
$sql = 'SELECT Account.First_Name, Account.Last_Name, Account.User_Name, Profile_Photos.Thumb_Url
FROM Account
LEFT JOIN Profile_Photos ON Account.idAccount = Profile_Photos.Account_Id
AND Profile_Photos.Active = 1
WHERE Account.idAccount != ?';
$account_id = $this->get_account_id();
$data = $this->db->query($sql, $account_id);
foreach($data->result() as $row){
if($row->Thumb_Url == NULL){
$image = base_url().'assets/images/no_photo_thumb.png';
}else{
$image = $row->Thumb_Url;
}
$new_data = new stdClass;
$new_data->First_Name = $row->First_Name;
$new_data->Last_Name = $row->Last_Name;
$new_data->User_Name = $row->User_Name;
$new_data->Thumb_Url = $image;
}
return $new_data;
}
Hopefully someone can help me with this? Thanks!
At the moment you are just returning the last data row. Change your code like this to return an array of all your rows from that function:
$rows = array()
foreach($data->result() as $row){
if($row->Thumb_Url == NULL){
$image = base_url().'assets/images/no_photo_thumb.png';
}else{
$image = $row->Thumb_Url;
}
$new_data = new stdClass;
$new_data->First_Name = $row->First_Name;
$new_data->Last_Name = $row->Last_Name;
$new_data->User_Name = $row->User_Name;
$new_data->Thumb_Url = $image;
$rows[] = $new_data;
}
return $rows;
This way every row returned from the database will be added to an array named $rows. At the end you have to return your new array.
You are overwriting $new_data each iteration. Try this
$new_data = new stdClass
...
$all_data[] = $new_data;
Instead of checking for null value in the code, you could just use a IFNULL statement in the SQL query, this does separate the logic a bit but it might just be worth it in this case.
The function returns only the last row in the result because the new_data variable is overwritten in every step of your loop. Declare new_data an array at the start of your function and add rows as array elements
...
$new_data[] = new stdClass;
...
Each iteration of the foreach overwrites $new_data so in the end when the function returns, only the last fetched row will be returned. To return more than one row you could store all the rows in an array and then return the array in the end. It would look something like this:
public function get_basic_user_data(){
$sql = 'SELECT Account.First_Name, Account.Last_Name, Account.User_Name, Profile_Photos.Thumb_Url
FROM Account
LEFT JOIN Profile_Photos ON Account.idAccount = Profile_Photos.Account_Id
AND Profile_Photos.Active = 1
WHERE Account.idAccount != ?';
$account_id = $this->get_account_id();
$data = $this->db->query($sql, $account_id);
$data = array();
foreach($data->result() as $row){
if($row->Thumb_Url == NULL){
$image = base_url().'assets/images/no_photo_thumb.png';
}else{
$image = $row->Thumb_Url;
}
$new_data = new stdClass;
$new_data->First_Name = $row->First_Name;
$new_data->Last_Name = $row->Last_Name;
$new_data->User_Name = $row->User_Name;
$new_data->Thumb_Url = $image;
$data[] = $new_data;
}
return $data;
}
To be able to use this function you have to change the code that uses it to loop through the array of objects.

Variable amount of columns returned in mysqli prepared statement

I have a situation where a dynamic query is being generated that could select anywhere from 1 to over 300 different columns across multiple tables. It currently works fine just doing a query, however the issue I'm running into in using a prepared statement is that I do not know how to handle the fact that I don't know how many columns I will be asking for each time and therefor don't know how to process the results.
The reason I believe a bind statement will help is because once this query is run once, it will most likely (though not always) be run again with the exact same parameters.
Currently I have something like this:
$rows = array();
$this->statement = $this->db->prepare($query);
$this->statement->bind_param('i',$id);
$this->statement->execute();
$this->statement->bind_result($result);
while($this->statement->fetch())
{
$rows[] = $result;
}
I know this doesn't work as I want it to, my question is how do I get the data back out of the query. Is it possible to bring the columns back in an associative array by column name, like a standard mysqli query?
I prever not to use eval, this is my solution (similar to ashurexm):
$md = $stmt -> result_metadata();
$fields = $md -> fetch_fields();
$result = new stdClass(); // Actual result object
$params = array(); // Array of fields passed to the bind_result method
foreach($fields as $field) {
$result -> {$field -> name} = null;
$params[] = &$result -> {$field -> name};
}
call_user_func_array(array($stmt, 'bind_result'), $params);
Using VolkerK's suggestion of mysqli_statement->result_metadata() I was able to fashion together the following code that accomplishes what I'm looking for, though the performance isn't any faster than using a standard query. I get the statement->result_metadata() to build an associative array to call bind_result on. I build up a bind_result statement as a string and eval it. I know this isn't particularly safe but it is my first pass.
public function executePreparedStatement()
{
if($this->statement->execute())
{
$this->record = array();
$md = $this->statement->result_metadata();
$fields = $md->fetch_fields();
$bindResult = '$this->statement->bind_result(';
foreach($fields as $field)
{
$bindResult .= "\$this->record['" . $field->name . "'],";
}
$bindResult = substr($bindResult,0,strlen($bindResult) - 1) . ');';
eval($bindResult);
return true;
}
else
{
$this->error = $this->db->error;
return false;
}
}
...
$this->prepareStatement($query);
$this->bindParameter('i',$runId);
if($this->executePreparedStatement())
{
$report = new Report();
while($this->statement->fetch())
{
$row = $this->record;
$line = array();
foreach($row as $key => &$value)
{
array_push($line,$value);
}
$report->addLine($line);
}
return $report
}
This is the very reason why mysqli should never be used with prepared statements.
So, you have to use PDO instead, which will make your executePreparedStatement() into three lines:
function executePreparedStatement($sql,$params) {
$stm = $this->db->prepare($sql);
$stm->execute($params);
return $stm->fetchAll();
}
used like this
$sql = "SELECT * from news WHERE category=?";
$data = $this->executePreparedStatement($sql,$_GET['category']);

Categories