I am trying to return a new instance of my User class via a DAO, and am struggling to see if it is working, it certainly isn't working as I expected, as I am not defining User properties, yet I am still seeing all the User fields and values from the database.
class Database
class Database
{
private $dbh;
public function __construct()
{
$this->openConnection();
}
private function openConnection()
{
try {
$this->dbh = new PDO('mysql:host=localhost; dbname=stats', 'user', 'pass');
$this->dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
} catch (PDOException $e) {
'There was an error connecting to the database, error: ' . $e->getMessage();
}
}
public function query($sql, $params)
{
$stmt = $this->dbh->prepare($sql, $params);
$stmt->execute($params);
return $stmt;
}
}
class UserDao
class UserDao
{
private $db;
public function __construct($db)
{
$this->db = $db;
}
public function findById($ward_code)
{
$record = $this->db->query("SELECT * from ward_statistics where WardCode = :WardCode", array(':WardCode' => $ward_code));
$record->setFetchMode(PDO::FETCH_CLASS, 'User')
$user = $record->fetch();
return $user;
}
}
class User
class User
{
// with no properties defined, I still get a
// full result set from UserDAO::findById()
}
Usage
$db = new Database();
$dao = new UserDao($db);
$user = $dao->findById('AMED');
var_dump($user);
Results in an object with a full result set from the DB (a single row that is - matched by the WardCode), with all fields populated with the correct values (from the DB).
Whilst this seems OK - I thought that PDO::FETCH_CLASS required the properties to available within the class.
My worry is, from a question I posted on Code Review a few days ago, I was also told that class methods such as the findById one should create new class instances, or update the existing one, yet all I seem to be doing is retrieving a row from the database.
Thanks
Replace fetchAll with fetch if you need just 1 object. According to documentation:
PDOStatement::fetchAll — Returns an array containing all of the result
set rows
Related
This question already has an answer here:
How to use PDO connection in other classes?
(1 answer)
Closed 3 months ago.
I want to use in my application PDO connection within multiple classes (objects) but the way how I tried in the past was not correct because everytime I called an object I created a new PDO object (connection) also.
Im right now using a spl_autoload_register for autoloading.
Here Is my DataBase class (I deleted from this example the connection propeties (name, host etc etc.):
DataBase.php
namespace MyApp;
use PDO;
class DataBase {
public function DB_CONN() {
try {
$pdo = new \PDO("mysql:host=" . $this->host . ";dbname=" . $this->DB_name, $this->DB_username, $this->DB_passw, array(PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES utf8"));
// Set the PDO error mode to exception
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
return $pdo;
}
catch (PDOException $e) {
die("ERROR: Could not connect. " . $e->getMessage());
return false;
}
}
}
So in this example here I need a PDO connection to Customer class and I created a PDO object in the Customer constructor like this:
Customer.php
namespace MyApp;
use MyApp\DataBase;
use PDO;
class Customer
{
private $PDO;
public function __construct($cus_id)
{
$this->PDO = (new DataBase)->DB_CONN();
}
public function getCustomerOrders(int $id): array
{
$CustomerOrders = (new Orders())->getOrdersByID($id)
}
public function getCustomerNameByID(int $id): ?string
{
$stmt = $this->PDO->prepare("SELECT name FROM customers WHERE id=:id");
$stmt->execute([":id" => $id]);
$result = $stmt->fetch(PDO::FETCH_ASSOC);
return $result["name"];
}
}
But its not the best method because because if I need an another class in this example named Orders (which also needs a DataBase connection) then I will create an another PDO object.
Orders.php
namespace MyApp;
use MyApp\DataBase;
use PDO;
class Orders
{
private $PDO;
public function __construct($cus_id)
{
$this->PDO = (new DataBase)->DB_CONN();
}
public function getOrdersByID(int $id): array
{
$stmt = $this->PDO->prepare("SELECT * FROM orders WHERE id =:id");
$stmt->execute([":id" => $id]);
return $stmt->fetchAll(PDO::FETCH_ASSOC);
}
}
So my question is that should I only pass the $PDO variable to the given class constructor? It will solve my problem? Like this:
try {
$pdo = new \PDO("mysql:host=" . $db_host . ";dbname=" . $db_name, $db_username, $db_passw, array(PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES utf8"));
// Set the PDO error mode to exception
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
} catch (PDOException $e) {
die("ERROR: Could not connect. " . $e->getMessage());
return false;
}
class Customer
{
private $PDO;
public function __construct(\PDO$PDO)
{
$this->PDO = $PDO;
}
public function getCustomerOrders(int $id)
{
$CustomerOrders = (new Orders($this->PDO))->getOrdersByID($id);
}
public function getCustomerNameByID(int $id): ?string
{
$stmt = $this->PDO->prepare("SELECT name FROM customers WHERE id=:id");
$stmt->execute([":id" => $id]);
$result = $stmt->fetch(PDO::FETCH_ASSOC);
return $result["name"];
}
}
class Orders
{
private $PDO;
public function __construct(\PDO$PDO)
{
$this->PDO = $PDO;
}
public function getOrdersByID(): array
{
$stmt = $this->PDO->prepare("SELECT * FROM ORDERS WHERE ID =:id");
$stmt->execute([":id" => $id]);
return $stmt->fetchAll(PDO::FETCH_ASSOC);
}
}
$Customer = new Customer($pdo);
print_r($Customer->getCustomerNameByID());
print_r($Customer->getCustomerOrders());
Or should I use dependency injection? Thank you!
Should I only pass the $PDO variable to the given class constructor ... or should I use dependency injection?
That is dependency injection. 🙂
It's tempting to think of Dependency Injection as a complex technical concept, which needs an entire framework to implement, but in reality it's just a way of designing code and can be summed up very simply:
"dependencies" (things the class needs) should be "injected" (passed in from outside)
Having a constructor which expects a database connection follows this principle perfectly.
The reason frameworks often provide specific functionality under the umbrella of "Dependency Injection" is that as your code gets more complex, you end up with a web of objects which need to be passed into each other's constructors, so you have to create them in the right order, and keep track of which ones are already constructed. A "Dependency Injection container" is just a way of managing all these objects: you give it a list of classes and their dependencies (or in some cases just point it at a set of source files) and then ask it for a particular object, and it works out what other objects need to be created first. It can feel like magic, but underneath all it's doing is creating some objects and passing them to each other's constructors.
I am new to OOP so this is probably a simple solution. I am trying to clean up my code a little bit so that I don't have to do so much when I create an object. My code is below:
include "Database.php";
Class Database {
protected $conn;
public function setDb($conn){
$this->conn = $conn;
}
}
Class Images extends Database{
protected $conn;
protected $stmt;
function __construct(){
}
public function RetrieveImages(){
$this->stmt = $this->conn->prepare('SELECT * FROM `pictures`');
$this->stmt->execute();
$boom = $this->stmt->fetchAll();
return $boom;
}
}
$db = new Images();
$db->setDb($conn);
$test = $db->RetrieveImages();
var_dump($test);
Database.php:
try{
$conn = new PDO('mysql:host=localhost;dbname=testing', 'blah','boom!');
$conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
} catch(PDOException $e){
echo'ERROR: ' . $e->getMessage();
}
This code is working fine but I would like to automate the $db->setDb($conn); part. Is there a way I can call this automatically so I can initiate the object like
$db = new Images();
$db->RetrieveImages();
I have tried adding
$db = new Database();
$db->setDb($conn);
to the constructor method inside the Images class but I get an error
Call to a member function prepare() on non object
I tried several different ways to get this working but I am not able to do so.
Is there a way I can call this automatically so I can initiate the object
That's not a very desirable property of any code; it prevents testability of your code; ignoring the unfortunate naming of your classes I would set it up like this:
class Database
{
protected $conn;
public function __construct($conn)
{
$this->conn = $conn;
}
}
Then your Images class becomes:
class Images extends Database
{
public function RetrieveImages()
{
$stmt = $this->conn->prepare('SELECT * FROM `pictures`');
$stmt->execute();
return $stmt->fetchAll();
}
}
To call the whole thing:
// $conn was created in "Database.php"
$images = new Images($conn);
print_r($images->RetrieveImages());
Your Database class has no database anythings.
It does not do anything.
You need to have a MySQLi or PDO object somewhere to do databsse calls.
I'm trying to re-code a homepage I made. This time I want to use OOP style, but I always get following error:
Statistic::checkExistingCounter() [statistic.checkexistingcounter]: Couldn't fetch MySQL
What am I doing wrong? I know that the prepare statement is senseless, but even just a query instead of prepare statement is not working at all.
Same error:
Couldn't fetch MySQL
My Database class:
class MySQL extends MySQLi {
private static $_instance = null;
private $host, $username, $password, $db;
public static function getInstance() {
if (!(self::$_instance instanceof self)) {
self::$_instance = new self();
}
return self::$_instance;
}
public function __construct(){
$this->host = '...';
$this->username = '...';
$this->password = '...';
$this->database = '...';
$this->connect();
}
public function __destruct() {
$this->db->close();
}
private function __clone(){}
public function connect() {
$this->db = #new MySQLi($this->host, $this->username, $this->password, $this->database);
/* change character set to utf8 */
$this->db->set_charset("utf8");
if (mysqli_connect_errno()) {
printf("Connect failed: %s\n", mysqli_connect_error());
exit();
}
return $this->db;
}
}
My statistic class:
class Statistic {
private $remote, $user_agent, $referer;
private $db;
/**
* Create Instance of MySQL
**/
function __construct($db) {
$this->db = MySQL::getInstance();
}
/**
* Check for counter today
*
* #param: string SQL
* #return: boolean (true = Counter exists, false = Counter doesnt exist)
**/
function checkExistingCounter($sql) {
$stmt = $this->db->prepare($sql);
$this->db->error;
if (!$stmt) {
echo 'Datenbankfehler';
exit;
}
$stmt->execute();
$stmt->store_result();
if ($stmt->num_rows) {
$stmt->close();
return true;
} else {
$stmt->close();
return false;
}
}
function counter() {
$sql = "SELECT ID FROM Counter WHERE Datum = CURDATE()";
$checkCounter = $this->checkExistingCounter($sql);
}
And this is a part of my index.php:
$db = new MySQL();
$statistic = new Statistic($db);
$statistic->counter();
You seem to be in a muddle here, implementing two sets of competing coding patterns:
your MySQL class both extends MySQLi (that is, any MySQL object is also a MySQLi object) and "delegates" to a MySQLi instance in its private variable $db
your Statistic class takes an instance of MySQL in its constructor ("dependency injection"), but then ignores it and asks the MySQL class for a "singleton" instance.
You need to read up more carefully on what each of these patterns is for, and decide on one or the other in each case (inheritance or delegation, dependency injection or singletons).
Currently, your code will do the following:
create a new MySQL object (which is also a MySQLi object, but hasn't been initialised to any particular database connection, because you haven't called parent::__construct())
in the MySQL constructor, set $this->host etc
in the connect() method, create a new MySQLi object, passing it the host etc
save this object as $this->db, which is only ever referenced in the destructor ($this->db->close())
return the MySQLi object from connect(), but nothing in __construct() is looking at that return value
back in the outer code, the MySQL object is passed to the constructor of the Statistic class
the constructor then ignores this, and calls the Singleton method MySQL::getInstance() instead
the getInstance() method (since this is the first time it has been called) will create a second MySQL object, repeating steps 1 to 5
this second MySQL object will be saved as $this->db on the Statistics object
the checkExistingCounter method attempts to use $this->db as a MySQLi connection, but the MySQL object was never connected to any database, so you get an error. (There is a connected connection, and if it wasn't private, you could access it as $this->db->db. There's another one kicking around as well, which was created at step 2, but you can't access that any more, because you ignored it at step 7.)
I want to use a class inside other class.
This is my first class:
class mydb {
public function connect($host="localhost",$user,$pass,$data,$persistency=False) {
$this->host = $host;
$this->user = $user;
$this->pass = $pass;
$this->data = $data;
$this->persistency = $persistency;
$this->link=#mysql_connect($this->host,$this->user,$this->pass)or die(mysql_error());
If(($this->link) AND ($this->data)) {
If(#mysql_select_db($this->data,$this->link) or die(mysql_error())) {
return True;
}
}
return False;
}
public function query($query="",$sup="") {
If($sup) {
$query=#mysql_query($query,$this->link)or die("<br /><br />".mysql_error()."<br /><br />");
} Else {
$query=#mysql_query($query,$this->link) or die("<br /><br />".mysql_error()."<br /><br />");
}
return($query);
}
}
This is my second class:
class articles {
function getpath($id) {
$sql=$db->query("select id,name from articles where id='$id'");
$row=mysql_fetch_array($sql);
return $row['name'];
}
}
I receive this error: ( Fatal error: Call to a member function query() )
You didn't create $db anywhere.
You need to create reference to the mydb class in the articles class. Assuming you create an instance of the mydb class and stored it in the variable $db, this is what you would do.
class articles {
public static $db = null;
...
self::$db->query(...);
}
articles::$db = $db;
The variable doesn't have to be static, it could be a regular class variable. You would then reference it like
$this->db->query(...);
As has been mentioned, you need an instance of the mydb class made available to articles.
Here's a simple example (an anti-pattern, perhaps):
class articles {
private $db = null; // add a class property to hold your database object
// each time we create an articles object, we'll connect to the database
public function __construct() {
$this->db = new mydb(); // add your connection params
}
public function getpath($id) {
// now we can reference the object via the class property $this->db
$sql = $this->db->query("select id,name from articles where id='$id'");
$row = mysql_fetch_array($sql);
return $row['name'];
}
// the rest of your code goes here
}
If you don't want to have to constantly create new mydb instances you can do something slightly different:
Dependency Injection
class articles {
private $db = null;
// Here we'll use an existing mydb object and pass it into our new articles
public function __construct(mydb $db) {
$this->db = $db;
}
// the rest of your code goes here
}
$db = new mydb(); // create mydb
$db->connect(); // add your params
$articles = new articles($db); // create articles using mydb from above
Singleton
class mydb {
// This is static, which is special
private static $instance = null;
public static function setInstance(mydb $db) {
self::$instance = $db;
}
public static function getInstance(mydb $db) {
return self::$instance;
}
// the rest of your code goes here
}
$db = new mydb(); // Create a mydb object
$db->connect(); // add your params
mydb::setInstance($db); // store the mydb object in the mydb class
// Later when you want to query we use the stored object:
// (and this will now work everywhere in the program--
// we don't need $db anymore)
mydb::getInstance()->query();
Analysis
Generally, you don't want to open or maintain many connections to your database. It's faster to use the same connection everywhere. Sometimes this is not appropriate, though.
The singleton is a cheap form of a Service Locator.. I.e. you create a resource (or "service") and then you have a means to locate that resource without passing a variable around with you. Singletons are a basic fix, but there are better ways to architect your applications.
You might want to read about two relevant design patterns: Service Locator and Dependency Injection. Both are described here: http://martinfowler.com/articles/injection.html
I'm trying to switch from building query strings and calling mysql_query() and embrace PDO. I've read a handful of tutorials, and understand the basic process of preparing a statement and assigning values to the placeholders.
What I don't understand is how to integrate this new technique into my existing web app.
Let me explain:
currently, I've got an init.php which is require_once'd on every page. It holds the database U/P and opens the database connection. With that loaded, I can sling queries left and right, willy AND nilly, anywhere I please without any further thought.
PDO strikes me as a much more deliberate approach. Do I still stuff all the U/P and connection handlers into my init file? Can I just work through my scripts and replace queries one-to-one with PDO statements?
Sorry for the simple question!
For what it is worth, I had the same issue and this is how I approached it. I divided the database functions into two different classes, Database and Query. I setup and open the database in my init function and then call instances of my Query class throughout the page, closing the queries each time I am done. Finally, I close the database.
The actual classes I use have some more error checking and other stuff I specifically, but here are the drafts I uses when putting the scheme together. Maybe it will be useful to you.
Database Class:
<?php
class Database {
private $db, $dbserver, $dbport, $dbname, $dbuser, $dbpassword;
//----------------------------------------------------------------------------------------------------------
//Function: __construct()
//----------------------------------------------------------------------------------------------------------
public function __construct($idbserver, $idbport, $idbname, $idbuser, $idbpassword)
{
$this->dbserver = $idbserver;
$this->dbport = $idbport;
$this->dbname = $idbname;
$this->dbuser = $idbuser;
$this->dbpassword = $idbpassword;
}
//----------------------------------------------------------------------------------------------------------
//Function: openDatabase()
//----------------------------------------------------------------------------------------------------------
public function openDatabase()
{
/* Create a new user db object with persistent database connection parameters */
try {
$this->db = new PDO("mysql:host=".$this->dbserver.";dbname=".$this->dbname.";port=".$this->dbport, $this->dbuser, $this->dbpassword, array(
PDO::ATTR_PERSISTENT => true
));
return true;
} catch (PDOException $e) {
print "Error!: " . $e->getMessage() . "<br/>";
die();
}
}
//----------------------------------------------------------------------------------------------------------
//Function: getDB
//----------------------------------------------------------------------------------------------------------
public function getDB()
{
//return the database
return $this->db;
}
//----------------------------------------------------------------------------------------------------------
//Function: getDBName
//----------------------------------------------------------------------------------------------------------
public function getDBName()
{
//return the database
return $this->dbname;
}
//----------------------------------------------------------------------------------------------------------
//Function: closeUserDatabase
//----------------------------------------------------------------------------------------------------------
public function closeDatabase()
{
//close the database
$this->db=null;
}
}
?>
Then my Query Class:
<?php
class Query {
private $db, $sql, $qresult;
//----------------------------------------------------------------------------------------------------------
//Function: __construct
//----------------------------------------------------------------------------------------------------------
public function __construct($idb, $isql)
{
$this->db = $idb;
$this->sql = $isql;
}
//----------------------------------------------------------------------------------------------------------
//Function: openQuery
//----------------------------------------------------------------------------------------------------------
public function openQuery($param)
{
/* assemble and run a query and return a result object */
//prepare the sql statement
//get the db object
$tempdb= $this->db->getDB();
$this->qresult = $tempdb->prepare($this->sql);
//bind parameters to the prepared statement
foreach ($param as $key => $value) {
$this->qresult->bindValue($key,$value);
}
return $this->qresult->execute();
}
//----------------------------------------------------------------------------------------------------------
//Function: fetchAssociative
//----------------------------------------------------------------------------------------------------------
public function fetchAssociative()
{
return $this->qresult->fetch(PDO::FETCH_ASSOC);
}
//----------------------------------------------------------------------------------------------------------
//Function: closeQuery
//----------------------------------------------------------------------------------------------------------
public function closeQuery()
{
$this->qresult = null;
}
}
?>
Then I set up the database in my init function:
//initialize the user database with buffered set true so we can use nested queries
$usrdb = new Database($udbserver, $udbport, $udbname, $udbuser, $udbpassword, array(PDO::MYSQL_ATTR_USE_BUFFERED_QUERY => true));
//open the database
$usrdb->openDatabase();
Then I call my query in the page:
//set up the sql
$sql = "SELECT * FROM Blah WHERE blahid = :filter";
//create the parameter array
$params = array(':filter'=>$filter);
//craetea new query
$q = new Query($usrdb, $sql);
//run the query
if ($q->openQuery($params)) {
//get the results if there are any
while ($row=$q->fetchAssociative()) {
$viewData['blahid']=$row['blahid'];
}
}
//clean up the query
$q->closeQuery();
After that I work with the results just like I always used to.
PDO works same way as old school approach. You still need to define connection and initialise object in order to query database.
I store the PDO database connection and retrieve an instance of it as a singleton.
Any place you need your database, to run a query or execute prepared statements, you use the singleton instance.
$db_handle = PDO_DBConnect::getInstance();
$stmt = $db_handle->prepare("SELECT * FROM users WHERE name= ?");
$result = $stmt->execute(array($cleaned_username));
...
If a class has multiple queries, you can store the handle in an instance variable,
$this->dbh = PDO_DBConnect::getInstance();
for convenience.
One other note is unlike pg_close or mysql_close, there is no close method for PDO. You simply set the db handle to null.