2

I've done quite a bit of reading on this and a lot of people are saying I should be using a singleton class. I was thinking of writing a "Config" class which would include a "config.php" file on __construct and loop through the $config array values and place them into $this->values...

But then I read more and more about how singletons should never be used. So my question is - what is the best approach for this? I want to have a configuration file, for organization purposes, that contains all of my $config variables. I do not want to be hardcoding things such as database usernames and passwords directly into methods, for the purpose of flexibility and whatnot.

For example, I use the following code in an MVC project I am working on:

public/index.php

<?php

include '../app/bootstrap.php';

?>

app/bootstrap.php

<?php

session_start();

function __autoload ($class) {  
    $file = '../app/'.str_replace('_', '/', strtolower($class)).'.php';

    if (file_exists($file)) {
        include $file;
    }
    else {
        die($file.' not found');
    }
}

$router = new lib_router();

?>

app/lib/router.php

<?php

class lib_router {

    private $controller = 'controller_home'; // default controller
    private $method = 'index'; // default method
    private $params = array();

    public function __construct () {
        $this->getUrl()->route();
    }

    private function getUrl () {
        if (isset($_GET['url'])) {
            $url = trim($_GET['url'], '/');
            $url = filter_var($url, FILTER_SANITIZE_URL);
            $url = explode('/', $url);      

            $this->controller = isset($url[0]) ? 'controller_'.ucwords($url[0]) : $this->controller;
            $this->method = isset($url[1]) ? $url[1] : $this->method;

            unset($url[0], $url[1]);

            $this->params = array_values($url);
        }

        return $this;
    }

    public function route () {
        if (class_exists($this->controller)) {
            if (method_exists($this->controller, $this->method)) {
                call_user_func(array(new $this->controller, $this->method), $this->params);
            }
            else {
                die('Method '.$this->method.' does not exist');             
            }
        }
        else {          
            die('Class '.$this->controller.' does not exist');
        }
    }

}

?>

Now let's say I visit http://localhost/myproject/lead/test

It is going to call the controller_lead class and the test method within.

Here is the code for app/controller/lead

<?php

class controller_lead extends controller_base {

    public function test ($params) {        
        echo "lead test works!";
    }

}

?>

app/controller/base

<?php

class controller_base {

    private $db;
    private $model;

    public function __construct () {
        $this->connect()->getModel();
    }

    private function connect () {
        //$this->db = new PDO($config['db_type'] . ':host=' . $config['db_host'] . ';dbname=' . $config['db_name']. ', $config['db_user'], $config['db_pass'], $options);
        return $this;
    }

    private function getModel () {
        $model = str_replace('controller', 'model', get_class($this));
        $this->model = new $model($this->db);
    }

}

?>

This is where I run into the issue. As you can see, the connect method is going to try and create a new PDO object. Now how am I going to inject this $config variable, given all the other code I just provided?

My options appear to be:

  • Use a singleton (bad)
  • Use a global (worse)
  • Include config.php in bootstrap.php, and inject it throughout multiple classes (Why should I inject this into my lib_router class when lib_router has absolutely nothing to do with the database? This sounds like terrible practice.)

What other option do I have? I don't want to do any of those 3 things...

Any help would be greatly appreciated.

inkd
  • 1,421
  • 1
  • 13
  • 16
  • private $db; should injected via public function __construct ($db) in controller_base, then go and look for a better how the PDO is created (maybe lazyload , in bootstrap.php). – s.d.a.p.e Apr 16 '15 at 21:39
  • or try this to power up your MVC https://github.com/AnthonyWlodarski/Design-Pattern-Examples/blob/master/registry.php – s.d.a.p.e Apr 16 '15 at 21:45
  • That is a singleton. Thanks for your comments, but my original question still stands. Also, injecting $db into controller_base requires me to inject it into lib_router first, and, as I mentioned, lib_router has absolutely nothing to do with the database. Its sole objective is to route the user to the appropriate controller based on the URL they are requesting. – inkd Apr 16 '15 at 21:50
  • $c=Registry::get('config'); $this->db = newPDO($c[0],$c[1]); In every way you will mixup those 3 things... Questions are: How many time changes $config and when do you new Instances of PDO? – s.d.a.p.e Apr 16 '15 at 22:00

2 Answers2

0

I ended up including a config file in my bootstrap which simply contained constants, and used the constants.

inkd
  • 1,421
  • 1
  • 13
  • 16
0

I ended up including a config file in my bootstrap which simply contained constants, and used the constants.

That was the same case for me - required the file in Model. The file based approach was the best fit since it has some benefits: you can .gitignore, set custom permission set, all your parameters are stored centrally, etc.

However, If the config file contains DB connection parameters only, I prefered to require the config file in Model only. Maybe, you could also break down into multiple, more specific config files and require them where necessary.

public function __construct()
{
    if (self::$handle === FALSE)
    {
        $db = array();

        require APP_DIR.DIR_SEP.'system'.DIR_SEP.'config'.DIR_SEP.'Database.php';

        if (!empty($db))
        {
            $this->connect($db['db_host'], $db['db_user'], $db['db_password'], $db['db_name']);
        }
        else
        {
            Error::throw_error('Abimo Model : No database config found');
        }
    }
}
sitilge
  • 3,687
  • 4
  • 30
  • 56