Single DB connection - DI - Singleton

First I would like to say that I’ve read a lot about this, hours. I know that there are many subjects about this even here, but I can’t really understand what is the correct way to use a single instance of a PDO connection and keep the possibility of testing the code in the same time - and easy to mantain.

How do you guys use only one instance of a DB connection?

class DB
{


	public function __construct(){


		try{

			//CONNECT TO DB THROUGH PDO


		}catch(PDOException $e){

			die($e->getMessage());

		}
	}


	public function query(){}
	public function get(){}
	public function delete(){}

}

class Articles


{
	private $_db;

	public function __construct(DB $database){

		$this->_db = $database;

	}
	public function getArticles(){

		//get articles from DB using a DB connection	
	}

}

Your code example is perfectly fine as it uses dependency injection. A simplest way of using only one instance is simply instantiate one instance and pass it to objects that require it.

If you want to go further then on top of what you presented write a factory that you will use to make an instance of the db class. This factory should be made to return only one instance, for example:

class Factory {
  private $db;

  public function getDB() {
    if (!$this->db) {
      $this->db = new DB;
    }

    return $this->db;
  }
}

Just remember not to overuse the factory, which means do not pass the factory around to all your classes, the majority of classes should simply require the database object without being concerned with factories. In most cases the factory is used in the top layer of your application that instantiates most needed objects.

A more elaborate form of a factory is dependency injection container - more complicated but automates object creation for you - you can use that as well analogously.

2 Likes

Something like this?

class db{

	public $db = 'database';
	public function __construct(){

		return $this->db;
	}
}
require_once('db.php');

class DI
{

	private $_services = [];



	public function __construct(){


		$this->register('database', function(){
			$obj = new DB;
			return $obj;
			
		});


	}

	public function register($name, Closure $callback){

		if(! array_key_exists($name, $this->_services)){

			$this->_services[$name] = $callback;
		}



	}


	public function getService($name){

		if(! array_key_exists($name, $this->_services)){

			throw new Exception('No service');
		}

		return $this->_services[$name]();
	}


}

require_once('di.php');

$test = new DI;
$test = $test->getService('database');
var_dump($test->db); //string(8) "database"

And one more question if you don’t mind.
For “simple” DI in constructor(no container or something like that), wouldn’t be a problem if I have too many dependencies? Let’s say I have a class where I need to use sessions, cookies and a database connection. Wouldn’t that be too much?

Yes, except that your db class doesn’t make much sense now but if it is meant as a simplified example of a class then that’s OK.

No, but there’s a point when it becomes too much, however it’s not set in stone. Usually, too many dependencies mean the class has too many responsibilities and should be simplified or split into several classes.

Most often I find too many dependencies in controller classes when I use the popular idea of grouping several actions (methods) of one entity type into a single controller, like having a User controller which has actions like addUser, editUser, listUsers, deleteUser. With time more can come like registerUser, banUser, activateUser, notifyUser and so on. Then it becomes apparent that each of these actions may require a different set of dependencies so with this kind of code organization I must supply all of them through a single constructor. The other thing is that the class has too many responsibilities as each action really is a different responsibility as it forms a procedure that is independent from all the others - the single responsibility principle has been violated.

1 Like

This topic was automatically closed 91 days after the last reply. New replies are no longer allowed.