Similar questions have been asked but I believe they do not cover my case.
I experimented with a few ways to pass db class to other classes for access and found that the below works well.
My question is: is there anything wrong with that approach and whether Dependency Injection will be a better solution?
class Database{
private $db_host = "localhost";
private $db_user = "root";
private $db_password = "";
private $db_name = "dbName";
private $pdo;
public $instance;
function __construct() {
try{
//create a PDO connection and assign it to some handler
$this->pdo = new PDO('mysql:host='. $this->db_host.';dbname='. $this->db_name, $this->db_user, $this->db_password);
$this->pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$this->pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
// echo "connected";
} catch (PDOException $e) {
exit("Error connecting to database". $e->getMessage());
}
}
public function getPDO(){
return $this->pdo;
}
}
class OtherClass{
require_once 'Database.php';
private $db_instance;
private $dbh;
function __construct() {
$this->db_instance = new Database();
$this->dbh = $this->db_instance->getPDO();
}
}
How about a simple class from which you can create and destroy a connection to the database if its not needed. Lets refractor your code abit.
First create a classname that can be identified easily, something like
class pdo_mysql{
Its a pdo class, but based on mysql, next declare some variables, (The explanation in the comments)
/*
* Description - The name of the database
*/
private $dbname;
/*
* Description - The database username
*/
private $dbuser;
/*
* Description - The database password
*/
private $dbpass;
/*
* Description - The database Host
*/
private $dbhost;
/*
* Description - The database driver
* #Parameters - null
* To do -
* Notes:
*/
private $dbdriver;
/*
* Description - Stores the connection to the database
*/
private $con;
Then lets define the constructor for our class:
/*
* Description - This is the default method that is called when this class is instantiated.
* #Parameters - dbhost|dbdriver|dbname|dbusername|dbpassword
*/
function __construct($dbhost, $dbdriver, $dbname, $dbuser, $dbpass){
$this->dbhost = $dbhost;
$this->dbdriver = $dbdriver;
$this->dbname = $dbname;
$this->dbuser = $dbuser;
$this->dbpass = $dbpass;
}
Note that when the class is instantiated, most class variables are set.
Then our connection function:
/*
* Description - Creates a connection to the database
* Notes: - Returns true on success || string with an error msg on failure
*/
private function _connect(){
try {
$this->con = new PDO(''.$this->dbdriver.':host='.$this->dbhost.';dbname='.$this->dbname.'', $this->dbuser, $this->dbpass);
$this->con->setAttribute(PDO::MYSQL_ATTR_USE_BUFFERED_QUERY, false);
$this->con->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$this->con->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC);
return true;
} catch (PDOException $e){
return $e->getMessage();
}
}
And finally a function to disconnect our connectio to the database:
/*
* Description - Disconnects database connection
*/
private function _disconnect(){$this->con = null;return true;}
And a finally a function to query the database:
/*
* Description - Retrieves information from the database
*/
function _loadResult($data){}
This class will use data that is pre populated, so lets create for it a function to initialize it: (this should be in a different file/class)
/*
* Description - Get the config options from a file and initiate the database object
* Notes: - Returns a new object of the database connection class on success | false on fail
the config file must contain the following values dbhost|dbengine|dbname|dbusername|dbpassword
*/
static public function getDbo(){
if(!is_readable(/*path to your config file*/)){// lets check that the config file exists and is readable
return 'ERROR - The config file is missing or its unreadable!';
}
$config = parse_ini_file(/*path to your config file*/);
if($config === false){//parse the config file and return an error incase the purse fails
return 'ERROR - Could not parse the config file!';
}
//the following values are populated by the ones parsed from the config file
$dbhost = $config['dbhost'];
$dbclassprefix = $config['dbclassprefix'];
$dbdriver = $config['dbdriver'];
$dbname = $config['dbname'];
$dbuser = $config['dbuser'];
$dbpass = $config['dbpass'];
static $dbobject = null;//create the database object if all went well
if(null === $dbobject){$dbclass = $dbclassprefix.$dbdriver;$dbobject = new $dbclass($dbhost, $dbdriver, $dbname, $dbuser, $dbpass);}
return $dbobject;//return the database object
}
A case usage of the above code:
$db = self::getDbo();//create the database object
if(is_string($db)){//if the return value is a string, then thats an error
//return an error
}
$res = $db->_loadResult($data);//call a function from the database class
This is just a shaddy example from my head to give you some rough ideas, but its fully guaranteed to work if coded correctly, with the above classes and functions, you can change the engine to either PDO_mysqli or another another by changing the value in your config file. Hope you get the idea
Related
I'm new to classes and PDO as well. I'm trying to write a class with separate functions for connecting to the db and closing the connection so that, in a page, I can:
Open the connection with the $db->connOpen
Perform all the queries I need inside the page
Close the connection at the end of the script with $db->connClose
class database
{
private $host = '';
private $db_name = '';
private $charset = '';
private $username = '';
private $password = '';
public function setHost($host) {
$this->host = $host;
}
public function setDbName($db_name) {
$this->db_name = $db_name;
}
public function setUser($username, $password) {
$this->username = $username;
$this->password = $password;
}
public function connOpen() {
try {
$dsn = "mysql:host=$this->host;dbname=$this->db_name;charset=$this->charset";
$db = new PDO($dsn, $this->username, $this->password, array(PDO::ATTR_PERSISTENT => true));
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
} catch (PDOException $e) {
echo $e->getMessage();
}
}
public function connClose() {
$db = null;
}
}
I guess the problem is regarding more the classes syntax than the PDO, since to fire a query like the one below into the page I would need to instantiate again the class PDO, doubling the connection to the db.
$stmt = $dbo->prepare('SELECT * FROM products WHERE id=:id');
$stmt->execute(array(':id' => $id));
$res = $stmt->fetchAll(PDO::FETCH_ASSOC);
My questions are:
How can achieve what I want using a class like the one above?
Is it correct, in this case, using a persistent connection?
Thanks
For some reason, most people who wants to learn OOP starts with a database wrapper that implements singleton and interferes with error handling.
My tip is that you treat the database connection as any other variable:
class Post {
function getByUserId(PDO $connection, $user_id) {
}
}
$database = new PDO(...);
$post->getByUserId($database, 123);
This is called dependency injection.
And don't waste time and energy writing code to avoid useful features, such as:
Centralised error handling
Being able to connect to more than one database
Only care about writing a database wrapper when you really have real features to add on top of PDO. I can think of:
Convert dates to DateTime objects automatically
Pass all queries to a logger
Throw custom exceptions on interesting events, such as duplicate index violation
... and even then be extremely careful to not make PDO worse ;-)
I am using this with one singlton class which give me db ojbect and then I am using that object to query in other classes;
<?php
class db{
/*** Declare instance ***/
private static $instance = NULL;
/**
*
* the constructor is set to private so
* so nobody can create a new instance using new
*
*/
private function __construct() {
/*** maybe set the db name here later ***/
}
/**
*
* Return DB instance or create intitial connection
*
* #return object (PDO)
*
* #access public
*
*/
public static function getInstance() {
if (!self::$instance)
{
self::$instance = new \PDO("mysql:host=".databaseHost.";dbname=".databaseName."", databaseUserName,databasePassword);;
self::$instance-> setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION);
self::$instance-> setAttribute(\PDO::ATTR_EMULATE_PREPARES, false);
}
return self::$instance;
}
/**
*
* Like the constructor, we make __clone private
* so nobody can clone the instance
*
*/
private function __clone(){
}
} /*** end of class ***/
?>
class where i am querying using db object
<?php
class posts {
public function getPostOfUserId($userId,$offset=0,$limit=NULL){
$helperString=" ";
if(!empty($limit))
$helperString=" LIMIT $offset, $limit ";
$executor= db::getInstance()->prepare("SELECT posts.*,users.facebook_id, users.first_name as post_by FROM posts JOIN tagged_friends ON posts.id = tagged_friends.post_id JOIN users ON posts.user_id = users.id WHERE tagged_friends.user_id = :user_id ORDER BY posts.id DESC ".$helperString, array(\PDO::ATTR_CURSOR => \PDO::CURSOR_FWDONLY));
$executor->execute(array(':user_id'=>$userId));
$posts=$executor->fetchAll(\PDO::FETCH_ASSOC);
if(!empty($posts))
return $posts;
else
return ;
}
}
elow are some file structure of my website. I have removed unnecessary codes from these example codes in order to avoid confusions and larger code chunk.
class: connection.php (collected from internet)
<?php
class connection {
private $_connection;
private static $_instance; //The single instance
private $_host = "localhost";
private $_username = "username";
private $_password = "password";
private $_database = "database";
/*
Get an instance of the Database
#return Instance
*/
public static function getInstance() {
if(!self::$_instance) { // If no instance then make one
self::$_instance = new self();
}
return self::$_instance;
}
// Constructor
private function __construct() {
$this->_connection = new mysqli($this->_host, $this->_username,
$this->_password, $this->_database);
}
// Magic method clone is empty to prevent duplication of connection
private function __clone() { }
// Get mysqli connection
public function getConnection() {
return $this->_connection;
}
}
?>
class: login.php (this class authorize users in the website)
class login extends connection{
private $conn;
private $userid;
public $loggedin;
function __construct($id){
$this->userid = $id;
$this->set_login();
}
private function set_login(){
$this->connect();
$result = $this->conn->query("SELECT status FROM users WHERE userid='".$this->userid.'"');
if($result){
$this->loggedin = true;
}else{
$this->loggedin = false;
}
}
private function connect(){
$this->conn = parent::getInstance()->getConnection();
}
}
phppage: index.php (this page is for testing number of connection)
<?php
spl_autoload_register(function ($class) {
include '../classes/' . $class . '.php';
});
$user = new login("75");
if($user->logged_in){
$db = connection::getInstance();
$conn = $db->getConnection();
$query = 'SHOW STATUS WHERE variable_name LIKE "Threads_%" OR variable_name = "Connections"';
$result = $conn->query($query);
while($row=$result->fetch_assoc()){
echo $row['Variable_name'].' - '.$row['Value'].'<br />';
}
}
?>
the index.php will show the following information about connection:
Connections - 1026572
Threads_cached - 7
Threads_connected - 9
Threads_created - 42943
Threads_running - 2
I am referencing two $conn in the above examples, one is inside login.php class and the another is in index.php page, but from the same connection.php class.
I can know the total active connection from Threads_running value. But sometimes I can see that the Threads_running - 2 and sometimes it becomes Threads_running - 1 while I reload the page.
So, my question to expert is, according to the classes above, is there any possibility to have more than one database connection at a time?
If no, why it is showing Threads_running - 2 sometimes, which means that there are more than one connection at a time?
I am making a new script and am currently building the database class. I have created a function which checks a user's IP address. When I went to test it out, I was left with a blank page rather than the die('It Worked');.
I don't like posting a load of code however it would make more sense if I posted my class so far.
class.Database.inc
<?php
require_once('config.php'); // Configuration file
/**
* MySQLi database; only one connection is allowed.
*/
class Database {
// Database credentials from config file
private $_DATABASE_SERVER = DB_SERVER;
private $_DATABASE_USER = DB_USER;
private $_DATABASE_PASS = DB_PASS;
private $_DATABASE_NAME = DB_NAME;
private $_connection;
// Store the single instance.
private static $_instance;
/**
* Get an instance of the Database.
* #return Database
*/
public static function getInstance() {
if (!self::$_instance) {
self::$_instance = new self();
}
return self::$_instance;
}
/**
* Constructor.
* Database connection (server, user, password, name)
*/
public function __construct() {
$this->_connection = new mysqli($this->_DATABASE_SERVER, $this->_DATABASE_USER, $this->_DATABASE_PASS, $this->_DATABASE_NAME);
// Error handling.
if (mysqli_connect_error()) {
trigger_error('Failed to connect to MySQL: ' . mysqli_connect_error(), E_USER_ERROR);
}
}
/**
* Empty clone magic method to prevent duplication.
*/
private function __clone() {}
/**
* Get the mysqli connection.
*/
public function getConnection() {
return $this->_connection;
}
/**
* DATABASE IP CHECK FUNCTION
*/
public function checkIp($user_ip) {
$db = self::getInstance();
$mysqli = $db->getConnection();
$sql_query = "SELECT ip FROM ";
$sql_query .= "ip_address WHERE ";
$sql_query .= "ip = '$user_ip'";
$result = $mysqli->query($sql_query) or die(mysqli_error($mysqli));
if ($row = $result->fetch_assoc()) {
die('It Worked!');
}
}
}
?>
The problem is with the function located towards the bottom of the file, the checkIp(); function.
I have created a test.php file with these contents.
<?php
require_once('class.Database.inc.php');
Database->checkIp('1');
?>
I'm not sure whether I've attempted to access the function incorrectly or whether it is something else. The value 1 is in the database, in case anyone was wondering.
I would avoid the singleton and create a Database object when you need one. Instead of Database->checkIp('1') you should be using an instance of Database rather than calling the function more static like. Also, your checkIp function should just access the getConnection function via $this since you're already inside the class and don't need to create a new instance.
You may want to ensure error reporting is on so that it's easier to identify problems:
error_reporting(E_ALL);
ini_set('display_errors', 1);
<?php
require_once('config.php'); // Configuration file
/**
* MySQLi database; only one connection is allowed.
*/
class Database {
// Database credentials from config file
private $_DATABASE_SERVER = DB_SERVER;
private $_DATABASE_USER = DB_USER;
private $_DATABASE_PASS = DB_PASS;
private $_DATABASE_NAME = DB_NAME;
private $_connection;
/**
* Constructor.
* Database connection (server, user, password, name)
*/
public function __construct() {
$this->_connection = new mysqli($this->_DATABASE_SERVER, $this->_DATABASE_USER, $this->_DATABASE_PASS, $this->_DATABASE_NAME);
// Error handling.
if (mysqli_connect_error()) {
trigger_error('Failed to connect to MySQL: ' . mysqli_connect_error(), E_USER_ERROR);
}
}
/**
* Get the mysqli connection.
*/
public function getConnection() {
return $this->_connection;
}
/**
* DATABASE IP CHECK FUNCTION
*/
public function checkIp($user_ip) {
$mysqli = $this->getConnection();
$sql_query = "SELECT ip FROM ";
$sql_query .= "ip_address WHERE ";
$sql_query .= "ip = '$user_ip'";
$result = $mysqli->query($sql_query) or die(mysqli_error($mysqli));
if ($row = $result->fetch_assoc()) {
die('It Worked!');
}
}
}
?>
usage:
<?php
require_once('class.Database.inc.php');
$database = new Database();
$database->checkIp('1');
?>
I'm wondering why in the PHP code below, the PDO objects $db is passed as NULL. i.e. $db=NULL in the constructor parameter.
class ColoredListsUsers
{
/**
* The database object
* #var object
*/
private $_db;
/**
* Checks for a database object and creates one if none is found
* #param object $db
* #return void
*/
public function __construct($db=NULL) /* why is #db passed as null here ? */
{
if(is_object($db))
{
$this->_db = $db;
}
else
{
$dsn = "mysql:host=".DB_HOST.";dbname=".DB_NAME;
$this->_db = new PDO($dsn, DB_USER, DB_PASS);
}
}
}
Earlier $db was declared as a PDO object:
// Create a database object
try {
$dsn = "mysql:host=".DB_HOST.";dbname=".DB_NAME;
$db = new PDO($dsn, DB_USER, DB_PASS);
} catch (PDOException $e) {
echo 'Connection failed: ' . $e->getMessage();
exit;
}
Just doesn't seem to make sense to make $db a PDO object then pass it as null....
code is from http://www.copterlabs.com/blog/creating-an-app-from-scratch-part-5/
public function __construct($db=NULL) means $db is an optional parameter. And if it's not specified then the default NULL value will be used.
In this case - look several lines below, the else body - the default connection is created.
Here I am sharing code of two functions. Both return an instance of a PDO object. But one of them works fine when a statement is inserted using the PDO connection where as other one does not. The same identical code is used in both cases to insert a record.
This is the function whose returned PDO object works with the insert statement.
public function getPDO() {
/** mysql hostname */
$hostName = 'localhost';
/** mysql database name */
$dbName = 'testDB';
/** db user name */
$userName = 'root';
/** db password */
$password = '';
try {
$str = "mysql:dbname=$dbName;host=$hostName";
$db = new PDO($str, $userName, $password );
return $db;
}catch(PDOException $e) {
echo "PDO connection failed. <br />";
echo $e->getMessage();
return null;
}
}
This is the function whose returned PDO object DOES NOT work with the insert statement. In this function the connection string and other connection parameters are read from static values defined in the class.
public function getPDO() {
try {
$connStr = "mysql:dbName=".self::$dbName.";host=".self::$hostName;
$db = new PDO($connStr, self::$userName, self::$password);
return $db;
}catch(PDOException $e) {
echo "PDO connection failed. <br />";
echo $e->getMessage();
return null;
}
}
Here is how the static variables are defined:
/** mysql hostname */
static $hostName = "localhost";
/** mysql database name */
static $dbName = "testDB";
/** db user name */
static $userName = 'root';
/** db password */
static $password = '';
And here is the piece of code which uses the PDO object to insert a record.
public function save() {
//get the PDO
$db_func = new db_functions();
$pdo = $db_func->getPDO();
$query = "INSERT INTO city(name, state, country) VALUES ('Vienna', 'Virginia', 'USA')";
echo $query;
$count = $pdo->exec($query);
if($count > 0) {
echo '</br> Successful </br>';
}else {
echo '</br> Unsuccessful </br>';
}
}
When I use the second function, the PDO object is created successfully but the insert fails. I do not see any error but the count is 0 and no records in the DB.
I am suspecting this has to do with the way I am declaring and using static variables in my code. Thanks for your help.
It's not related to static variables, your second method just doesn't pass the database name properly. It must be 'dbname' instead of 'dbName'.