Can't use database connection while inside loop - php

I am having a problem with mysqli. I am trying to search a database for all people who meet a category. While looping through the results, I want to create an instance of a "Person" class, passing the database connection to the class. This is where the problem starts. Here is my code.
$con = new mysqli($db_host,$db_user,$db_password,$db_name);
if (mysqli_connect_errno())
{
die(mysqli_connect_error()); //There was an error. Print it out and die
}
$sql = "SELECT id FROM users";
$stmt = $con->prepare( $sql );
if ($stmt)
{
$stmt->execute();
$stmt->bind_result($id);
while($stmt->fetch())
{
$person = new Person( $con );
}
$stmt->close();
}
If i move the $person = new Person( $con ); to just after the while loop, it successfully makes an object of the last person. It just won't work when inside the loop. What is the reason for this?

According to error shown, you can't use the same connection until previous result set is in use. In order to make it work, you can do something like this:
$personIDs = array();
while($stmt->fetch())
{
$personIDs[] = $id;
}
$stmt->close();
and than just go through all ids buffered into array:
foreach($personIDs as $id) {
$person = new Person( $con );
}
Or, you can use store_results
if ($stmt)
{
$stmt->execute();
$stmt->bind_result($id);
$stmt->store_results();
while($stmt->fetch())
{
$person = new Person( $con );
}
$stmt->free_result();
$stmt->close();
}

Related

PhP $stmt->fetch() while loop keeps replacing existing data

