I do not know a lot about DI or DI containers and their applications or theories, but I am trying to integrate and use it because I have the need for a single instance (singleton) of certain classes (namely PDO) and understand it is best to avoid doing that and instead use DI.
I use DI elsewhere (without a container and through simple construction injection) but doing this with a class that should behave like a singleton is just extraordinarily tedious, and a singleton’s appeal is becoming hard to deny here because I am constantly passing the pseudo-singleton through various class for no reason other than to pass it to the actual class that needs it, which can be several levels down.
But I can’t seem to figure out how to integrate a DI library into an existing MVC app and also figure out how it actually has clear advantages.
The example I am having difficulty with is with a typical MVC app, where a controller calls a model, but the model doesn’t require the DB, but some of the classes the model calls do require the DB.
class DBPassThroughClass
{
protected $db;
public function __construct(PDO $db)
{
$this->db = $db;
}
public function doSomething()
{
// often there are many "pass through" classes before I actually get to a class that will require the DB
$AClassThatActuallyRequiresTheDB = new AClassThatActuallyRequiresTheDB($this->db);
}
}
class Controller
{
protected $db;
public function __construct(PDO $db)
{
$this->$db = $db;
}
public function doControllerLogic()
{
// do some controller stuff
// call the pass through class and pass in the DB
$dbPassThroughClass = new DBPassThroughClass($this->db);
}
}
With just normal DI I would be passing the DB connection on down from the controller into the model and into anything else it calls. This is pretty tedious, but I can understand reasons for doing it. So it seems like I am passing the DB through countless classes only to pass it again to the next class, whereas there may be a chain of classes (very often) that do not have anything to do with the DB.
(A separate concern is that the constructor is going to sort of explode with basic class arguments like needing to pass a database, flash messages, logger(s) and more around.)
I could instantiate the class right before I call any class that requires the DB, but there are thousands of instances of this, and they would all have to be changed anyway if I changed the DB’s options.
$db = new DB($db_options);
$objectThatRequiresDB = new ObjectThatRequiresDB($db);
It seems like it would be much easier to implement, and even simpler to change (which is the big point of DI) to just use a singleton to retrieve the DB instance in classes that require the DB.
class IActuallyNeedTheDB
{
protected $db;
public function __construct()
{
$this->db = PDOSingleton::getInstance();
}
}
I thought autowiring might help me here, but I can’t figure out how to make it work at all.
(http://php-di.org/doc/autowiring.html )
I’ve tried numerous DI packages (not just PHP-DI) that claim to have autowiring but I can’t really get any of their autowiring functionality to work (possibly because I am just misinterpreting it).
class DBPassThroughClass
{
protected $db;
public function __construct(PDO $db)
{
$this->db = $db;
}
public function doSomething()
{
// often there are many “pass through” classes before I actually get to a class that will require the DB
$AClassThatActuallyRequiresTheDB = new AClassThatActuallyRequiresTheDB($this->db);
}
}
$di_library = new DILibrary();
$db_pass_through_class = new DBPassThroughClass(); // DB is required and properly type hinted
But this just tells me I am missing the PDO argument for the DBPassThroughClass instead of the DILibrary auto-resolving the PDO dependency through reflection. (Do I need to register the DI library as an spl_autoloader?)
And even if it did work, unless I am placing the DI library in some pretty much super-parent global class or trait, it seems like I have to instantiate the DI library (or at least call it) every time which doesn’t seem much different from the example above:
$db = $diLibrary->get('PDO');
$objectThatRequiresDB = new ObjectThatRequiresDB($db);
Vs
$db = new DB($db_options);
$objectThatRequiresDB = new ObjectThatRequiresDB($db);
And integrating it would require making $diLibrary pretty much a global (because instantiating it anew would invalidate the DI library’s configurations and bindings), which I know is to be avoided.
Sorry if I am not getting my point across here, but in brief: I need to know how to integrate DI properly, how to integrate a DI library with autowiring properly, and the reasons (if any) why I should use DI here over a singleton in this instance.