0

I am using the Front Controller pattern found here https://www.sitepoint.com/front-controller-pattern-1/. Therefore all classes in my application are created in the run() function on my index.php page:

$frontController = new FrontController();
$frontController->run();

I am accessing my database via PDO with $dbh = new PDO('mysql:host='.$host.';dbname='.$database, $user, $pass);

My problem is I don't know how to pass the database to the objects created by the FrontController.

I have tried passing the database to the FrontController and then passing it to the class within the FrontController. Simplified example shown below.

$dbh = new PDO('mysql:host='.$host.';dbname='.$database, $user, $pass);

class FrontController {

    const DEFAULT_CONTROLLER = "IndexController";
    const DEFAULT_ACTION     = "index";
    
    protected $controller    = self::DEFAULT_CONTROLLER;
    protected $action        = self::DEFAULT_ACTION;
    protected $params        = array();
    protected $basePath      = "";
    protected $dbh;

    public function __construct($dbh, array $options = array()) {
        $this->dbh = $dbh;
        if (empty($options)) {
           $this->parseUri();
        }
        else {
            //set controller, action and params
        }
    }

    public function run() {
        call_user_func_array(array(new $this->controller($this->dbh), $this->action), $this->params);
    }
}

class User {
    
    public $dbh;

    function __construct($dbh){
        $this->dbh = $dbh;
    }

    function get_username_from_db(){
        $stmt = $this->dbh->prepare(...);
        //etc
}

index.php

$frontController = new FrontController($dbh);
$frontController->run();

The above code works, but it feels clunky. The FrontController is only used for routing the application and will never access the database itself. It therefore has no business knowing about the $dbh object, except that it will create other objects that do need to know about the $dbh class. It also means if the FrontController creates another object that doesn't need the $dbh class, that object will get the $dbh class anyway because the FrontController is passing out a copy of $dbh to everyone.

My question is: is there a cleaner way for me to pass the database to the objects created by the FrontController, so that only the objects that need it get it, and only when they need it? I'm trying to follow best practice/basic principles but I can't find any questions on this site or elsewhere that reflect this situation. For all I know this is the right way of doing it, but I suspect it's not.

monkeyman
  • 135
  • 1
  • 4
  • 14
  • 1
    Now it's time to introduce you to Dependency injection Containers – Your Common Sense Aug 06 '20 at 08:12
  • @YourCommonSense do you have an example or some reference material you can share? – monkeyman Aug 06 '20 at 08:29
  • Sadly, I don't have any at hand. Just quick google gave me [this](https://route.thephpleague.com/4.x/dependency-injection/) but it looks too complicated. The idea is: 1) Let your container know what classes require what services and 2) Let your container to instantiate your controllers. It will inject all services automatically. – Your Common Sense Aug 07 '20 at 12:33
  • [This answer](https://stackoverflow.com/q/51486449/285587) looks closer to your case – Your Common Sense Aug 07 '20 at 12:35
  • Thanks @YourCommonSense. I have been doing some Googling myself and came across [this](https://symfonycasts.com/screencast/oo-ep2/service-container#play) and [PHP-DI](https://php-di.org/doc/understanding-di.html) which have both been helpful and I've started to try build my own DI Container. – monkeyman Aug 08 '20 at 15:44
  • Looks promising. When you finish, it would be a good idea to write an answer to your question. – Your Common Sense Aug 08 '20 at 16:13

1 Answers1

-2

you can wrap database call inside a class like this.

class database {
   function connect(){
      $dbh = new PDO('mysql:host='.$host.';dbname='.$database, $user, $pass);
      return $dbh;
   }
}

then, if you only need database in function run, you can do this

class FrontController {
//.....
function run(){
    $dbh = (new database)->connect();
  //...
}
}

for user class you can either user as you have used or you can also use below code.

class User {
    
    public $dbh;

    function __construct(){
        $this->dbh = (new database)->connect();
    }

    function get_username_from_db(){
        $stmt = $this->dbh->prepare(...);
        //etc
}