A class which represents an SQL table

Posted on

Problem

I’m representing DB tables as classes (in this case PHP classes). This has the goal of modeling every section of the web page, where you will get inputs from the user.

My actual base design is:

  • One class per table and every row is represented by an Object of this class.

  • The constructors asks for what they need in order to get the table row which they will represent:

    info = $this->getRowFromVideos($id);
    }
    }

The way I access the DB is not important here.

Every field of the table is stored as an array in an $info variable. And I have a method to access for the whole row or just a field.

    function getInfo($key = false){
        if($key) return $this->info[$key];
        else return $this->info;
    }

For composed tables like Players who needs an User and a Game ID, the query would be like this:

SELECT * FROM players WHERE userID = 'xxxxxx' AND gameID = 'SC2'

And the constructor class asks for an User object and Game object:

class Player extends DB {
    private $info = array();
    private $user;
    private $game;

    public function __construct($user, $game){
        $this->user = $user;
        $this->game = $game;
        $this->info = 
              $this->getRowFromPlayers($user->getInfo("userID"),$game->getInfo("gameID")); 
    }
}

Of course, these classes are not designed to satisfy CRUD needs, like inserting a new user or deleting a video. Those responsibilities would be encapsulated in other classes.

I cannot see which problems may appear with this design in the future if the web page gets really big.

Solution

Take a look at
Object Relational Mapping,
especially
Object-relational impedance mismatch
on Wikipedia.

Basically there problems with this as soon as it gets a bit more
complicated and you need to query e.g. a lot of objects at once, or you
have joins, and so on.

Let’s take $this->getRowFromPlayers. I’m assuming, that that method
will fetch one row from the database. So you already have a
bottleneck here because you’ll have to run n separate queries for
each of your n IDs when you want to get like a list of objects by
their IDs, which is very inefficient. So you’ll have to construct
objects from the outside, mapping a result of multiple rows from the
database to newly constructed (or cached) objects.

Another problem often comes up in that your objects don’t exactly are
the table rows, they are just mapped onto objects, so testing by
identity is mostly meaningless unless you keep track of objects and
return the same object for the same row of a table. It gets complicated
very fast.

Obviously there are already solutions for this, so I suggest you take a
look at existing PHP libraries for this.

I’m representing DB tables as classes

No, you’re not. You are representing objects as classes – the same objects that you store as rows in your database tables. In other words, you are using classes as data structures. Consider this:

class Player extends DB {
    ...
}

This is wrong: a player is not a database (and a database is not a table). So I suggest to rename your DB class to, for example, Row or PODS (Plain Old Data Structure)

You would be representing DB tables as classes if you had a class like this (pseudocode):

class Table {
    private Column[] columns;
    private Index[] indexes;
    ...
    public addColumn(column);
    public drop();
    ...
}

When OOP becomes Oops…

We have this very powerful tool, called SQL, to access a database. It can do efficient and complex actions on a relational database. But what do you do? You throw all of this away and you create a set of classes with very limited capability. Why do you do this?

The answer eludes me. Do you find SQL too ugly, or too difficult to learn? Do you not like normal data, or do you feel a need to make everything into objects? I can only guess.

There are alternatives, even in OOP (Object Oriented Programming). Wouldn’t it be far more powerful to model SQL in classes? This has been done many times and can be found in most frameworks, here are just some examples.

They don’t limit what you can do. At their best they help you to write better SQL commands. In Laravel your query would look something like this:

$userID = $user->getInfo('userID');
$gameID = $game->getInfo('gameID');
$info   = DB::table('players')->where('userID','=',$userID)
                              ->where('gameID','=',$gameID)
                              ->get();

Now this may, on the surface, not look better than your example, but it is far more flexible. There are fewer limitation build into this code, see the documentation.

Your approach would fail miserably when you have to retrieve data from more than one table, which often happens in many projects. You would have to perform many look-ups where SQL, even in the OOP versions, can do it in one.

Personally I often still prefer the normal SQL strings:

$SQL = 'SELECT * FROM players WHERE userID = :userID AND gameID = :gameID';

but there are conditions in which the OOP approach, of writing a SQL command, has its benefits. In the past, when a SQL command depended on many conditions writing the SQL string could become a mesh. The OOP approach to SQL can really help there.

My advice would be to learn OOP by starting at the beginning, with simple examples. Get a feel for what it can, and cannot do. Read a book, try the examples, build something simple yourself. Be creative, but never use OOP for the shake of using OOP.

Leave a Reply

Your email address will not be published. Required fields are marked *