1

I develop web applications with PHP. I have many classes using a database connection.

This is the structure i generally use;

class SomeClass {
    private $db;

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

    function SomeFunction() {
        $this->db->query(...);
    }
}

And this is what i thought to use;

class Common {
    public static DB = new mysqli(...);
}

class SomeClass {
    function SomeFunction() {
        Common::DB->query(...);
    }
}

There are two parts of my question;

  1. Which one is better practice? If i use first one, i have to pass database object to every class uses db. If i use second one, every class uses db will be dependent to Common class.

  2. I made examples simpler. Actually i have a Database class extends mysqli and a DatabaseTableManager class using Database class. I add GetTable() function to Database class. I create DatabaseTableManager objects in this function (return new DatabaseTableManager($this, $tableNameFromArgument)) so i do not have to pass db object every time. In DatabaseTableManager class, i built some queries to make my job easier.

E.g. to insert a record to a table;

$DB = new Database(...);
$myTable = $DB->GetTable('myActualTableNameInDatabase');
$myTable->Insert( array('col1' => 'val1', 'col2' => 'val2') );

So i use this class a lot in my other classes. Naturally all these classes become dependent to DatabaseTableManager class. What is the better solution?

previous_developer
  • 10,579
  • 6
  • 41
  • 66

3 Answers3

2

What is the better solution?

What is the problem? :) So first of all from the standpoint of a general design it's said that we benefit from not introducing global static state. This disqualifies the singleton and using global variables.

This reduces the burden to make a choice a lot. Passing the needed Mysqli connection object into the class that needs it via it's constructor is fine. If you're concerned about "passing it all the time", define a place where you create these objects.

You've already done that in fact, for the "table manager" (maybe manager is not such a good term (it does not manage the tables in the database, it just represents them), but let's focus on the structure first). That class will give birth to objects that represent a table.

For that part of your application that is consuming these, there is no need to even know about which kind of database lies behind, because you've encapsulated that detail.

Naturally the table manager itself still needs to have that knowledge. However this is no problem, because you could change that in a central place if you would need to change that.

So actually I must say, even you're keen to look for problems, what you create actually looks pretty well.

Sure, as always you can run into problems, however if the way you make use of the database in your application is generally table based, this does not look that wrong to me. The pattern you've implemented might go into the direction of Active Record. It is known that this is suitable for small to medium sized applications that have Transaction Scripts.

hakre
  • 193,403
  • 52
  • 435
  • 836
0
  1. It depends. Do you want to carry over the state of the row pointer? If so, go ahead and create the Common class. From a design perspective, your code that's depending on a database is forced to depend on some sort of abstraction, so go with whatever's most flexible to you. I would personally just have a DB object and pass it around. The single class may have advantages. If you could write a more fleshed-out example, we may be able to provide better answers.

  2. Why not put the methods inside your Database class instead of needlessly creating two classes and/or adding inheritance? Passing objects or variables to functions is normal behavior unless something's keeping track of state. Could you provide a more complete example of your Database and DatabaseTableManager classes?

0

I would leave it the way you have it now:

  • Your Someclass is not dependent on any other class.
  • gets the DB injected at construction. This is optimal for testing.
  • it is a separation of concerns.
  • this construct is the most easy to convert to a real dependency injector, in combination with a Inversion of Control container.
JvdBerg
  • 21,777
  • 8
  • 38
  • 55