Running $stmt->fetch() in a while() loop replaces all previously collected data in an object with the last record, even if you push it to an array in the loop.
I have a simple database called dbemployee
I've written a small script to get all the employees above a specific age.
Heres my code:
<?php
$conn = mysqli_connect("localhost", "root", "", "test");
if ($conn->connect_error) {
die("Connection failed: " . $conn->connect_error);
}
$output = new stdClass();
$employee = new stdClass();
// $ageCheck = $_POST['ageCheck'];
$ageCheck = 18;
$sql = "SELECT ID, vemail, vname, vage FROM dbemployee WHERE vage > ?";
$stmt = $conn->stmt_init();
if ($stmt->prepare($sql)) {
$stmt->bind_param("s",$ageCheck);
$stmt->execute();
$stmt->store_result();
$stmt->bind_result($ID, $vemail, $vname, $vage);
if ($stmt->num_rows > 0) {
while ($stmt->fetch()) {
$employee->ID = $ID;
$employee->vemail = $vemail;
$employee->vname = $vname;
$employee->vage = $vage;
$output->data[] = $employee;
}
} else {
$output->data = "";
}
$output->response = "SUCCESS";
}
header('Content-Type: application/json');
print_r($output);
?>
Surprisingly all I'm getting is 5 entries of the last result as if the last result is replacing previous results.
You need to create an instance inside the loop, otherwise it will point to the same memory address:
while ($stmt->fetch()) {
$employee = new stdClass;
$employee->ID = $ID;
...
You can check this in the docs (https://www.php.net/manual/en/language.oop5.basic.php):
When assigning an already created instance of a class to a new variable, the new variable will access the same instance as the object that was assigned. This behaviour is the same when passing instances to a function. A copy of an already created object can be made by cloning it.

How to organize SQL select functions?

I want to get information by user id, so lets add this to the model:
public function getById ($id)
{
$sql = 'SELECT * FROM users';
return ActualDbHander::run($sql);
}
later, I want to get only some fields:
public function getById ($id, $fields = '*')
{
$sql = 'SELECT '.$fields.' FROM users';
return ActualDbHander::run($sql);
}
another idea, lets add ordering:
public function getById ($id, $fields = '*', $orderBy = '')
{
$sql = 'SELECT '.$fields.' FROM users';
if ($orderBy != '')
{
$sql.= ' ORDER BY '.$orderBy;
}
return ActualDbHander::run($sql);
}
and I see this becaming messy and messy. What if I want to add JOIN-s? What if I want to add detailed WHERE-s? This is when "too generalic" methods born.
I completely agree with mch and Mjh comments, but, only in the case you actually want to have a "BD driver" (and build it yourself) I'd use different names for each query, very specific names, because you need to know exactly what a function will return to you.
So if I were you I would use names like getAllUsers, getUserById, getAllUsersOnlyPersonalData, getUserByIdOnlyPersonalData, getAllUsersOnlyContactData and so on (with fixed fields and filters for each method).
Note that in your examples you are not using at all the $id variable, so you are always receiving a list of users.
Regarding the method to make the queries, there are lots of ways to do it. Personally, I prefer MySQLi Object-Oriented prepared statements, because it's safe, easy and currently very extended, so I will use it just to ilustrate the examples.
Your functions would be something like this:
<?php
class DBDriver{
function openConnection(){
// If you don't always use same credentials, pass them by params
$servername = "localhost";
$username = "username";
$password = "password";
$database = "database";
// Create connection
$conn = new mysqli($servername, $username, $password, $database);
// Check connection
if ($conn->connect_error) {
die("Connection failed: " . $conn->connect_error);
}
// Return conection object
return $conn;
}
function closeConnection($conn){
$conn->close();
}
function getAllUsers (){ // We don't need ids here
$conn = $this->openConnection();
// Array of arrays to store the results
// You can use any other method you want to return them
$resultsArray = [];
$sqlQuery = "SELECT * FROM users";
// In this case it's not neccesary to use prepared statements because we aren't binding any param but we'll use it to unify the method
if ($stmt = $conn->prepare($sqlQuery)) {
// Execute query
$stmt->execute();
// Bind result variables (I don't know your actuall column names)
$stmt->bind_result($id, $name, $email, $phone, $birthdate);
// Fetch values
while ($stmt->fetch()) {
$resultsArray[] = [$id, $name, $email, $phone, $birthdate];
}
// Close statement
$stmt->close();
}
$this->closeConnection($conn);
// If no results, it returns an empty array
return $resultsArray;
}
function getUserByIdOnlyContactData ($userId){
$conn = $this->openConnection();
// Array to store the results (only one row in this case)
$resultsArray = [];
$sqlQuery = "SELECT name, email, phone FROM users WHERE id = ?";
if ($stmt = $conn->prepare($sqlQuery)) {
// Bind parameter $userId to "?" marker in $sqlQuery
$stmt->bind_param("i", $userId);
$stmt->execute();
$stmt->bind_result($name, $email, $phone);
// If id found
if ($stmt->fetch()) {
$resultsArray = [$name, $email, $phone];
}
// Close statement
$stmt->close();
}
$this->closeConnection($conn);
return $resultsArray;
}
function getAllUserOnlyBirthdayDataOrderByBirthday (){
$conn = $this->openConnection();
$resultsArray = [];
$sqlQuery = "SELECT id, name, birthdate FROM users ORDER BY birthdate";
if ($stmt = $conn->prepare($sqlQuery)) {
$stmt->execute();
$stmt->bind_result($id, $name, $birthdate);
while ($stmt->fetch()) {
$resultsArray[] = [$id, $name, $birthdate];
}
// Close statement
$stmt->close();
}
$this->closeConnection($conn);
return $resultsArray;
}
} // Class end
This way it's true you will have lots of functions depending on your requirements but as you can see it's extremely easy to add new ones or modify them (and you won't get mad with many different options in the same function).
Hope this helps you to organize your database driver!

MySQL query in PHP function

I'm attempting to use functions to get certain data from database. For example, I want to get info from an user with ID 1.
try {
$connection = new PDO("mysql:host=localhost;dbname=database", "root", "password");
}
catch (PDOException $e) {
die("Error: " . $e->getMessage());
}
$connection->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
function getUser($id) {
global $connection;
$query = $connection->prepare("SELECT * FROM accounts WHERE ID = '$id'");
$query->execute();
while($row = $query->fetch()) {
echo $row['playername'];
$user[] = $row;
}
}
And then in my index.php.
include 'inc/db.php';
getUser("1");
foreach($user AS $user) {
echo $user['ID'];
}
The first echo works, I get the username displayed, but the foreach doesn't echo anything. I tried to var_dump($user); but ended up getting NULL.
You need to have:
function getUser(...) {
...
$user = array();
while(...) {
$user[] = $row;
}
return $user;
}
And then in your main code:
$users = getUser(1);
foreach($users as $user) { .... }
Right now you're defining local variables and then not returning them, so they're lost when the method exits. And then not capturing any possible returned values anyways, making your code basically pointless.
Your problem is that you are writing too much code. PHP can't process so much, chokes and dies.
All you actually need is
$pdo = new PDO("mysql:host=localhost;dbname=database", "root", "password");
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$pdo->setAttribute(ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC);
function getUser($id)
{
global $pdo;
$query = $pdo->prepare("SELECT * FROM accounts WHERE ID = ?");
$query->execute(array($id));
return $query->fetch();
}
$user = getUser(1);
echo $user['playername'];
to make it little more serious, you should use prepared statement to pass variable into query and return data from the function.

Run a call from a function PHP

i'm building an website using php and html, im used to receiving data from a database, aka Dynamic Website, i've build an CMS for my own use.
Im trying to "simplify" the receiving process using php and functions.
My Functions.php looks like this:
function get_db($row){
$dsn = "mysql:host=".$GLOBALS["db_host"].";dbname=".$GLOBALS["db_name"];
$dsn = $GLOBALS["dsn"];
try {
$pdo = new PDO($dsn, $GLOBALS["db_user"], $GLOBALS["db_pasw"]);
$stmt = $pdo->prepare("SELECT * FROM lp_sessions");
$stmt->execute();
$row = $stmt->fetchAll();
foreach ($row as $row) {
echo $row['session_id'] . ", ";
}
}
catch(PDOException $e) {
die("Could not connect to the database\n");
}
}
Where i will get the rows content like this: $row['row'];
I'm trying to call it like this:
the snippet below is from the index.php
echo get_db($row['session_id']); // Line 22
just to show whats in all the rows.
When i run that code snippet i get the error:
Notice: Undefined variable: row in C:\wamp\www\Wordpress ish\index.php
on line 22
I'm also using PDO just so you would know :)
Any help is much appreciated!
Regards
Stian
EDIT: Updated functions.php
function get_db(){
$dsn = "mysql:host=".$GLOBALS["db_host"].";dbname=".$GLOBALS["db_name"];
$dsn = $GLOBALS["dsn"];
try {
$pdo = new PDO($dsn, $GLOBALS["db_user"], $GLOBALS["db_pasw"]);
$stmt = $pdo->prepare("SELECT * FROM lp_sessions");
$stmt->execute();
$rows = $stmt->fetchAll();
foreach ($rows as $row) {
echo $row['session_id'] . ", ";
}
}
catch(PDOException $e) {
die("Could not connect to the database\n");
}
}
Instead of echoing the values from the DB, the function should return them as a string.
function get_db(){
$dsn = "mysql:host=".$GLOBALS["db_host"].";dbname=".$GLOBALS["db_name"];
$dsn = $GLOBALS["dsn"];
$result = '';
try {
$pdo = new PDO($dsn, $GLOBALS["db_user"], $GLOBALS["db_pasw"]);
$stmt = $pdo->prepare("SELECT * FROM lp_sessions");
$stmt->execute();
$rows = $stmt->fetchAll();
foreach ($rows as $row) {
$result .= $row['session_id'] . ", ";
}
}
catch(PDOException $e) {
die("Could not connect to the database\n");
}
return $result;
}
Then call it as:
echo get_db();
Another option would be for the function to return the session IDs as an array:
function get_db(){
$dsn = "mysql:host=".$GLOBALS["db_host"].";dbname=".$GLOBALS["db_name"];
$dsn = $GLOBALS["dsn"];
$result = array();
try {
$pdo = new PDO($dsn, $GLOBALS["db_user"], $GLOBALS["db_pasw"]);
$stmt = $pdo->prepare("SELECT * FROM lp_sessions");
$stmt->execute();
$rows = $stmt->fetchAll();
foreach ($rows as $row) {
$result[] = $row['session_id'];
}
}
catch(PDOException $e) {
die("Could not connect to the database\n");
}
return $result;
}
Then you would use it as:
$sessions = get_db(); // $sessions is an array
and the caller can then make use of the values in the array, perhaps using them as the key in some other calls instead of just printing them.
As antoox said, but a complete changeset; change row to rows in two places:
$rows = $stmt->fetchAll();
foreach ($rows as $row) {
echo $row['session_id'] . ", ";
}
Putting this at the start of the script after <?php line will output interesting warnings:
error_reporting(E_ALL|E_NOTICE);
To output only one row, suppose the database table has a field named id and you want to fetch row with id=1234:
$stmt = $pdo->prepare("SELECT * FROM lp_sessions WHERE id=?");
$stmt->bindValue(1, "1234", PDO::PARAM_STR);
I chose PDO::PARAM_STR because it will work with both strings and integers.

retrieve data from db using function pdo

here is the code before implementing the function,
try {
$conn = new PDO('mysql:host=localhost;dbname=dbname', 'usr', 'pass');
$conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$stmt = $conn->prepare('SELECT * FROM posts WHERE status= :published ORDER BY id DESC LIMIT 5');
$stmt->execute(array(':published' => 'published'));
while ($result = $stmt->fetch(PDO::FETCH_ASSOC)) {
$contents = $result['content'];
$title = $result['title'];
....
this works fine. Then i moved db connecting commands to separate php file(functions.php). and created this function,
function run_db($sqlcom,$exe){
$conn = new PDO('mysql:host=localhost;dbname=dbname', 'usr', 'pass');
$conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$stmt = $conn->prepare($sqlcom);
$stmt->execute($exe);
$data_db = $stmt->fetch(PDO::FETCH_ASSOC) ;
return $data_db;
}
Then i changed first mentioned code like this,
try {
$data_db=run_db('SELECT * FROM posts WHERE status= :published ORDER BY id DESC LIMIT 5',array(':published' => 'published'));
while ($result = $data_db) {
$contents = $result['content'];
$title = $result['title'];
Then all i got was one post repeating infinitely. Can anyone tell me how to correct this?
Change the function to this:
function run_db($sqlcom,$exe){
$conn = new PDO('mysql:host=localhost;dbname=dbname', 'usr', 'pass');
$conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$stmt = $conn->prepare($sqlcom);
$stmt->execute($exe);
return $stmt;
}
and the call to that function to:
try {
$stmt = run_db('SELECT * FROM posts WHERE status= :published ORDER BY id DESC LIMIT 5',array(':published' => 'published'));
while ($result = $stmt->fetch(PDO::FETCH_ASSOC)) {
$contents = $result['content'];
$title = $result['title'];
EDIT: Better solution is the one that jeroen advices - to return all the fetched objects at once:
function run_db($sqlcom,$exe){
$conn = new PDO('mysql:host=localhost;dbname=dbname', 'usr', 'pass');
$conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$stmt = $conn->prepare($sqlcom);
$stmt->execute($exe);
return $stmt->fetchAll(PDO::FETCH_ASSOC);
}
Then calling this way:
try {
$data = run_db('SELECT * FROM posts WHERE status= :published ORDER BY id DESC LIMIT 5',array(':published' => 'published'));
foreach($data as $result) {
$contents = $result['content'];
$title = $result['title'];
EDIT 2: Anyway - wrapping such a logic into one function is not very good idea. now You are limited with executing of only SELECT queries and the resulting array containing always only record's associative array. What if You would like to (for any reason) retrieve the array of objects, or even only one single value? What if You would like to execute INSERT, UPDATE, DELETE queries???
If You for sure want to go this way, then I'd suppose creating a class with functions like this:
class MyPDO {
private $connection;
static $instance;
function __construct() {
$this->connection = new PDO('mysql:host=localhost;dbname=dbname', 'usr', 'pass');
$this->connection->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
}
static function getInstance() {
return self::$instance ? : self::$instance = new MyPDO;
}
// retrieves array of associative arrays
function getAssoc($sqlcom, $exe) {
$stmt = $this->connection->prepare($sqlcom);
$stmt->execute($exe);
return $stmt->fetchAll(PDO::FETCH_ASSOC);
}
// retrieves array of objects
function getObj($sqlcom, $exe) {
$stmt = $conn->prepare($sqlcom);
$stmt->execute($exe);
return $stmt->fetchAll(PDO::FETCH_OBJ);
}
// retireves one single value, like for SELECT 1 FROM table WHERE column = true
function getOne($sqlcom, $exe) {
$stmt = $conn->prepare($sqlcom);
$stmt->execute($exe);
return $stmt->fetchColumn();
}
// just executes the query, for INSERT, UPDATE, DELETE, CREATE ...
function exec($sqlcom, $exe){
$stmt = $conn->prepare($sqlcom);
return $stmt->execute($exe);
}
}
Then You can call it this way:
try {
$pdo = MyPDO::getInstance();
foreach($pdo->getAssoc('MySQL QUERY'), array($param, $param)) as $result) {
print_r($result);
}
} catch(\Exception $e) {
// ...
}
Just return the statement:
function run_db($sqlcom,$exe){
static $conn;
if ($conn == NULL)
{
$conn = new PDO('mysql:host=localhost;dbname=dbname', 'usr', 'pass');
$conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
}
$stmt = $conn->prepare($sqlcom);
$stmt->execute($exe);
return $stmt;
}
try {
$stmt=run_db('SELECT * FROM posts WHERE status= :published ORDER BY id DESC LIMIT 5',array(':published' => 'published'));
while ($result = $stmt->fetch(PDO::FETCH_ASSOC)) {
$contents = $result['content'];
$title = $result['title'];
And you could also set the default fecth mode on the connection.
An alternative to the correct answers that return $stmt from the function, would be to fetch all rows in the function and use a foreach in your main code:
In function:
...
$data_db = $stmt->fetchAll(PDO::FETCH_ASSOC) ;
return $data_db;
Outside of function:
$data_db=run_db('SELECT * FROM posts WHERE status= :published ORDER BY id DESC LIMIT 5',array(':published' => 'published'));
foreach ($data_db as $result) {
$contents = $result['content'];
$title = $result['title'];
...
There are two essential problems with your code.
It connects every time it runs a query.
It returns data in only one format, while PDO can return results in dozens different formats.
Accepted answer makes it wrong, as it is just trying to reinvent PDO features, but very dirty way, with a lot of duplicated code and still failing to make it as good as with vanilla PDO.
As it said in xdazz's answer, you have to return the statement. And then use PDO's native fetch mode to get the result in desired format, using method chaining.
Also, you shouldn't add try to your code.

Categories