Is there an easy way to make an array of objects in php when you have the objects attributes come from a mySQL query?
My code is below in order to show you what I'm doing. I'm probably far off because I'm not used to php at all and I can't grasp it for some reason.
public function loadMeals(){
$stmt = $this->conn->prepare("SELECT id, dishName, ingredients FROM meals");
$stmt->execute();
$stmt->bind_result($id, $dishName, $ingredients);
$meals = array();
while ($stmt->fetch()) {
$this->id = $id;
$this->dishName = $dishName;
$this->ingredients = $ingredients;
$meals[] = $this;
}
return $meals;
}
Keep in mind that this is all in a class Meals.
Thanks guys.
Based on how you are using it (creating an array of Meal objects, the method should be declared as a static method, called without $this context. The loop may create Meal objects via new self() and set the appropriate properties before appending onto the array.
If you think about it, this method's purpose is not to act on a single Meal, so it makes sense for it to be static.
Since it is called without $this context, you will need to pass the MySQLi connection as a parameter
public static function loadMeals($conn) {
// Do the query/fetch like you already have
// except that $conn is a local param, not a class prop.
$stmt = $conn->prepare("SELECT id, dishName, ingredients FROM meals");
$stmt->execute();
$stmt->bind_result($id, $dishName, $ingredients);
$meals = array();
while ($stmt->fetch()) {
// Create a new Meal object
$meal = new self();
// And set its properties
$meal->id = $id;
$meal->dishName = $dishName;
$meal->ingredients = $ingredients;
// Append it to the array
$meals[] = $meal;
}
// Return it
return $meals;
}
To use it, outside the class:
// Call the function statically.
// You'll need to pass the database connection to it
$meals = Meal::loadMeals($conn);
I'll point out that while it is very good to be in the habit of doing prepare()/execute(), you do not actually need it here since you are not passing any input into the query. You can just call $conn->query(), eliminating some complexity from MySQLi's bind_result() variable references.
$result = $conn->query("SELECT id, dishName, ingredients FROM meals");
if ($result) {
// Fetch and instantiate a Meal object in one go.
while ($meal = $result->fetch_object('Meal')) {
$meals[] = $meal;
}
}
This might be a short solution to your problem but consider using mysql_fetch_assoc when grabbing your elements from the database.
For more reading and example:
mysql_fetch_assoc documentation from php.net
For PDO:
public function loadMeals(){
$stmt = $this->conn->prepare("SELECT id, dishName, ingredients FROM meals");
$stmt->execute();
$meals = array();
while ($obj = $stmt->fetch(PDO::FETCH_OBJ)) {
$meals[] = $obj;
}
return $meals;
}
or $meal = $stmt->fetchObject('MyMeal', $ctor_args); See this from PHP manual. Also note the first comment about constructors there, to expect the unexpected results that using this may cause.
Related
Since a few weeks I use prepared statements in PHP with the fetch_object(className:: class). I really like this way because of the autocomplete in my controllers and in my view(Twig), but some times I need an inner join (One to many).
Now I do it like below. Luckily I have only a few results in the projects where I use this so the number of queries is low. But on a large result, this will create a lot of queries. To improve performance I store the $returnResult in apcu / Redis in the controller or save it in a JSON file when I can't use the other cache method.
My question is: can I do this on a better way to reduce the number of queries but still use the fetch_object(className:: class)
Thanks in advance for the help.
public function getAll()
{
// Create database connection
$db = DB::connect();
$stmt = $db->prepare("SELECT * FROM dataroom_category ORDER BY display_order");
$stmt->execute();
$returnResult = [];
$categoryResult = $stmt->get_result();
$stmt2 = $db->prepare("SELECT * FROM dataroom_file WHERE dataroom_category_id = ? ORDER BY display_order");
$stmt2->bind_param('i', $id);
/** #var data $category */
while ($category = $categoryResult->fetch_object(data::class)) {
$id = $category->getId();
$stmt2->execute();
$filesResult = $stmt2->get_result();
while ($file = $filesResult->fetch_object(DataroomFile::class)) {
// Add the file to the setFiles array with $file->getid() as key
$category->setFiles($file);
}
// I noticed by using mysqli_free_result the server used alot less memory at the end
mysqli_free_result($filesResult);
$returnResult[$category->getId()] = $category;
}
mysqli_free_result($categoryResult);
$stmt2->close();
$stmt->close();
// Return the categories with the files for usage in the controller and view
return $returnResult;
}
Before I ask my question, I will post the code: (Please note if you can just explain the second question it'll be sufficient, I am just really confused what's happening at the foreach loop.
<?php
class dbh {
private $servername; private $username; private $password; private $
dbname;
protected function connect() {
$this->servername = "localhost";
$this->username = "root";
$this->password = "";
$this->dbname = "whatever";
$conn = new mysqli($this->servername, $this->username, $this->password,
$this->dbname);
return $conn;
}
}
<?php
class User extends Dbh {
protected function getAllUsers() {
$sql = "SELECT * FROM user";
$result = $this->connect()->query($sql);
$numRows = $result->num_rows;
if($numRows > 0) {
while($row = $result ->fetch_assoc()) {
$data[] = $row;
}
return $data;
}
}
}
<?php
class ViewUser extends User {
public function showAllUsers() {
$datas = $this->getAllUsers();
foreach ($datas as $data) {
echo $data['uid'] . "<br>";
echo $data['pwd'] . "<br>";
}
}
}
<?php
includes all those classes
?>
<body>
<?php
$users = new ViewUser();
$users->showAllUsers();
?>
</body>
I don't understand a couple things about this code.
Firstly, what is "this" in those classes? It is just a placeholder for the users object, which is the current object, right? I think it is.
Secondly, and my main question, when we call the member function showAllUsers(), we go to the ViewUser class, and then we have a $datas variable that gets assigned to $this->getAllUsers() which ends up returning a $data array, which contains all the rows in the database... Right?
Then my question is, what are the contents of $datas in the foreach loop? Is it an array of rows? $data is supposed to be key values, but $data is also an array which has me very confused.
I am visualizing it like $datas = $data, so
$datas[] = [$data[0], $data[1], $data[2], $data[3], ... $data[last_one]]
and each one of these elements contains a row ...
So foreach($datas as data) goes through each element, but to display it we need to echo data[0]?
Is my understanding correct? I know it's an associative array so those 0, 1, 2... etc are the column titles of table..
Also, what exactly does $fetch_assoc do when we have $row = fetch_assoc? Are we just storing rows in the $rows variable? The while loop is false when we reach the last row, right? I'm just so used to seeing a conditional in a while loop, like for instance while (x == 4). I have never seen a situation where we assign a variable, like for example while (x = 4) until now.
This is actually quite poor for a class handling scheme, but your questions are still valid.
You're sort of right. $this is a reference to the current instance (read: object) of the class where you define a method that uses the this keyword.
Correct in all cases. You're also right in that datas is an array of arrays (which represents the rows in the database table). Each element of $datas contains another array that represents a single row where each element is a column of that row. They're associative arrays so the index is a key, like you said.
As per the documentation, mysqli::query returns an object of type mysqli_result, so you can't access it directly as an array. mysqli::fetch_assoc will convert the mysqli_result into an associative array, row by row by moving a pointer, which is why you keep looping in the while loop until fetch_assoc returns false (that is, when no more rows are in the result object, or when the pointer reaches the last row).
Goodmorning.
I've an issue with this function inside a class:
public function singleField($query,$bindParam,$table){
$stmt = $this->conn->prepare($query);
while (list($key, $value) = each($bindParam)) {
$stmt->bindParam($key,$value);
}
$stmt->execute();
while($row = $stmt->fetch()){
return $row[$table];
}
}
cannot understand what I'm doing wrong, here how I use the class:
$conn = new conn;
$conn->singleField("SELECT field FROM table WHERE ID = :ID AND field2 = :field2....etcetc",array(":ID"=>1,":field2"=>"OtherVar...etc..."),"Table");
being sure that the connection created in construct is working, now I wonder why the query is not working and not return the result.
I wish I had the opportunity in the array can enter more than one value to pass to BindParam, this is why i created that while but I think that's wrong.
Are three days that I work on.
Thank You for any help.
I am using MySQLi and PHP to call a stored MySQL routine with prepared statements. It returns a result set with dozens of columns.
$stmt = $dbconnection->prepare("CALL SomebodysDbProcedure(?);");
$stmt->bind_param("s", $idvalue);
$stmt->execute();
$stmt->bind_result($col1, $col2, $col3, ...);
However, I am only interested in a subset of the output columns.
The documentation says bind_result() is required to handle the complete set of returned columns:
Note that all columns must be bound after mysqli_stmt_execute() and
prior to calling mysqli_stmt_fetch().
Do I need to add code also for those columns I'm uninterested in? If so the application will break if the MySQL stored routine result set is expanded in the future, or even columns rearranged. Is there a workaround for this?
I'm assuming that you just don't want to write out all those variables for the bind_result() function. You could use a function like below instead of the bind_result() function. Pass it your $stmt object and you'll get back an array of standard objects with the fields you want.
function getResult($stmt)
{
$valid_fields = array('title', 'date_created'); // enter field names you care about
if (is_a($stmt, 'MySQLi_STMT')) {
$result = array();
$metadata = $stmt->result_metadata();
$fields = $metadata->fetch_fields();
for (; ;)
{
$pointers = array();
$row = new \stdClass();
$pointers[] = $stmt;
foreach ($fields as $field)
{
if (in_array($field->name, $valid_fields)) {
$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;
}
return array();
}
The answer of Jonathan Mayhak guided me in the right direction. On PHP bind_result page, nieprzeklinaj provides a function called fetch(). It works; use it like this:
$stmt = $conn->prepare("CALL SomebodysDbProcedure(?);");
$stmt->bind_param("s", $idvalue);
$stmt->execute();
$sw = (array)(fetch($stmt));
$s = $sw[0]; // Get first row
$dateCreated = $s['date_created']; // Get field date_created
Edit: Unfortunately successive calls within the same PHP file don't seem to work with this method.
Try using fetch_fields php method:
array mysqli_fetch_fields ( mysqli_result $result )
http://php.net/manual/en/mysqli-result.fetch-fields.php
I'm using a factory(class) to present forms from a target database table - as defined at class instance. Then on submit, create a new instance of the class which then insert a new record in to the database. $_POST key names match the table column names.
My issue is dynamically assigning bind parameters when the variables are determined at class instance. I'm getting the following, whether I use Reflections method or inline.
Warning: mysqli_stmt::bind_param() [mysqli-stmt.bind-param]: Number of elements in type definition string doesn't match number of bind variables
The following method is called in the sub class after the post array has been contructed and assigned to the class property $array.
private function addrecord($array,$tbl,$_conn){
//define field name array for query statement
foreach ($array as $key=>$value){
$keyarr[]=$key;
}
//BUILD THE QUERY STATEMENT
$query = "INSERT INTO $tbl SET ";
foreach ($keyarr as $key){
$query .= ($key."=?, "); //clone and add next element
}
$query = rtrim($query,", "); //remove EOL whitespace and comma
//done
/*
//Hard code bind parameters works as expected
if (self::$_conn = new mysqli(DB_HOST,DB_UNAME,DB_UPWORD,DB_NAME)){
$stmt=self::$_conn->prepare($query);
$stmt->bind_param("sssss",$array['user_id'],$array['user_name'],$array['user_email'],$array['user_date'],$array['user_active']);
$stmt->execute();
$insertid=$stmt->insert_id;
$stmt->close();
echo "The record was created with id ".$insertid;
}
*/
//Tried re assigning post array as reference
//same error as just passing $array
//$array = $this->refValues($array);
//Binding params using Reflections, same error
self::$_conn = new mysqli(DB_HOST,DB_UNAME,DB_UPWORD,DB_NAME);
$stmt = self::$_conn->prepare($query);
$ref = new ReflectionClass('mysqli_stmt');
$method = $ref->getMethod("bind_param");
$method->invokeArgs($stmt,$array);
$stmt->execute();
$stmt->close();
self::$_conn->close();
}
//Pass By Reference required for PHP 5.3+, dev server 5.3.17
function refValues($arr){
if (strnatcmp(phpversion(),'5.3') >= 0){
$refar = array();
foreach($arr as $key => $value)
$refar[$key] = &$arr[$key];
return $refar;
}
return $arr;
}
Thanks in advance and much appreciated.
As you can see, mysqli is practically unusable with prepared statements.
So, I'd suggest you to either use PDO or, better, some intelligent library that can make safe query without prepared statments.
With such a library your function will be written in one line
private function addrecord($array,$tbl){
$this->conn->query("INSERT INTO ?n SET ?u", $tbl, $array);
}
please note that if $array is coming from the untrusted source, you have to filter it's content out first.
Per Common Sense, changed process to PDO. Works as expected. Should have done it sooner. Only issue remaining is UI feedback. Would like to return an insert id, however MySQL doesnt return a last insert id for PDO. And again, the class doesnt know the tables structure in advance so hard coding in not an option. Need a workaround. Any thoughts? Heres the new insert process using PDO.
try{
self::$_conn = new PDO('mysql:host='.DB_HOST.';dbname='.DB_NAME.'', DB_UNAME, DB_UPWORD);
$stmt = self::$_conn->prepare($query);
$arcount=count($array); //set number of bindParam loops
foreach($array as $key=>$value){
$stmtarr[]=$value; //convert assoc to numerated array
}
//re index array so increment will match up with placeholder position
$stmtarr = array_combine(range(1, count($stmtarr)), array_values($stmtarr));
for($i=1;$i<=$arcount;$i++){ //bind variable, one for each placeholder,
$stmt->bindParam($i,$stmtarr[$i]);
}
$stmt->execute();
} catch (PDOException $e){
print "Error: ".$e->getMessage()."<br>";
die();
}
Assume everything else is the same as above.