views:

66

answers:

2

How is it done?

I have a Model class that is the parent to many sub-classes, and that Model depends on a database connection and a caching mechanism.

Now, this is where it starts getting troublesome: I have no control over how each object gets instantiated or used, but I have control over methods that get used by the sub-classes.

Currently I have resorted to using static methods and properties for dependency injection, as such:

class Model
{
    private static $database_adapter;
    private static $cache_adapter;
    public static function setDatabaseAdapter(IDatabaseAdapter $databaseAdapter)
    {
        self::$databaseAdapter = $databaseAdapter;
    }
    public static function setCacheAdapter(ICacheAdapter $cacheAdapter)
    {
        self::$cacheAdapter = $cacheAdapter;
    }
}

Which has worked out well, but it feels dirty (it creates a global state for all Models).

I have considered the factory pattern, but that removes the control of the instantiation from the sub-classes (how do I instantiate an object with a variable number of parameters in it's constructor?).

Now I am at a loss. Any help would be appreciated.

+1  A: 

As far as I know this is a perfectly acceptable alternative. Another possibility suggested by Sebastian Bergmann, the creator of PHPUnit, is to have a $testing static property. You can read his recent article regarding the Testing of Singletons. It sounds like you have similar issues.

Mike B
A: 

You're solution would be fine for setting default adapters, but I'd add a way for the individual models to have a different adapter. Consider this:

abstract class Model {
    protected $_database_adapter;
    protected $_default_database_adapter;

    public function getDatabaseAdapter() {
        if(!$this->_database_adapter) {
            if(self::$_default_database_adapter) {
                $this->_database_adapter = self::$_default_database_adapter;
            } else {
                throw new Exception("No adapter set yet");
            }
        }
        return $this->_database_adapter;
    }

    public function setDatabaseAdapter(IDatabaseAdapter $databaseAdapter) {
        $this->_database_adapter = $databaseAdapter;
    }

    public static function setDefaultDatabaseAdapter(IDatabaseAdapter $databaseAdapter) {
        self::$_default_database_adapter = $databaseAdapter;
    }
}

Of course you could extract all static methods/properties into a Registry, Container or anything else as central.

For example, perhaps you don't want to collect data from the same database host over your whole application. Then your original script would look like the following:

Model::setDatabaseAdapter($default);
$my_model->query('....');
Model::setDatabaseAdapter($another_adapter);
$my_other_model->query('....');
Model::setDatabaseAdapter($default);

which is awfully alike:

mysql_select_db('default_db');
mysql_query('...');
mysql_select_db('other_db');
mysql_query('...');
mysql_select_db('default_db');
chelmertz