0

I am switching my MVC to use PDO (I know, overdue). My application has, in the past, used the following class hierarchy:

Database.class>Main.class>User.class

(each one extending the other). But before any object is created, the mysql connection was made (mysql_connect). Once the connection was open I could use Database.class as a wrapper class through which all my queries were performed. Through extention, a query in the User.class could be made simply by calling the "query" function ($this->query).

Using PDO, I've tried to imitate the process but find errors. I created a singleton function in the Database.class:

function __construct()
{
    $this->db = new PDO('mysql:host='.DB_HOST.';dbname='.DB_NAME.';charset=utf8', DB_USER, DB_PASSWORD);
    $this->db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
    $this->db->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
}

public static function getInstance()
{
    if (!isset(self::$instance)){
        $object = __CLASS__;
        self::$instance = new $object;
    }
    return self::$instance;
}

function query($qry,$params=NULL){
    $qry = $this->db->prepare('SELECT * FROM users WHERE userID = :userID');
    $qry->execute(array(':userID' => 1));
    $results = $qry->fetchAll(PDO::FETCH_ASSOC);
    return $results;
}

Then in Main.class I get the instance:

function __construct()
{
    $this->db = parent::getInstance();
}

So in User.class I try to call

function __construct(){
    parent::__construct();
}
function test(){
    return $this->db->query("test");
}

So I can run any queries fine from the Main.class object. But if I try to run queries from User.class object I get the error: "Call to a member function query() on a non-object" In other words, if User.class extends main I should be able to access the variable $db in Main from User.class (I call the constructor for Main when the User object is created). Part of the issue is that Main.class is created earlier in the application as it's own object, I believe causing two instances of PDO to be created - which is why it doesn't work through extension (through a second object that also extends the database.class)

So my question is: is there a way to make this happen? Or is my best option to use injection for every object I create (because some scripts incorporate multiple objects that extend Main.class - which try to create an instance of PDO each time) and pass the pdo object to the constructor? I'd rather not have to do that (the less markup the better) So another option would be to use a STATIC variable that all classes use? What's the best method? (let me know if this is confusing)

I've seen people using injection for this, and I've seen examples of extending the pdo wrapper class (but only once).

Thanks! (I love stack overflow!)

Joao
  • 2,356
  • 2
  • 23
  • 35
  • This post might perhaps be of help: http://stackoverflow.com/questions/11514143/php-pdo-mysql-how-to-use-database-connection-across-different-classes?rq=1 Also: http://stackoverflow.com/questions/14062764/accessing-a-pdo-instance-from-another-class?rq=1 – Whistletoe Aug 07 '13 at 00:08

1 Answers1

1

You dont want any of these to extend the database class because that will essentially make them all singletons of which you can only have one instance... you Want to make them USE the database class instead. So you would put you most abstract db methods on the Database and then methods that create queries for specific things would be on the User or what have you. This means your Database actually wraps PDO and is what all other classes work with for db operations. The Main or Base class may not even be needed unless you are trying to implement active record or something.

class Database {
   static protected $instance;

   /**
    * @var PDO
    */
   protected $connection;

   protected function __construct($dsn, $user, $pass, $attrs = array()) {
       // create pdo instance and assign to $this->pdo
   }

   public static function getInstance() {
       if(!self::$instance) {
           // get the arguments to the constructor from configuration somewhere
           self::$instance = new self($dsn, $user, $pass);
       }

       return self::$instance;
   }

   // proxy calls to non-existant methods on this class to PDO instance
   public function __call($method, $args) {
       $callable = array($this->pdo, $method);
       if(is_callable($callable)) {
           return call_user_func_array($callable, $args);
       }
   }
}

class Main {
   protected $db;

   public function __construct() {
      $this->db = Database::getInstance();
   }   
}

    class User extends Main{

       public function __construct() {
            parent::__construct();
       }

       public function findById($id) {
           $qry = $this->db->prepare('SELECT * FROM users WHERE userID = :userID');
           $qry->execute(array(':userID' => $id));
           $results = $qry->fetchAll(PDO::FETCH_ASSOC);

           return $results
       }
    }
prodigitalson
  • 58,127
  • 8
  • 92
  • 110
  • Thanks for the input. The Main class also has several other functions which are used across the whole application. Most of my classes extend Main. So basically a class that extends another class that has a singleton method becomes a singleton object as well? Are you recommending I use injection? – Joao Aug 07 '13 at 00:24
  • I always recommend injection over a singleton :-) If you have a singleton then the entire hierarchy is going to have a single "instance" thats how a singleton typically works. You cant have a different constructor signature across the classes... – prodigitalson Aug 07 '13 at 00:25