How can I pass DB connection object from one class to another? - php

I'm trying to pass a PDO connection object from one class to another. But I'm not being very successfull. And I only want to instanciate only one PDO object.
With the help from dqhendricks and awm I managed to get the following solution working:
class Factory {
function createUser($id = NULL) {
return new User(Conn::get_conn(), $id);
}
function createApplication($id = NULL) {
return new User(Conn::get_conn(), $id);
}
}
class Conn {
private static $conn = NULL;
private function __construct() {}
private static function init() {
$conf = self::config();
try {
self::$conn = new PDO($conf['dsn'], $conf['user'], $conf['pass']);
}
catch (PDOException $e) {
echo $e->getMessage();
}
}
public static function get_conn() {
if (!self::$conn) { self::init(); }
return self::$conn;
}
private static function config($cfg_file = 'sl.config') {
$config = parse_ini_file('/../'.$cfg_file);
$conf = array();
$conf['user'] = $config['db_user'];
$conf['pass'] = $config['db_password'];
$conf['dsn'] = 'mysql:dbname='.$config['db_name'].';host='.$config['db_host'];
return $conf;
}
}
In my UserDAO class, I can now do this:
class UserDAO {
private $db;
private $id;
function UserDAO (&$db, $id) {
$this->db = &$db;
$this->id = &$id;
}
public function getRows($sql)
{
$result = $this->db->query($sql);
$row = $result->fetch(PDO::FETCH_ASSOC);
return $row;
}
function getUsers($limit = 10) {
$sql ="SELECT * FROM sl_store LIMIT $limit";
return $this->getRows($sql);
}
}
//My User class
class User extends UserDAO implements iUser {}
// And to test it working:
$user1 = Factory::createUser('5');
$user2 = Factory::createApplication('7');

How about defining an abstract class which gives you the PDO object on request?
E.g.
abstract class Db {
private static $x = null;
private static function init() {
try {
self::$x = new PDO(...);
} catch (PDOException $e) {
...
}
}
public static function getX() {
if (!self::$x) self::init();
return self::$x;
}
}

no need to have your class create an instance of itself if all you want is an instance of a different object back. maybe make a static method in Conn to return an instance of a db connection.
class Conn {
// prevent new statement
private __construct() {}
public static return_pdo() {
blah
blah
blah
return $db;
}
public static config($file) {
do stuff
}
}
then call statically
$pdo = Conn::return_pdo();

It's because new Conn() returns $Conn object, not the value from $Conn->Conn() method.
Try this:
class Conn{
function Conn() {
$db = new PDO($conf['dsn'], $conf['user'], $conf['pass']);
}
function get_db() {
return $this->db;
}
}
class Factory {
function createUser($id = NULL) {
$new_conn = new Conn();
$db = $new_conn->get_db();
}
}

Related

pass database connection to phpunit test

i am trying to test a function that executes a select statement, but im a bit confused about the way i should pass the connection to this function and how to assert the data.
this is the class with the function i want to test:
class AdProviders {
public $providers = null;
protected $db = null;
function __construct() {
}
function getDbh() {
if ($this->db === null){
$this->db = Slim::getInstance()->db;
}
return $this->db->getConnection();
}
function setDbh($db) {
$this->db = $db;
}
function getProviders() {
if ($this->providers == null){
$DBH = $this->getDbh();
$query = "select * from providers";
$STH = $DBH->query($query);
$STH->setFetchMode(PDO::FETCH_ASSOC);
while($provider = $STH->fetch()) {
$this->providers[$provider['id']] = $provider;
}
}
return $this->providers;
}
}
and this is my test:
require dirname(__FILE__).'/../../src/vendor/autoload.php';
class AdProvidersTest extends PHPUnit_Extensions_Database_TestCase
{
/**
* #return PHPUnit_Extensions_Database_DB_IDatabaseConnection
*/
public function getConnection()
{
$pdo = new PDO('mysql:host=localhost;dbname=testdb', 'root', 'LWw6A$cXvvf');
return $this->createDefaultDBConnection($pdo, 'testdb');
}
/**
* #return PHPUnit_Extensions_Database_DataSet_IDataSet
*/
public function getDataSet()
{
return $this->createXMLDataSet(dirname(__FILE__).'/../dbTest/fixtures/providers.xml');
}
public function testgetProviders_compareResult()
{
$db = $this->getConnection();
$fixture = new AdProviders($db);
$res = $fixture->getProviders();
}
}
when i run the test function now i get the following error:
1) AdProvidersTest::testgetProviders_compareResult
Trying to get property of non-object
why am i getting it? and how can i get the data inside the providers.xml
using the getProviders? thx
You have to change the constructor of AdProviders class to that the PDO instance can be passed to it. Otherwise $fixture = new AdProviders($db); will not work.
Please change
function __construct() {
}
to
function __construct($db) {
$this->db = $db;
}
UPDATE:
I saw that you are not storing a PDO instance in your AdProviders::$db property. If your are passing a PDO instance to your __construct you should also change your method setDbh from
function getDbh() {
if ($this->db === null){
$this->db = Slim::getInstance()->db;
}
return $this->db->getConnection();
}
to
function getDbh() {
if ($this->db === null){
$this->db = Slim::getInstance()->db->getConnection();
}
return $this->db;
}

Share a parameters class with other classes

I would like to share the parameters taken from a table in a db.
To take these parameters, I created a class made ​​for this.
this is my scenario to share the parameters contained in a db between the various classes. is the correct approach to do this?
class Database
{
$private mys;
public function __construct()
{
$this->mys = new mysqli(....);
}
}
class params
{
$private db;
$public var1;
$public var2;
public function __construct()
{
$this->db = new Database();
}
public function getParams()
{
$result = $this->db->mys->query ("SELECT * FROM params");
while($row = $result->fetch_assoc())
{
$this->var1 = $row['var1'];
$this->var2 = $row['var2'];
}
}
}
class foo
{
private $db;
private $ps;
public function __construct()
{
$this->db = new Database;
$this->ps = new Params;
}
public function viewParams()
{
echo $this->ps->var1;
echo $this->ps->var2;
}
}
To access the private fields from other classes declare public 'getter' methods like:
public getVal1() {
return $this->val1;
}
In my modest opinion, there are various ways to share a parameters class with other classes. And they depends on how your system currently is and how it'll grow.
From what I learnt, a correct ways to pass dependencies to your classes is to inject they to the constructor, ex instead of:
class foo
{
...
public function __construct()
{
$this->db = new Database;
I would prefer to instanciate $db first:
$db = new Database();
and then
class foo
{
...
public function __construct(Database $db)
{
$this->db = $db;
This way your classes are loosely coupled and you can afford Unit Testing easily one day, working with interfaces and not concrete implementations, etc...please take a look at http://en.wikipedia.org/wiki/Dependency_injection
As a personal suggest I'll try to design the params class as an entity, mapped as a Db table object:
//entityParams.php
class EntityParams
{
private $id; //column name
private $columnA;
private $columnB;
public function setId($id)
{
$this->id = $id;
}
public function setColumnA($columnA)
{
$this->columnA = $columnA;
}
...
}
then use a Database class as a simple connector class (just to promotes single-responsibility and separation of concerns):
//Database.php
class Database
{
private $dbh = NULL;
public function connect()
{
$this->dbh = new Mysqli(...);
}
public function getConnection()
{
if(is_null($this->dbh))
{
$this->connect();
}
return $this->dbh;
}
}
and use a DatabaseManager instead, to let it do all dirty works.Something like:
//DatabaseManager.php
class DatabaseManager
{
private $db;
private $entities = array();
private $currentEntity;
public function __construct(Database $db)
{
$this->db = $db;
}
public function fromEntity($entityName)
{
$entityClass = "Entity".ucfirst($entityName);
if(!isset($this->entities[$entityClass]))
{
$this->entities[$entityClass] = $entityName;
}
$this->currentEntity = $entityClass;
return $this;
}
public function getAll()
{
$results = $this->db->getConnection()->query("SELECT * FROM {$this->entities[$this->currentEntity]}");
$entities = array();
foreach ($results->fetch_all(MYSQLI_ASSOC) as $key => $item)
{
$e = new $this->currentEntity;
$e->setId($item['id']);
$e->setColumnA($item['columnA']);
$e->setColumnB($item['columnB']);
$entities[] = $e;
}
return $entities;
}
Finally your Foo class (and everyone else) just has a simple dependency with the DatabaseManager (which could evolve as a RespositoryManager):
//Foo.php
class Foo
{
public $dbm;
public function __construct(DatabaseManager $dbm)
{
$this->dbm = $dbm;
}
public function viewParams()
{
return $this->dbm->fromEntity("params")->getAll();
}
public function viewParam($id)
{
return $this->dbm->fromEntity("params")->find(2);
}
}
//client.php
/* Here you can instantiate the classes and pass they through constructor or investigate on how to create and use a Dependency Injection Container */
$db = new Database();
$dbm = new DatabaseManager($db);
$foo = new Foo($dbm);
var_dump($foo->viewParams());
var_dump($foo->viewParam(1));
I just wrote simple basic ideas that could be worth to play around it and improve it.

Calling an object of class in another class

I am a newbie to OOP. I have worked in procedural programming a lot. So i am in little trouble right now. Can you please tell me how to call an object of a class in another class and then i can access all the variables and function of that class with using that object.
For e.g
I have a class of DBconnection. i write my db queries in it. Now i have an other class named Users. Now i need to access the db queries in User class, definitely i need object of DBconnection class to access all db queries. What can i do please help
The example code which i have written is as below:
**DBConnection class**
class DBConnection
{
public $SITEURL;
public $servername;
public $username;
public $password;
public $dbname;
public $objDbConnect;
function DBConnection(){
$this->SITEURL=Configuration::SITEURL;
$this->servername=Configuration::servername;
$this->username=Configuration::username;
$this->password=Configuration::password;
$this->objDbConnect=mysql_connect($this->servername,$this->username,$this->password);
if($this->objDbConnect){
mysql_select_db($this->dbname);
}
}
function InsertRecord($pStrTableName,$pArrField,$pArrValue)
{
$strSql="insert into $pStrTableName (";
$intFieldSize=count($pArrField);
for($i=0;$i<$intFieldSize;$i++)
{
if($i==$intFieldSize-1)
{
$strSql.=$pArrField[$i];
}
else
{
$strSql.=$pArrField[$i].",";
}
}
$strSql.=") values (";
$intFieldSize=count($pArrValue);
for($i=0;$i<$intFieldSize;$i++)
{
if($i==$intFieldSize-1)
{
$strSql.="'".$pArrValue[$i]."'";
}
else
{
$strSql.="'".$pArrValue[$i]."'".",";
}
}
$strSql.=")";
if(mysql_query($strSql))
{
return 1;
}
else
{
return 0;
}
}
}
**Users class**
class Users{
var $username,
$userpassword,
$email,
function User(){
}
}
The best way to do this would be to use Singleton pattern.
Following is an example
class DBConnection
{
private static $instance = null;
private function __construct() {
}
/*
* #return DBConnection
*/
public static function get_db()
{
if ( empty(self::$instance) ) self::$instance = new DBConnection();
return self::$instance;
}
public function query()
{
}
}
class User
{
function testfunc()
{
$db = DBConnection::get_db();
$db->query();
}
}
You can declare an object of your class in your second class and use it like
$c= new DBConnection();
$c->InsertRecord('Parameters here etc');
echo $c->username; //and other public variables similarly
DBConnection class
class DBConnection
{
public $SITEURL;
public $servername;
public $username;
public $password;
public $dbname;
public $objDbConnect;
function DBConnection(){
$this->SITEURL=Configuration::SITEURL;
$this->servername=Configuration::servername;
$this->username=Configuration::username;
$this->password=Configuration::password;
$this->objDbConnect=mysql_connect($this->servername,$this->username,$this->password);
if($this->objDbConnect){
mysql_select_db($this->dbname);
}
}
function InsertRecord($pStrTableName,$pArrField,$pArrValue)
{
$strSql="insert into $pStrTableName (";
$intFieldSize=count($pArrField);
for($i=0;$i<$intFieldSize;$i++)
{
if($i==$intFieldSize-1)
{
$strSql.=$pArrField[$i];
}
else
{
$strSql.=$pArrField[$i].",";
}
}
$strSql.=") values (";
$intFieldSize=count($pArrValue);
for($i=0;$i<$intFieldSize;$i++)
{
if($i==$intFieldSize-1)
{
$strSql.="'".$pArrValue[$i]."'";
}
else
{
$strSql.="'".$pArrValue[$i]."'".",";
}
}
$strSql.=")";
if(mysql_query($strSql))
{
return 1;
}
else
{
return 0;
}
}
}
Users class
class Users{
var $username;
var $userpassword;
var $email;
var $dbcon;
public function __construct()
{
//create an object
$this->dbconn = new DBConnection();
//get user name from dbconnection class
echo $this->dbconn->username;
}
function User(){
}
}
//create object for user class
$user = new User();
**DBConnection class**
class DBConnection
{
public $SITEURL;
public $servername;
public $username;
public $password;
public $dbname;
public $objDbConnect;
function DBConnection(){
$this->SITEURL=Configuration::SITEURL;
$this->servername=Configuration::servername;
$this->username=Configuration::username;
$this->password=Configuration::password;
$this->objDbConnect=mysql_connect($this->servername,$this->username,$this->password);
if($this->objDbConnect){
mysql_select_db($this->dbname);
}
}
public static function getConnection(){
if(!empty($this)){
return $this;
}
return new DBConnection();
}
function InsertRecord($pStrTableName,$pArrField,$pArrValue)
{
$strSql="insert into $pStrTableName (";
$intFieldSize=count($pArrField);
for($i=0;$i<$intFieldSize;$i++)
{
if($i==$intFieldSize-1)
{
$strSql.=$pArrField[$i];
}
else
{
$strSql.=$pArrField[$i].",";
}
}
$strSql.=") values (";
$intFieldSize=count($pArrValue);
for($i=0;$i<$intFieldSize;$i++)
{
if($i==$intFieldSize-1)
{
$strSql.="'".$pArrValue[$i]."'";
}
else
{
$strSql.="'".$pArrValue[$i]."'".",";
}
}
$strSql.=")";
if(mysql_query($strSql))
{
return 1;
}
else
{
return 0;
}
}
}
**Users class**
class Users{
var $username,
$userpassword,
$email,
function User(){
$db = DBConnection::getConnection();
$db->InsertRecord($x, $x, $x);
}
}
1 . You can change your class DBConnection to singleton. And call your class in User like this DBConnection::getInstance()->InsertRecord(...). It's easy. But not recommended.
2 . You can push your DBConnection instance inside User. Like this
class Users{
var $db_connection;
function __construct($db_connection){
$this->db_connection = $db_connection;
}
$db_connection = new DBConnection(...);
$user = new User($db_connection);
3 . You can instantiate new DBCOnnection inside User class. Like in #Sundar answer.
P.S. Do not use old construct style.
You can keep a reference to the DBConnection in your User class (pass it in in the constructor).
private $dbConn;
function __contruct($dbConnRef) {
$this->dbConn = $dbConnRef;
}
public function storeSelf() {
$this->dbConn->InsertRecord(...);
}

Extending mysqli and using multiple classes

I'm new to PHP oop stuff.
I'm trying to create class database and call other classes from it. Am I doing it the right way?
class database:
class database extends mysqli {
private $classes = array();
public function __construct() {
parent::__construct('localhost', 'root', 'password', 'database');
if (mysqli_connect_error()) {
$this->error(mysqli_connect_errno(), mysqli_connect_error());
}
}
public function __call($class, $args) {
if (!isset($this->classes[$class])) {
$class = 'db_'.$class;
$this->classes[$class] = new $class();
}
return $this->classes[$class];
}
private function error($eNo, $eMsg) {
die ('MySQL error: ('.$eNo.': '.$eMsg);
}
}
class db_users:
class db_users extends database {
public function test() {
echo 'foo';
}
}
and how I'm using it
$db = new database();
$db->users()->test();
Is it the right way or should it be done another way?
Thank you.
You can do it that way, there's nothing wrong with that (I do something similar quite often). The only thing I would suggest is using exceptions instead of die (that way you can safely handle the error)...
protected function error($eNo, $eMsg, $extra = '') {
throw new Exception('MySQL error: ['.$eNo.'] '.$eMsg.': '.$extra);
}
Plus, I'd suggest overloading the query method as well
public function query($sql, $result_mode = MYSQLI_STORE_RESULT) {
$result = parent::query($sql, $result_mode);
if ($result === false) {
$this->error($this->errno, $this->errstr, $sql);
}
return $result;
}
I'd also suggest storing a copy of the $db object inside of the child class. So:
class db_users extends database {
protected $db = null;
public function __construct(Database $db) {
$this->db = $db;
}
public function test() {
echo 'foo';
}
}
Then, in __call:
if (!isset($this->classes[$class])) {
$class = 'db_'.$class;
$this->classes[$class] = new $class($this);
}
There is nothing wrong with this factory style for creating classes. I'd place a bit of exception handling in it.
My only other concern is extending database in your sub classes.
So I'd modify as follows:
public function __call($className, $args) {
if (!isset($this->classes[$class])) {
if(include_once('db_'.$class)) {
$class = 'db_'.$class;
$this->classes[$class] = new $class($this);
} else {
throw new Exception("Db class not found");
}
}
return $this->classes[$class];
}
And the users class as:
public class db_users {
private $db;
public __constructor($db) {
$this->db = $db;
}
public function test() {
return 'Foo';
}
}

Using a database class in my user class

In my project I have a database class that I use to handle all the MySQL stuff. It connects to a database, runs queries, catches errors and closes the connection.
Now I need to create a members area on my site, and I was going to build a users class that would handle registration, logging in, password/username changes/resets and logging out. In this users class I need to use MySQL for obvious reasons... which is what my database class was made for.
But I'm confused as to how I would use my database class in my users class. Would I want to create a new database object for my user class and then have it close whenever a method in that class is finished? Or do I somehow make a 'global' database class that can be used throughout my entire script (if this is the case I need help with that, no idea what to do there.)
Thanks for any feedback you can give me.
Simple, 3 step process.
1/ Create a database object.
2/ Give it to your user class constructor.
3/ Use it in the user methods.
Little example.
File Database.class.php :
<?php
class Database{
public function __construct(){
// Connects to database for example.
}
public function query($sqlQuery){
// Send a query to the database
}
[...]
}
In User.class.php :
<?php
class User{
private $_db;
public function __construct(Database $db){
$this->_db = $db;
}
public function deleteUser(){
$this->_db->query('DELETE FROM Users WHERE name = "Bobby"');
}
}
Now, in userManager.php for example :
<?php
$db = new Database();
$user = new User($db);
// Say bye to Bobby :
$user->deleteUser();
If you want the current trendy name of this old technique, google "Dependency Injection". The Singleton pattern in php will fade away soon.
As he said, put all your functions in the database class and use the database object to access those functions from your user class. This should be the best method in your case.
Eg:
global $database;
userclassvar = $database->doSomething();
What I like to do is make the database class with the Singleton pattern in mind. That way, if you already have a database object, it just retrieves it, otherwise creates a new one. For example:
Database.class.php
class Db
{
protected static $_link;
private function __construct()
{
// access your database here, establish link
}
public static function getLink()
{
if(self::_link === null) {
new Db();
}
return self::_link;
}
// etc.
}
User.class.php
class User
{
protected $_link; // This will be the database object
...
public function __construct()
{
$this->_link = Db::getLink();
}
}
And now you can use User's $_link property to do the database functions, like $this->_link->query(...). You don't necessarily have to put the Db::getLink() in the constructor if your class doesn't have to interact with the database that much.
Since you are using the database as an object, why not just add methods to the object that your "users class" can employ to take care of the things it needs to do. The users class can contain a pointer to the database class. The database class will protect your database, and assure that the users class is using it appropriately.
Here is a solution using PDO.
<?php
class Database {
private static $dbh;
public static function connect() {
$host = "mysql:dbname=YOUR_DB_NAME;host=YOUR_DB_SERVER";
$username = "YOUR_USERNAME";
$password = "YOUR_PASSWORD";
try {
self::$dbh = new PDO( $host, $username, $password );
self::$dbh->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_SILENT );
self::$dbh->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_WARNING );
self::$dbh->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION );
} catch( PDOException $e ){
$error_message = $e->getMessage();
exit();
}
return self::$dbh;
}
}
class MYObject {
public static $dbh = null;
public function __construct(PDO $db = null) {
if($db === null){
$this->dbh = Database::connect();
} else {
$this->dbh = $db;
}
}
}
class User extends myObject {
public function __construct($id = null, PDO $db = null) {
if($db === null){
parent::__construct();
} else {
parent::__construct($db);
}
if($id !== null){
return $this->select($id);
}
}
public function select($id) {
$retVal =false;
try {
$stmt = $this->dbh->prepare("SELECT...");
$stmt->execute();
if( $stmt->rowCount()==1 ){
$row = $stmt->fetchAll(PDO::FETCH_ASSOC);
$retVal =json_encode($row);
}
} catch (PDOException $e ) {
$error_message = $e->getMessage();
exit();
}
return $retVal;
}
}
?>
I think the better aproach would be to create the database class that instatiate right away on its own on a database.php and then include it on user.php. then every time you create a function that needs a database, you globalise the database object.
Check this.
databse.php
<?php
require_once ('includes/config.php');
class MysqlDb{
public $connection;
private $last_query;
private $magic_quotes_active;
private $real_escape_string_exists;
public function __construct() {
$this->open_connection();
$this->magic_quotes_active = get_magic_quotes_gpc();
$this->real_escape_string_exists = function_exists( "mysql_real_escape_string" );
}
public function open_connection() {
$this->connection = mysql_connect(DBHOST,DBUSER,DBPASS);
if(!$this->connection){
die("Could not Connect ".mysql_error());
}else{
$db = mysql_select_db(DB, $this->connection);
}
}
public function close_connection(){
if(isset($this->connection)){
mysql_close($this->connection);
unset($this->connection);
}
}
public function query($sql){
$this->last_query = $sql;
$results = mysql_query($sql, $this->connection);
$this->comfirm_query($results);
return $results;
}
private function comfirm_query($results){
if(!$results){
$output = "Query Failed " .mysql_error()."<br />";
$output .= "Last Query: " . $this->last_query;
die($output);
}
}
public function escape_value($value){
if( $this->real_escape_string_exists ) {
if($this->magic_quotes_active ) { $value = stripslashes( $value ); }
$value = mysql_real_escape_string( $value );
} else {
if( !$this->magic_quotes_active ) { $value = addslashes( $value ); }
}
return $value;
}
public function fetch_array($results){
return mysql_fetch_array($results);
}
public function num_row($results){
return mysql_num_rows($results);
}
public function insert_id(){
return mysql_insert_id($this->connection);
}
public function affected_row(){
return mysql_affected_rows();
}
}
$database = new MysqlDb();
?>
here is the user.php
<?php
require_once ('includes/database.php');
class User {
public $id;
public $fName;
public $lName;
Public $userName;
public $password;
public $email;
public $acess;
public static function find_all(){
global $database;
return self::find_by_sql("SELECT * FROM users");
}
public static function find_by_id($id=0){
global $database;
$results_array = self::find_by_sql("SELECT * FROM users where id={$id}");
return !empty($results_array)? array_shift($results_array) : false;
}
public static function find_by_sql($sql){
global $database;
$results = $database -> query($sql);
$object_array = array();
while($row = $database -> fetch_array($results)){
$object_array[] = self::instantiate($row);
}
return $object_array;
}
public static function instantiate($row){
$user = new self;
foreach($row as $attribute => $value){
if($user -> has_attribute($attribute)){
$user -> $attribute = $value;
}
}
return $user;
}
private function has_attribute($attribute){
$object_vars = get_object_vars($this);
return array_key_exists($attribute, $object_vars);
}
}
?>

Categories