tags:

views:

104

answers:

5

Hello guys,

Hope you can help me with this one: i have two classes: Database and Users. The Database connects to the database using PDO (inside the constructor) and has functions to alter tables, insert data, etc. The Users class will handle login, as well add/remove users. However, the Users class needs to connect to the database. How can i do this?

+1  A: 

Simply add a reference to the database class instance into Users:

class Users  {
  var $database;
  function __construct()  {
    $this->database = new Database();
  }
};

Alternatively, if Database is a singleton, just reference it directly.

dark_charlie
What is a singleton?
Vinny
A class that has only one instance in the whole program, it is usually stored in a global variable in PHP.
dark_charlie
You can't instantiate an object (or call a method) in a PHP property declaration.
bogeymin
@bogeymin: Thanks, fixed.
dark_charlie
Forgot the `$this` pointer.
konforce
@konforce: Sometimes I wonder what I'm doing, thanks.
dark_charlie
+1  A: 

Same way you normally would, but it might help to make the database a class property:

<?php
class Users
{

    protected $_db;

    public function __construct(Database $database = null)
    {
        if (!$database) {
            $database = new Database;
        }
        $this->_db = $database;
    }

    public function addUser($username)
    {
        // Do stuff ...
        $this->_db->insert($data);
    }

}

Then you can use the User class like:

<?php
$users = new Users;
$users->addUser('joebob');
Ryan Chouinard
Ryan, you stole my answer. Guess I need to work on my typing speed. :-)
bogeymin
@bogeymin - I usually have the same problem :-)
Ryan Chouinard
Wouldn't that be wrong? I mean... i'm connecting to the database everytime i need to execute a query or a select.
Vinny
@Vinny. Yes, this is a bad approach if you are creating multiple `Users` classes. The `_db` pointer should be static. Really it needs to be shared across the entire application. You don't want to connect to the database multiple times.
konforce
You're connecting every time you instantiate the Database class. In this example, you also connect when you instantiate Users. Or, you can re-use a connection, since the constructor will accept a Database instance: $users = new Users($database);
Ryan Chouinard
Using a singleton would fix this?
Vinny
I mean, transform my Database class into a singleton.
Vinny
@Vinny - Singletons have their own sets of problems, but that is another (questionably) valid approach. The approach I presented is, however, very flexible for most needs.
Ryan Chouinard
+3  A: 

There are several things you could do:

Globals

$db = new Database();

class Users
{
  public function foo()
  {
    global $db;
    $db->query();
  }
}

Setting a static variable

$db = new Database();

class Model
{
  static public $db;
}

Model::$db = $db;

class Users extends Model
{
  public function foo()
  {
    self::$db->query();
  }
}

Use a singleton

class Database
{
   private static $instance;

   private function __construct()
   {
   }

   public static function instance()
   {
      return self::$instance ? self::$instance : self::$instance = new self();
   }
}

class Users
{
   public function foo()
   {
      Database::instance()->query();
      // or $db = Database::instance(); $db->query();
   }
}

The one thing you want to avoid is creating a new database connection per model or class.

konforce
Or, dependency injection, which is passing the Database resource to the objects that need it, either on instantiation or through a setDatabase() method. All these have their own pros and cons.
Ryan Chouinard
A: 

One way to do so would be to create ONE shared instance of the database class, then use it as a global variable wherever needed.

Start by creating the instance anywhere in your project, just make sure it is in global space (not inside another class or function).

$DB = new Database();

Then to access the shared database object, just use the $GLOBALS built-in array:

class User {
  function __construct() {
    $DB = &$GLOBALS['DB'];
    // do something
    $DB->callSomeMethod();
  }
  ...
}

As pointed out by @Ryan, namespace collisions are possible using this strategy. The best middle path out would be to convert the Database class into a singleton. Then it would store its own instance (translation: ONE connection no matter what) which could be accessed via a Database::getInstance() method.

Jagtesh Chadha
Globals are rarely a good idea. They introduce potential for namespace collisions, and make it difficult to use discrete resources for different purposes.
Ryan Chouinard
Say there is a Posts class that needs to use a DB connection too. Without globals or refactoring the existing code, you'd have to create one DB connection each for Posts and Users. As the number of classes goes up, Globals starts to win :) It's a trade-off between being cautious and unoptimal use of resources.
Jagtesh Chadha
I transformed my Database class into a singleton, unfortunately, when i try to access a method from the Database class, inside the Users class, i get: Call to a member function query() on a non-object
Vinny
A: 

This is my Users class:

class Users {

    private $data;
    private $db;

    public function __construct() {
        $db = Database::getInstance('localhost', 'database', 'root', '123456');
    }

    public function __get($key) {
        return $this->data[$key];
    }

    public function __set($key, $value) {
        $this->data[$key] = $value;
    }

    public function test() {

        foreach($this->db->query('table', '*', '', '', '', '', '0,5') as $value) {
            $results .= $value['field1'] . "<br />";
        }

        return $results;
    }
}
Vinny