简体   繁体   中英

OO PHP - How to create a “generic” database object?

Continuing my journey into more advanced OO, I'm trying to come up with the best way of being able to create a class called database that can happily use the soon-to-be-deprecated mysql_ commands, the mysqli_ library, and perhaps further down the line, other flavours of database as well.

I'd like my calling code to look something like this, for simplicity:

$db = new database("MYSQL", $host,$user,$password,$databaseName); 
$db->query("SELECT myField FROM myTable");

while ($ROW = $db->fetch_assoc($QRY)) {
  echo $ROW['myField'] . "<br/>";
}

When instantiating the database object, thats the part where i'd hope to specify MYSQL, MYSQLI, MSSQL, or whatever I feel like adding later.

I appreciate I could achieve exactly what I want by implementing switch blocks in every method of database :

class database() {

  function __construct($type,$host,$user,$password,$databaseName) { 

    $this->dbType = $type; 

    switch ($type) {

      case "mysql":
        mysql_dao::connect($host,$user,$password,$databaseName);
        break;

      case "mysqli":
        mysqli_dao::connect($host,$user,$password,$databaseName);
        break;

      case "mssql":
        mssql_dao::connect($host,$user,$password,$databaseName);
        break;

      //other cases
    }

  }

  function query($SQL) {  
    switch ($this->dbType) { 
       case "mysql": mysql_dao::query($SQL); break;
       case "mysqli": mysqli_dao::query($SQL); break; 
       case "mssql": mssql_dao::query($SQL); break;
    }
  }

  function fetch_assoc($SQL) {  
    switch ($this->dbType) { 
       case "mysql": mysql_dao::fetch_assoc($SQL); break;
       case "mysqli": mysqli_dao::fetch_assoc($SQL); break; 
       case "mssql": mssql_dao::fetch_assoc($SQL); break;
    }
  }

  //other methods....

}

...But this seems incredibly messy. Every time a method of database was called, the code is checking what type it is and calling the method from a different object.

So my next solution was simply to omit database at all, and just use calling code that looks like this:

$db = new mysql_dao($host,$user,$password,$databaseName); 
$db->query("SELECT myField FROM myTable");

while ($ROW = $db->fetch_assoc($QRY)) {
  echo $ROW['myField'] . "<br/>";
}

...and so we just call the database access object for the type of database we're using, but I don't like that either, if I wanted to shift a whole project from mysql to mysqli, i'd have to seek out all of these object references and change them. Although this is fairly trivial in a modern PHP editor, it still doesn't feel like it should be the best way (i'm starting to get theoretical instead of practical, now!)

So finally, i'd hoped that I could build the database class with a single switch statement:

class database {

    function __construct($type,$host,$user,$pass,$db) {    

        switch ($type) {

            default:
            case "MYSQL":
                return new mysql_dao($host,$user,$pass,$db);
                break;

            case "MYSQLI":
                return new mysqli_dao($host,$user,$pass,$db);
                break;

            case "MSSQL":
                return new mssql_dao($host,$user,$pass,$db);
                break;    
        }    

    }

}

... and then just implement an interface with all of the other methods being in the classes for the individual database types. Unfortunately this isn't working either because even though the constructor for database returns an instance of another object, I can't call database->query because the method doesn't exist in the database object.

As far as I could work out, extending database with mysql_dao / mysqli_dao etc. classes wasn't right either, since you have to call the extended class to use its methods, and I want to call the parent class but use the methods of the child class. So this suggests that database should extend the dao classes, but you can't have one child class with multiple potential parents (can you?)

To conclude - I am almost sure I am missing something, and that what I want to do should be both possible and quite straightforward, and I just haven't thought of the right way - can anyone give me a pointer?

Despite the fact that what you're doing is reinventing the wheel (take a look at PDO), in general, for cases like this you might want to use something like the factory pattern .

In basic pseudo-code:

$db = Database::create('mysql', $host, ...); // gets a MySQLDatabase object
$db = Database::create('mysqli', $host, ...); // gets a MySQLiDatabase object

Why not just use PDO? It provides a unified way of interfacing with a range of databases (The actual queries it executes have to be written with the target database in mind, but the API is the same regardless of what kind of database you're connecting to).

In this kind of situation, I would generally :

  • Define some DAOInterface , with methods such as connect , query , fecthAll , ...
    • all class that connect to the database will have to implement that interface
    • which means that your mysql_dao , mysqli_dao , ... will implement that interface

Then, when working with those classes, the main advantage is :

  • You'll later instanciate, as a $db variable, either mysql_dao or mysqli_dao , but always get an object that implements DAOInterface
  • Which means you will be sure that you can invoke all those methods, on your $db object


After that, there will only be one place where you have to choose between mysql_dao and mysqli_dao : it's when you are instanciating one of those two, assigning the result to $db .

Then, as your know for sure that $db is an object that implements DAOInterface , you know for sure that you will always be able to call the same methods on that object.

In order to have the functionality to be functional you will need to pass and return parameter as references as in the following example :

 public function &Query($sql){
     $var = mysql_query($sql, $this->connection);
     return $var;
 }

 public function Fetch(&$cur){
     return mysql_fetch_array($cur);
 }

The "&" represent the reference of the variable not just a copy for more documentation :http://www.php.net/manual/en/language.references.pass.php

What you are trying to do works well. I created on to interface a MySQL database, but I'm sure you'll find a way to create an object for the different type of DB.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM