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 ;
}
}
Related
This is in reference to a CentOS 7 server running PHP 5.4 and MariaDB 5.5.
I am somewhat new to OOP in PHP. In converting a bunch of scripts from MySQL to MySQLi and from procedural database functions to OOP, I set up this basic Database class:
class Database
{
private $host = "localhost";
private $username;
private $password;
private $database;
private $dbconnect;
function __construct()
{
// Load config file for database connection info
$ini = parse_ini_file("config.ini");
$this->username = $ini['db.user'];
$this->password = $ini['db.pword'];
$this->database = $ini['db'];
}
public function connect()
{
// Only make a new connection if one not already established.
if (empty($this->dbconnect)) {
$mysql = new mysqli($this->host, $this->username, $this->password, $this->database);
if ($mysql->connect_errno) {
throw new appError($mysql->connect_error);
}
$this->dbconnect = $mysql;
}
return $this->dbconnect;
}
public function query($query)
{
$db = $this->connect();
$result = $db->query($query);
if ($db->errno) return false;
return $result;
}
public function select($query)
{
$rows = array();
$result = $this->query($query);
if ($result === false) return false;
// Create array with results
while ($row = $result->fetch_assoc()) {
$rows[] = $row;
}
return $rows;
}
}
With the former procedural database functions that used mysql_*, a persistent database connection was made near the start of the script and the resource ID was stored in a global variable, then all queries were run through that one database connection by accessing the global resource variable. Importantly, this worked well from within other functions and objects. Here's how that function worked:
function db_connect() {
if (!empty($GLOBALS['DBCONNECT']) && is_resource($GLOBALS['DBCONNECT'])) {
return $GLOBALS['DBCONNECT'];
} else {
$result = mysql_connect("localhost", DB_USER, DB_PASSWORD);
$GLOBALS['DBCONNECT'] = $result;
return $result;
}
}
So at the start of each script I'd do this...
db_connect();
And then run my queries like this...
$result = mysql_query($query, db_connect());
This made sure one database connection was made and that all queries are run through that connection.
With the above new Database class, I instantiate it at the start of my script...
$db = new Database;
$db->connect();
But I don't understand how to make that Database object accessible to other objects that need to perform database queries so that the same database connection is used by the entire script. What I do now is essentially this...
class MyClass
{
public function myFunction()
{
$db = new Database;
$data = $db->select("SELECT * FROM mydata WHERE id = 888");
...
}
}
This instantiates a new Database object within the above class, which is creating a new and additional connection to the database, because it can't access the Database $db object created in the parent calling script (that I know of).
Is there a way to use an object to open a persistent MySLQi database connection that all functions and objects loaded by that script can use? Or is this just something that is better done with a procedural function rather than a class and object? Does the solution lie in making the Database properties and its methods static? If I do that, I'm not sure how I load the database username and password from the config.ini file which the connect() function needs and is only done upon instantiation of the Database object.
I guess the basic question here is how do I access the properties or methods in an instantiated object from another object? Or maybe that isn't the question and I'm completely missing something else. Thanks!
You can do something like that a static method with a only a single tone
<?php
/*
* Mysql database class - only one connection alowed
*/
class Database {
private $_connection;
private static $_instance; //The single instance
private $_host = "HOSTt";
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);
// Error handling
if(mysqli_connect_error()) {
trigger_error("Failed to conencto to MySQL: " . mysql_connect_error(),
E_USER_ERROR);
}
}
// Magic method clone is empty to prevent duplication of connection
private function __clone() { }
// Get mysqli connection
public function getConnection() {
return $this->_connection;
}
}
and to make a connection to the database and make a query simple use the lines:
$db = Database::getInstance();
$mysqli = $db->getConnection();
$sql_query = "SELECT foo FROM .....";
$result = $mysqli->query($sql_query);
There are a few options, one simple one (and likely the preferred) would be to use a static factory method in your database class that returns a previously initialized instance of the database class.
class Database
{
//static instance of the class
private static $instance;
public static function getDB()
{
//if the instance is not set, set it
if(is_null(self::$instance))
{
self::$instance = new Database();
self::$instance->connect();
}
return self::$instance;
}
//rest of your class below
}
This static method can be called from anywhere in any context and would allow for getting the same connected instance of the database. Just call $db = Database::getDB() anywhere you need it. For example:
function TestFunction()
{
//get an instance to the database
$dbInstance = Database::getDB();
//then use the database object like you normally would.
$dbInstance->query("SELECT * FROM `someTable`");
}
class TestClass
{
public function doSomething()
{
//get an instance to the database
$dbInstance = Database::getDB();
//then use the database object like you normally would.
$dbInstance->query("SELECT * FROM `someTable`");
}
}
$tc = new TestClass();
$tc->doSomething();
Another option, and one that I use for simplicity, is declare your connection static and reference that everywhere in your database class for queries and such. This allows for creating $db = new Database() anywhere and will just use the same connection. I've had this frowned upon as if it were same as a global because you can really only connect to one database (subsequent connections would overwrite the connection variable) But it worked for me and I didn't need multiple connections. I also like it that I can have each instance remember whatever queries were run on that instance and have a private count of the queries run.
I am not real good at this yet and I have tried to figure it out but can't seem to do so.
I am trying to write a PHP class which reuses its database connection and doesn't make a new one every time it needs it. How do I make sure I am not recreating a new connection every time?
Please consider the code below and let me know if I am creating several connections or reusing the same one in the PHP file.
Thanks
PHP File
require_once(class.myclass.php);
$object = new myclass;
[...modify $object properties...]
if ($object->save_to_db()) {
echo "OK \n";
}
else {
echo "FAIL :" . $object->get_err();
}
[...modify $object properties...]
if ($object->save_to_db()) {
echo "OK \n";
}
else {
echo "FAIL :" . $object->get_err();
}
unset($object);
Class file
class myclass {
private $id;
private $db; // Database Connection
private $err; // Error
public function save_to_db() {
$db = $this->get_db();
$query = "...";
if(!$db->execute($query)) {
$this->err = $db->get_err();
$save_to_db = false;
}
else {
$save_to_db = true;
}
unset($db);
return $save_to_db;
}
private function get_db() {
if (!isset($this->db)) {
$this->db = new db;
}
return $this->db;
}
public function get_err() {
return $this->err;
}
function __destruct() {
if (isset($this->db)) {
unset($this->db);
}
}
}
A really good way to create a database handler is to use a static class method which returns itself. Called a Singleton pattern.
class DB {
/**
* Points to the instance of this class.
* #var object
*/
private static $instance = null;
/**
* Points to the database connection
* #var object
*/
private $db;
/**
* Initiates a new instance of self if one isn't set and opens a db connection
* #return object an object of self
*/
public static function getInstance() {
if(self::$instance == null) {
self::$instance = new self;
$this->db = new PDO();
}
return self::$instance;
}
/**
* Runs a query on the database
* #param string $query the query to run on the db
* #return array the result set
*/
public function query($query) {
// Query..
$stmt = $this->db->prepare("SELECT * FROM table");
$stmt->execute();
return $stmt->fetchAll();
}
}
And the simple usage would be:
$result = DB::getInstance()->query("SELECT * FROM table");
var_dump($result); // Outputs array of results.
This will only open one connection the first time you invoke DB::getInstance(). This way, you don't have to initiate the class and it supports chaining. This method is used by the Laravel framework aswell.
There are various ways of doing this, as lxg stated, the singleton method is one way of doing it, but you can also assign the class the connection which you open at the start of the scripts runtime.
class NeedDB {
/**
* Points to the DB connection
* #var object
*/
private $db;
/**
* Class constructor
* #param object $db The DB connection object
*/
public function __construct($db) {
$this->db = $db;
}
/**
* Runs a query on the database
* #param string $query the query
* #return array the result set
*/
public function query($query) {
$stmt = $this->db->prepare($query);
$stmt->execute();
return $stmt->fetchAll();
}
}
$db = new PDO();
$class = new NeedDB($db);
$result = $class->query("SELECT * FROM table");
var_dump($result);
There are several approaches to this. Generally, when creating a “real-world” application, you would use a decent framework which provides the database connection and common DB features as a dependency/service.
But while you're playing, these are the most common for a simple use case:
A) The Singleton design pattern. (Using singletons is discouraged these days in favour of dependency injection, but for the sake of playing/learning, it's acceptable to use it.)
B) Assign the DB connection to a global variable (i.e. outside the context of any function) and import it, when you need it, with the global keyword. This would look something like this:
$db = createMyConnection();
function iNeedTheDbConnection()
{
global $db;
// here goes the code that uses the connection
}
This is actually even worse than the singleton, but it works, and is still widely used (e.g. in WordPress).
I have just started learning the concept of Object oriented programming and I've written this class with functions.
It works fine, but I'm interested in knowing if I have done this correctly...
Here is my code:
class Database{
const DB_HOSTNAME = 'localhost';
const DB_USERNAME = 'root';
const DB_PASSWORD = 'password';
const DB_NAME = 'shop';
protected $_db_connect;
protected $_sql;
protected $_result;
protected $_row;
function db_connect(){
$this->_db_connect = mysql_connect(self::DB_HOSTNAME,self::DB_USERNAME,self::DB_PASSWORD) or die(mysql_error());
}
function slect_db(){
mysql_select_db(self::DB_NAME) or die(mysql_error());
}
function sql(){
$this->_sql = 'SELECT * FROM users';
}
function query(){
$this->_result = mysql_query($this->_sql);
}
function fetch_array(){
while($this->_row = mysql_fetch_array($this->_result)){
$username = $this->_row['user_USERNAME'];
echo "<ul>";
echo "<li>".$username."</li>";
echo "</ul>";
}
}
function db_close(){
mysql_close($this->_db_connect);
}
}
$database = new Database();
$database->db_connect();
$database->slect_db();
$database->sql();
$database->query();
$database->fetch_array();
$database->db_close();
Please use PDO or MySQLi as it's more secure and mysql_* functions are deprecated as stated above, I've provided some generic code using PDO to help you with that new venture. As stated in the comments, you really shouldn't be echoing out the data if you're looking for an object oriented design, what you should be doing is after you do the query, return the rows that you fetched and from there use something such as a foreach loop to then display your data. Doing your database class this way will also make sure that multiple database connections aren't open at once! Note, this code is just for you to reference, and should be tested before use in production or anything live.
config.php:
<?php
//Enter your database connection details here.
$host = 'localhost'; //HOST NAME.
$db_name = 'databasename'; //Database Name
$db_username = 'root'; //Database Username
$db_password = ''; //Database Password
try
{
$pdo = new PDO('mysql:host='. $host .';dbname='.$db_name, $db_username, $db_password);
}
catch (PDOException $e)
{
exit('Error Connecting To DataBase');
}
?>
database.class.php:
<?php
class database
{
function __construct($pdo)
{
$this->pdo = $pdo;
}
function getData()
{
$query = $this->pdo->prepare('SELECT * FROM database');
$query->execute();
return $query->fetchAll();
}
}
?>
index.php:
<?php
require_once 'config.php';
require_once 'database.class.php';
$db = new database($pdo);
$rows = $db->getData();
?>
It is possible to improve the way you connect to databases using autoloading and dependency injection containers. Here is a way of using Auryn to connect to your database while being sure there is only one connection opened and not having to manually require files throughout your application.
I will cover only PDO and Auryn here. There are other dependency injection containers and notably the mysqli extension to connect to database, but the content should help you using another container if you wish to do so.
The database class
Having a database class is superfluous. The \PDO class is already providing all necessary methods to query the database. Having a database class makes you repeat the functions it provides and limits your actions (or makes you create many functions) when you want to for example use multiple different fetch styles depending on your needs in a specific method.
Dependency Injection
If you haven't already, have a read on dependency injection. The point is that when a class needs to access the database, it should not have to bother constructing the \PDO object, it should be constructed with it:
class Mapper {
private $pdo;
public function __construct(\PDO $pdo) {
$this->pdo = $pdo;
}
public function createFromId($id) {
$stmt = $this->pdo->prepare("SELECT name FROM foo WHERE id=:id");
$stmt->execute([
":id" => $id,
]);
return $stmt->fetchObject();
}
}
Notice that I directly pass the \PDO object, not a wrapper class. That way, I always have access to all of its capabilities, not only a subset of user-defined functions.
Dependency Injection Container
A dependency injection container helps build your classes, giving them the objects they need, and giving you great flexibility on how to actually build those objects. Here I'll only focus on configuring and sharing a \PDO object through the use of Auryn.
I assume you have installed the required Auryn class, the easier way is using composer. This is out of the scope of this answer, there are multiple resources on how to use it.
Create the injector
$injector = new \Auryn\Injector();
Define the \PDO class parameters
$injector->define("PDO", [
":dsn" => "mysql:host=localhost;charset=utf8;dbname=dbname",
":username" => "user",
":passwd" => "passwd",
":options" => [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
PDO::ATTR_EMULATE_PREPARES => false,
],
]);
You can either write the configuration parameters directly here, or get them from a config file. I like having a config.ini file and using parse_ini_file() to get my configuration options, as I can easily switch databases by editing a config file.
Share the \PDO object
$injector->share("PDO");
This part is really important. This lines makes the injector give the same \PDO object every time it is tasked with constructing a class that needs a connection. Note that the order of the lines is not important, you can share the class before defining it, only make sure to create you database needing classes after writing both those lines.
Create your objects
$mapper = $injector->make("Mapper");
That's it. The injector will create your mapper object, creating the \PDO object if it haven't already been, passing the existing instance if it has.
Autoloading
Assuming you have used composer, you can make use of its great autoloader. Otherwise you can also roll you own autoloader.
The point here is to stop having require() everywhere in your code, especially if you have complex class hierarchies, which you should have in a single responsibility compliant class system.
Wrapping up
With this set up, you now can use the \PDO object in your classes while being assured there will only be one instance per request, without the need to require files everywhere, and without using a singleton anti-pattern.
$database = new Connection();
class Connection
{
function __construct()
{
switch($_SERVER['DOCUMENT_ROOT'])
{
case 'path':
$this->host = 'hostname';
$this->user = 'username';
$this->passwd = 'password';
$this->database = 'dbname';
break;
default :
$this->host = 'localhost';
$this->user = 'root';
$this->passwd = 'root';
$this->database = 'dbname';
break;
}
$this->clink = #mysql_connect($this->host,$this->user,$this->passwd);
#mysql_select_db($this->database,$this->clink);
}
}
$objConn = new mysqlconnect();
$Conn = $objConn->setobjConnect("localhost","root","P#ssw0rd");
class Database{
var $last_query; //Saved result of the last query made
var $last_result; //Results of the last query made
var $func_call; //A textual description of the last query/get_row/get_var call
var $link; //database link
var $lastquery; //last query
var $result; //query result
// Connect to MySQL database
function database() {
$this->link=mysql_connect(DB_HOST, DB_USER, DB_PASS) or die('Server connexion not possible.');
//Set All Charsets to UTF8
mysql_query("SET character_set_results=utf8 , character_set_client=utf8 , character_set_connection=utf8 , character_set_database=utf8 , character_set_server=utf8");
mysql_select_db(DB_NAME) or die('Database connection not possible.');
}
/** Query the database.
* #param $query The query.
* #return The result of the query into $lastquery, to use with fetchNextObject().
*/
function query( $query ){
$this->lastquery=$query;
$this->result=#mysql_query( $query, $this->link );
return $this->result;
}
/** Do the same as query() but do not return nor store result.
* Should be used for INSERT, UPDATE, DELETE...
* #param $query The query.
* #param $debug If true, it output the query and the resulting table.
*/
function execute($query)
{
#mysql_query($query);
}
/** Convenient method for mysql_fetch_object().
* #param $result The ressource returned by query().
* #return An ARRAY representing a data row.
*/
function fetchArray($result){
if ($result == NULL)
$result = $this->result;
if ($result == NULL || mysql_num_rows($result) < 1)
return NULL;
else
return mysql_fetch_assoc($result);
}
/** Close the connecion with the database server.
* It's usually unneeded since PHP do it automatically at script end.
*/
function close()
{
mysql_close($this->link);
}
/** Get the number of rows of a query.
* #param $result The ressource returned by query(). If NULL, the last result returned by query() will be used.
* #return The number of rows of the query (0 or more).
*/
function numRows($result = NULL)
{
if ($result == NULL)
return #mysql_num_rows($this->result);
else
return mysql_num_rows($result);
}
}
![<?php
class mysqlconnect{
private $server=null;private $user=null;private $password=null;private $objConnect=null;
function setobjConnect($s,$u,$p){
$this->server = $s;
$this->user = $u;
$this->password = $p;
return $this->objConnect = mysql_connect($this->server,$this->user,$this->password);
}
}
$objConn = new mysqlconnect();
$Conn = $objConn->setobjConnect("localhost","root","P#ssw0rd");
if($Conn)
{
echo "Database Connect";
}
else
{
echo "Database Connect Failed.";
}
mysql_close($Conn);
?>]
1
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 am rather new to the concepts of design patterns and I'm thinking of using Dependency Injection and Polymorphism (as each are applicable) -- but I have many Singletons and while most of them can easily be changed, my DBAL cannot.
The reason why is the DBAL creates a connection to the Database -- sets up it's own PDO object. If I passed new DBALs to every class that needs it (quite a few) I would get multiple, unnecessary connections to the database.
The class is something like this
class DB {
/**
* Hold the PDO object
* #var PDO
*/
private $_db;
/**
* Hold the last error messages
* #var string
*/
private $_error_message = NULL;
/**
* Hold the last error code
* #var int
*/
private $_error_code = NULL;
/**
* Connects to the database server and selects a database
*
* #param string $user MySQL database user
* #param string $password MySQL database password
* #param string $name MySQL database name
* #param string $host MySQL database host
* #return bool
*/
public function connect( $user, $password, $name, $host ) {
// Connect
try {
$this->_db = new PDO( "mysql:host=$host;dbname=$name", $user, $password );
} catch ( PDOException $e ) {
$this->_error_message = $e->getMessage();
$this->_error_code = $e->getCode();
return false;
}
return true;
}
// ...
}
?>
There will be many classes that inherit this class -- what is the best way to handle this? (I am new to design patterns)
An alternative method is to use a registry:
$db = new DB($host, $user, $pass);
Config::set('db', $db);
// Inside other classes
Config::get($this, 'db');
// Passes $this so the config can override the DB for different classes
Problem here is you end up with a Config singleton.
To truly do DI, you basicly need to pass object around to every other object.
$db = new DB($host, $user, $pass);
$user = new User($db);
// Or with a DI container
$c = new Pimple();
$c['db'] = function() {
return new DB($host, $user, $pass);
};
But ask yourself why you don't want to use a singleton.
If it looks like a singleton, smells like a singleton, and you use it like a singleton, then a singleton pattern is probably the best fit for the job.
http://pimple.sensiolabs.org/
add to the class:
private function __construct($user, $password, $name, $host ){
connect( $user, $password, $name, $host );
}
public static function getInstance(){
if(self::$_db == NULL) self::$_db = new DB;
return self::$_db;
}
and change the following:
// change $_db to be static!
private static $_db = NULL;