6

Suppose that there are two classes that define objects of vastly different function such that in the datastore, the information they require is divided into two separate databases.

For example, the classes Employee and SteelExtruder may be placed in the databases `human_resources` and `capital` respectively. Rather than hard code the connections to their respective databases:

class Employee {
    protected $dbc;
    public function __construct() {
        $this->dbc = new mysqli('localhost', 'user', 'pass', 'human_resources');
    }
}

I can see the advantages in flexibility that come from injecting the database connection into the constructor:

class Employee {
    protected $dbc;
    public function __construct(mysqli $dbc) {
        $this->dbc = $dbc;
    }
}

However, I fail to see how to implement it properly. I have read that singletons and database class libraries are bad practice, since they merely mask the fact that they are really just global variables. On the other hand, the alternatives do not seem fantastic either:

A) Declare any necessary connections globally in a loader file. Functionally identical to a database library class, but smells worse:

loader.php:
$dbc_human_resources = new mysqli('localhost', 'user', 'pass', 'human_resources');
$dbc_capital = new mysqli('localhost', 'user', 'pass', 'capital');

index.php:
$employees = array(
    new Employee($dbc_human_resources),
    new Employee($dbc_human_resources),
    ...
    );

B) Create a new connection every time a new object is created. Unless there is some sort of optimization I am not aware of, this seems carelessly inefficient.

$employees = array(
    new Employee(new mysqli('localhost', 'user', 'pass', 'human_resources')),
    new Employee(new mysqli('localhost', 'user', 'pass', 'human_resources')),
    ...
);

C) Store all the objects into a library object that takes a single connection, which doesn't look so bad but feels wrong:

Employee.php:
class Employee {
    //non-database properties
    public function __construct($args = array()) {
        //non-database initialization
    }
}

EmployeeLibrary.php:
class EmployeeLibrary {
    protected $dbc, $employees;
    public function __construct(mysqli $dbc) {
        $this->dbc = $dbc;
    }
    public function add_new_employee($args) {
        array_push($this->employees, new Employee($args));
    }
    //do things with database connection
}

loader.php:
$employee_library = new EmployeeLibrary(new mysqli('localhost', 'user', 'pass', 'human_resources');

index.php:
$employee_library->add_new_employee(...);
$employee_library->add_new_employee(...);

I am sure this isn't a rare problem. Is there an accepted way to do this?

1

1 Answer 1

5

All three are wrong, because you are storing connection strings in source code. Source code is not the right place for configuration, because you are not expected to have to change your code (and so, do all the regression testing) every time your database moves or every time you move from development database to staging and to production database.

Instead:

  • Store the connection string in a configuration file.

  • Create a class which accesses configuration file options.

    The class should provide the callers with the simple interface to those options; callers shouldn't care where the configuration file is or how options are stored inside.

    This abstraction makes it possible later to switch from, for instance, a configuration stored in a JSON file, to a configuration stored in an XML file, or even move configuration to a database such as Redis.

  • Create an instance of this class every time you need to access the configuration options.

    You don't need a singleton here; if one day, performance becomes an issue (that is, the time needed to open and parse the configuration file becomes prohibitive), use caching.

    If you are absolutely sure (beware of YAGNI) that you'll have to move to a different storage mechanism soon (for example from JSON file to Redis), you may use Dependency Injection by creating the configuration object at the top of the stack, and then pass it to the callers. This will ensure that when moving to a different mechanism, you'll change no more than one line in your code base, replacing:

    $configuration = new JsonFileAppConfiguration();
    

    by:

    $configuration = new RedisAppConfiguration();
    

    but everything else will rely only on the interface IAppConfiguration that both those classes implement.

4
  • Fair point. However, wouldn't I still be faced with spawning a lot of DB connections? It's this, not the parsing of the configuration file, that I'm concerned will be prohibitive. Perhaps I am unclear on the exact responsibilities of the configuration parsers.
    – concat
    Jun 21, 2015 at 17:22
  • @concat: with database connections pooling, you don't have to reuse a connection. Open it when you need it; do your job; close the connection as soon as you don't need it any longer. Reading the manual, it seems that mysqli in PHP supports pooling, so I wouldn't worry about that. You may start to worry only if and when the profiling will tell you that this part becomes an actual bottleneck. Jun 21, 2015 at 17:28
  • True! "Every PHP process is using its own mysqli connection pool."
    – concat
    Jun 21, 2015 at 17:33
  • I'm running into mysqli::mysqli(): (08004/1040): Too many connections when creating more than max_connections number of objects, even when using persistent connections. I am unsure as to what exactly "Every PHP process is using its own mysqli connection pool." entails, but it doesn't seem to be what we expect.
    – concat
    Jun 28, 2015 at 18:21

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.