PHP POCO style database access - php

With PHP I know how to access databases using mysql_query(), PDO->query() and pdo->prepare() followed by PDO->bindParam() and PDO->execute().
However in the past I have come across a method of database access where you have a class object which contains variable names which mirror the fields in a database and you change the field values to interact with the database.
I have only seen this done in C# however. The C# object is known as a POCO object and looks like a class with only variables in it.
Is it possible to do something similar in PHP PDO.

The simplest solution is to use PDO with PDO::FETCH_OBJ or PDO::FETCH_CLASS.
See documentation:
http://www.php.net/manual/en/pdostatement.fetch.php
http://www.php.net/manual/en/pdostatement.setfetchmode.php
Example:
$stmt = $pdo->prepare(...);
$stmt->execute();
while ($obj = $stmt->fetch(PDO::FETCH_OBJ)) {
// $obj is a new object of class stdClass
echo $obj->columnname
}
Example:
$stmt = $pdo->prepare(...);
$stmt->execute();
$stmt->setFetchMode(PDO::FETCH_CLASS, 'MyClass');
while ($obj = $stmt->fetch()) {
// $obj is a new object of class MyClass
echo $obj->columnname
}

Related

Non-static method mysqli_result::fetch_array() cannot be called statically

Since moving to PHP 8 the code below now gives me the error 'Fatal error: Uncaught Error: Non-static method mysqli_result::fetch_array() cannot be called statically...'
if (!isset($isone)) {
$seestuno=isset($_POST['seestuno']);
}
if (isset($seestuno)) {
include ('/home/username/private/dblon.php'); //database connection ($dbcnx below)
$sql="SELECT stuno,fname,lname FROM Studetails WHERE stuno=$seestuno";
$result=#mysqli_query($dbcnx, $sql);
$row=#mysqli_result::fetch_array($result); //I changed from mysql_fetch_array. I believe this is more correct?
The bottom line gives the error. I thought this was basic stuff (since I did it) so can anyone help me out? Many thanks.
mysqli_result::fetch_array ( int $mode = MYSQLI_BOTH ) is the method signature for the method "fetch_array" of the mysqli_result class. Therefore, you'd have to call it as an object method:
$row = $result->fetch_array();
Or, of course, you use the equivalent to mysql_fetch_array instead:
$row = mysqli_fetch_array($result);
There are examples at the bottom of the official docs for that. The rest of your code uses mysqli's procedural interface, so I'd stick to the later case unless all mysqli calls are converted to object oriented style.
:: is a scope resolution operator and is used to access static and constant members of a class.
fetch_array() is not a static method. You can't access it statically. It is an instance method that can be called on every object.
To call a method on an object you have to use object operator ->.
When you fix your code, it should look something like this:
if (!isset($isone)) {
$seestuno = isset($_POST['seestuno']);
}
if (isset($seestuno)) {
include '/home/username/private/dblon.php'; //database connection ($dbcnx below)
$stmt = $dbcnx->prepare("SELECT stuno,fname,lname FROM Studetails WHERE stuno=?");
$stmt->bind_param('s', $seestuno);
$stmt->execute();
$result = $stmt->get_result();
$row = $result->fetch_array($result);
}

PHP OOP Programming

That's something I was curious. In php mysqli I can do action function over the unknown class name, for example:
$res = $mysqli->query($query);
$res->fetch_array();
That is the name of the fetch_array known, but the res variable. I would like to know how it is implemented. Sorry for the sloppy google translation.
If I understand your question correctly, then the answer is that method mysqli::query returns object that is instance of class mysqli_result. This class has method fetch_array().
Also, you may be interested in function get_class.
Before this line:
$res = $mysqli->query($query);
There was for sure mysqli connection:
$mysqli = new mysqli("localhost", "user", "password", "database");
From this, mysqli is a class which is ready for you that provided by PHP, consider its like this:
class mysqli{
}
The, $res is copy of $mysqli->query($query); which is:
here query is a property of mysqli class (a function), we do this to copy the result of mysqli query which is resource for (success) and false for failure.
You can also combine like this:
$row = $mysqli->query($query)->fetch_array();
print_r($row);
My guess is
class MySQLi{
/* other code */
public function query($query){
// some code
return new MySQLiResult(); // Don't know if that's the real class name
}
}
And the result should look like this
class MySQLiResult{
public function fetch_array(){
return array();
}
}
Note, that I've only written imaginary code. I don't really know, what the class names are.
It's only a rough estimate of how it should look.

Pass form field names to bind parameter at class instance

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.

procedural php/mysql to object oriented issue

This is the original code I am trying to write in object oriented form..
$posResult = mysql_query("SELECT MAX(position)+1 FROM todo");
if(mysql_num_rows($posResult))
list($position) = mysql_fetch_array($posResult);
This is where I am at.... I think I need to bind_result? also, I have an issue with the mysql_fetch_array...
if($stmt = $this->HH->Database->prepare("SELECT MAX(position)+1 FROM todo")) {
$stmt->execute();
$stmt->store_result();
if($stmt->num_rows != FALSE) {
list($position) = mysql_fetch_array($posResult);
}
}
Can someone advise on how to correct this?
It looks like you are moving over to PDO, which is great, it is a much better way to connect, but you are still trying to use some of the older mysql_* commands which won't work. While there are a number of ways to get the data, but this is the approach that I most commonly use:
if($stmt = $this->HH->Database->prepare("SELECT MAX(position)+1 FROM todo")) {
$stmt->execute();
$stmt->setFetchMode(PDO::FETCH_INTO, new yourClassName);
foreach($stmt as $yourClassObject)
{
$someVar=$yourClassObject->QueryColumnName;
// or
$this->someProperty=$yourClassObject->QueryColumnName;
}
}
I do generally have a class that matches the results of the query, so I use FETCH_INTO but you can use a different method such as return an indexed array, associated array or others.

Set a PHP object global?

I just started switching my project form the mysql to PDO. In my project a new PDO Object is created more or less right a the beginning of the programm.
$dbh_pdo = new PDO("mysql:host=$db_url;dbname=$db_database_name", $db_user, $db_password);
Now I would like to use this handler (is that the correct name?) in some functions and classes. Is there a way to make objects global just like variables or am I trying something unspeakably stupid, because I couldn't find anything when searching the web ...
Yes, you can make objects global just like any other variable:
$pdo = new PDO('something');
function foo() {
global $pdo;
$pdo->prepare('...');
}
You may also want to check out the Singleton pattern, which basically is a global, OO-style.
That being said, I'd recommend you not to use globals. They can be a pain when debugging and testing, because it's hard to tell who modified/used/accessed it because everything can. Their usage is generally considered a bad practice. Consider reviewing your design a little bit.
I don't know how your application looks like, but say you were doing this:
class TableCreator {
public function createFromId($id) {
global $pdo;
$stmt = $pdo->prepare('SELECT * FROM mytable WHERE id = ?');
$stmt->execute(array($id));
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
foreach ($rows as $row) {
// do stuff
}
}
}
You should do that instead:
class TableCreator {
protected $pdo;
public function __construct(PDO $pdo) {
$this->pdo = $pdo;
}
public function createFromId($id) {
$stmt = $this->pdo->prepare('SELECT * FROM mytable WHERE id = ?');
$stmt->execute(array($id));
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
foreach ($rows as $row) {
// do stuff
}
}
}
Since the TableCreator class here requires a PDO object to work properly, it makes perfect sense to pass one to it when creating an instance.
You'll use $GLOBALS['dbh_pdo'] instead of $dbh_pdo inside any functions. Or you can use the global keyword, and use $dbh_pdo (i.e. global $dbh_pdo).
You could also try using a Singleton to pass back a PDO object to you. That way you only ever have one PDO object (and one database connection) in any request which saves on memory/server resources.

Categories