Reputation: 2965
I've encountered an architectural issue with my application. I've rolled my own (very basic) MVC, and one of my models is a database object: class MySQLDatabase { }
There's a number of places in which I'd want to use my database object, without creating duplicate instances. Inside my controller, I have declared public $db;
and within the __construct { }
I have $this->db = new MySQLDatabase;
Question:
How do I use $db
within my other classes--they're all instantiated within the controller's __construct { }
as well... would I declare global $db
at the top of all my classes that require database connectivity?
I'm used to global variables being declared in the global scope as regular variables, and then using the global
keyword to reference the global scope... I'm not sure if that applies to variables declared within a class (my controller.)
Upvotes: 3
Views: 3057
Reputation: 858
Don't use singletons. It's much better to explicitly pass around data. For example:
abstract class Controller {
private static $conn; // could be an array for multiple connections
final protected function getDBConnection() {
if (!$this->conn) {
$this->conn = new DBConnection();
}
return $this->conn;
}
abstract public function process(Request $r);
}
class HomePageController extends Controller {
public function process(Request $r) {
$results = $this->getDBConnection()->query('SELECT stuff FROM foo;');
// do stuff with $results
}
}
You could also have an explicit model object you pass around, e.g. the one that represents the user, but that may be overkill for your project.
Upvotes: 1
Reputation: 402
I think what you need here is a singleton for you Database object :)
See here for more details : http://en.wikipedia.org/wiki/Singleton_pattern
Edit with sample singleton for php :
<?php
class UniqueObject {
private $_uniq = null;
//private cause you don't want to instanciate the classic way
private function __construct() {
//...
}
//check if unique object exists or not, then return it
public static function uniq() {
if(!self::$_uniq)
self::$_uniq = new UniqueObject();
return self::$_uniq;
}
}
//call your unique object whenever you need it
UniqueObject::uniq();
?>
(it's late, i hope i didn't do any mistake :))
Upvotes: 1
Reputation: 8334
I would stay away from using globals or the Singleton pattern (which is essentially a global anyway), and try and find some alternatives. Additionally you are talking about a database connection, by using the Singleton pattern you are saying that there will never be more than one database connection, whilst that is generally true in smaller applications, as they grow larger you won't be able to accomodate multiple connections.
Once you make something global then you lose the automatic contraints of where it can be used/modified. Using MVC a view shouldn't be used for anything other than to display data, by using a global/singleton it is up to the developer to not make use of the globals. Whereas with a different design they don't have that option.
You mentioned you've created your own MVC framework, so I imagine the classes you want to use it in are your models? Correct me if they are anywhere else.
If your models extend from a common base class then you could pass your database object to that class as a static variable which can be assigned to any new instances in the construct or using a factory method in the factory method.
This isn't to say that globals or singletons should be avoided at all costs, but definitely try consider the alternatives that could lead to a neater design.
Here's some reading on the Singleton pattern if you're interested:
There are many more out there...
Upvotes: 4
Reputation: 57268
I Think you going about this the wrong way, you should not be performaing quesries to the database from you controller.
this means that the below is invalid.
class ControllerIndex extends Controller
{
public function index()
{
$this->db->selectAll("table");
}
}
There should be a layer that separates your controller from your database interface, this is where a Model comes in.
You should have a models folder that contain classes for actions taken such as users,posts,logging etc.
class Users_Model extends Model
{
public function getUser($id)
{
}
}
The model class should be part of your system core, and should extend your Database Class, this way within your main controller you should be loading the models via the ModelLoader class.
for example:
class ModelLoader
{
private $models = array();
public function __get($model)
{
//load (/application/models/?.php) and initiate it here
//Storing it in models array above
}
}
Then in your main controller:
class Controller
{
private $model;
public function __construct()
{
$this->model = new ModelLoader;
}
}
this way your bringing your loader into scope for the child controller:
class Controller_index extends Controller
{
public function index()
{
$user = $this->model->users->getUser(22);
}
}
Hope this helps!
Upvotes: 2
Reputation: 742
If I understand correctly you have a single controller that instantiates the database object and it also takes care of instantiating other classes. If so, you could implement some form of dependency injection either passing the db object in the constructor of the other classes or creating a setter method.
A good blog article on the subject:
http://www.potstuck.com/2009/01/08/php-dependency-injection/
Upvotes: 3
Reputation: 29
You'll need to use a singleton pattern. They give examples in the php docs
<?php
class Example
{
// Hold an instance of the class
private static $instance;
// A private constructor; prevents direct creation of object
private function __construct()
{
echo 'I am constructed';
}
// The singleton method
public static function singleton()
{
if (!isset(self::$instance)) {
$c = __CLASS__;
self::$instance = new $c;
}
return self::$instance;
}
// Example method
public function bark()
{
echo 'Woof!';
}
// Prevent users to clone the instance
public function __clone()
{
trigger_error('Clone is not allowed.', E_USER_ERROR);
}
}
?>
http://php.net/manual/en/language.oop5.patterns.php
Upvotes: 0