OOP – Should Database Connection Be Public for Other Classes?

databaseobject-oriented

I have a basic Database class, and it has a mysqli property (an object itself) which contains the connection information. Other classes uses this class to read and manipulate the database.

There are built-in functions in any database connection object (in this case, PHP's mysqli::multi_query, mysqli::next_result etc.), that I need to use in other classes.

The problem is that I really do not want to set the connection property as public since I believe it is a bad practice to do so in any property, especially not one with connection information.

So what should I do? Should I set it to public anyway, or should I create manually a function for each built-in function I need to use?

Best Answer

The whole point of wrapping a core PHP database driver is to provide your custom interface for handling database requests. Making the mysqli property public completely defies the purpose and you wouldn't really need your custom class in the first place.

The best approach would be to create a DatabaseInterface, have class or classes implement it, classes, which, preferably in their constructor, take a core PHP database driver (be it mysqli or PDO) and use the DatabaseInterface as parameters for classes which require database interaction.

This way you provide your own abstraction, over which you have full control, and are also not limited to single implementation.


More details and implementation example

In PHP you have generally two recommended options for database connection.

  • using PDO
  • using mysqli

The problem is PDO and mysqli provide different interface to querying, creating transactions, so if you used for example mysqli in your application directly and then wanted to switch to PDO, you would have to refactor every single database call in your app.

Use interfaces for abstraction

You create an interface, which will represent your very own interface for database calls. Here is a very simple example how some of its methods may look.

interface MyDatabase
{
    public function beginTransaction();
    public function commitTransaction();
    public function doQuery($sql);
}

Right now you want to use mysqli, so you create a new class, MyMySQLiDriver, which will implement the MyDatabase interface and either take a mysqli object in its constructor as a parameter, or you can use the procedural-programming mysqli_* functions.

class MyMySQLiDriver implements MyDatabase
{
    protected $mysqli;

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

    public function beginTransaction() { /* use $this->mysqli here */ }
    public function commitTransaction() { /* use $this->mysqli here */ }
    public function doQuery($sql) { /* use $this->mysqli here */ }
}

Anywhere else in your code, if you want to use a database, you are going to pass an object of MyDatabase (the interface) type as a parameter and use its methods.

function IAmUsingTheDatabase(MyDatabase $db)
{
    $db->beginTransaction();
    $db->doQuery("SELECT * ...");
    $db->commitTransaction();
}

Note that the IAmUsingTheDatabase has no idea whether you are using PDO, mysqli or anything else. It wants to use the database and it uses the interface you provided.

As I have said in the comments under this post, if, in the future, you don't want to use the MyMySQLiDriver anymore, you create a new class, be it MyPDODriver or anything else, which will again implement MyDatabase interface.


Where to go from here

You can study more on this topic under Inversion of Control (this is directly tied to Dependency injection and benefits greatly from the Strategy pattern).

Related Topic