Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

PHP - multiple different databases dependency injected class

I've spent the last several hours trying to find an answer to the "best", most logical, etc way to write a php database class to simultaneously connect to one postgresql db and one mysql db. Also, I'd like to adopt a Dependency Injection design but am new to that whole concept.

So far I've come up with...

class Database {

    public function PgSqlConnect() {
            /* Connect to database */
        $host = 'localhost';
        $dbname = '---';
        $user = '---';
        $pass = '---';
        $timeout = 5;   /* seconds */

        try {
            $pgsql_dbh = new PDO("pgsql:host=$host; dbname=$dbname", $user, $pass); 
            $pgsql_dbh->setAttribute( PDO::ATTR_TIMEOUT, $timeout ); 
            $pgsql_dbh->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION );

            return $pgsql_dbh;
        } catch( PDOException $e ) {
            echo 'Unable to connect to database: ' . $e->getMessage();
        }
    }


    public function MySqlConnect() {
            /* Connect to database */
        $host = 'localhost';
        $dbname = '---';
        $user = '---';
        $pass = '---';
        $timeout = 5;   /* seconds */

        try {
            $mysql_dbh = new PDO("mysql:host=$host; dbname=$dbname", $user, $pass); 
            $mysql_dbh->setAttribute( PDO::ATTR_TIMEOUT, $timeout ); 
            $mysql_dbh->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION );

            return $mysql_dbh;
        } catch( PDOException $e ) {
            echo 'Unable to connect to database: ' . $e->getMessage();
        }
    }

}

Obviously the duplicated code violates the DRY approach. I know and have seen many examples of multiple db connections, but most deal with same driver and don't provide DI capability.

I should also add that I've considered placing the connection details into the Database class constructor as...

$driver = 'mysql';
...
$mysqldb = new Database($driver,$un,$pw,...);

$driver = 'pgsql';
...
$pgsqldb = new Database($driver,$un,$pw,...);

but I don't know if that is really a good idea nor how well it would work with DI.

Many thanks!

like image 437
Isius Avatar asked Nov 02 '11 02:11

Isius


2 Answers

You should create an interface first for all the DB operations.

interface IDatabase
{
    function connect();
    function query();
    ...
}

Then have different driver classes implementing this interface

class MySQLDB implements IDatabase
{
}
class PGSQLDB implements IDatabase
{
}

This way you can easily use dependency injection.

class Test
{
   private $db;

   function __construct(IDatabase $db)
   {
        $this->db = $db;
   }
}

You can call it as:

$mysqldb = new MySQLDB();
$test = new Test($mysqldb);
or
$pgsqldb = new PGSQLDB();
$test = new Test($pgsqldb);
like image 170
Vikk Avatar answered Nov 04 '22 09:11

Vikk


To avoid duplicated code you can extend an abstract class

abstract class AbstractDb {
    public function connect() {
        // common code to avoid duplication
        echo 'connected!';
    }

    abstract public function escapeField();
    abstract public function escapeValue();
}

class MySQL extends AbstractDb {
    public function escapeField() {
        // Db-specific method
    }

    public function escapeValue() {
        // Db-specific method
    }
}

$db = new MySQL;
$db->connect();

Or use composition, and let the Db class use a different driver for db-specific methods

interface IDriver {
    public function escapeField();
    public function escapeValue();
}

class MySQLDriver implements IDriver {
    public function escapeField() {
        // Db-specific method
    }

    public function escapeValue() {
        // Db-specific method
    }
}

class Db {
    public function __construct($driver) {
        $this->driver = $driver;
    }

    public function connect() {
        // common code here ? idk, it's just an example
        echo 'connect!';
    }

    // this method is db-specific, so we call the driver
    public function escapeField($field) {
        return $this->driver->escapeField($field);
    }

    public function escapeValue() {
        // same here
    }
}

$db = new Db(new MySQLDriver);
$db->connect();

In php 5.4 there will be traits, so there will be more approaches to avoid code duplication.

like image 45
HappyDeveloper Avatar answered Nov 04 '22 09:11

HappyDeveloper