Chapter 11. DBC classes

Table of Contents

Basics
Usage of DBC classes
Conventions for DBC classes

Basics

Common problem with the PHP applications (at least old-school style) is that they have SQL statements scattered all over the place. This makes modifications and maintenance significantly harder.

TF approach is to keep all DB operations centralized and that is achieved by so called "DBC class". You may think of them as representation as PHP class of every DB table in your database. All operations on given DB table are encapsulated in respective DBC class. Let's see simple example to illustrate the concept.

Suppose that you have DB table called users with following fields:

  • ID - unsigned integer value
  • username - string
  • password - string

Each DB table is represented by two classes:

  • model - PHP class that represents the structure, i.e. fields, and contains methods for getting and setting (with validation checks) field values. Files with those classes are named like users.class.php and the class inside is named Users. Model does not contain any DB related stuff.
  • DBC class - inherites the model and adds DB operations like save(), update() delete(), etc. All DBC classes implement interface I_DB_Storable.

First we will create model class that represents the structure.

<?php

class User extends Tangra_Class {
    private 
$id;
    private 
$username;
    private 
$password;


    public function 
set_id($id) {
        
tangra_if_not_int_throw_e($id);

        
$this->id $id;
    }


    public function 
get_id() {
        return 
$this->id;
    }


    public function 
set_username($username) {
        
$this->username $username;
    }


    public function 
get_username() {
        return 
$this->username;
    }


    public function 
set_password($password) {
        
$this->password $password;
    }


    public function 
get_password() {
        return 
$this->password;
    }
}

DBC classes have to deal with saving and loading data into/from DB. Here is its source:

<?php


require_once(TANGRA_MAIN_DIR.'interfaces/i_db_storable.class.php'); // loading interface I_DB_Storable


require_once('user.class.php'); // loading model


class User_DBC extends User implements I_DB_Storable {

    public function 
save(DB_Connection $dbc) {
        
$ret false;

        if (
$this->get_id()) {
            
$ret $this->update($dbc);
        } else {
            
$ret $this->insert($dbc);
            
$this->set_id($ret);
        }

        return 
$ret;
    }


    public function 
load_by_id(DB_Connection $dbc$id) {
        
tangra_if_not_int_throw_e($id);


        
$ret false;

        
$sql "select id, username, password from users where id = $id";
        
$rez $dbc->execute($sql);

        if (!
$rez->is_eof()) {
            
$rez_obj $rez->fetch_object();

            
$this->set_id($id);
            
$this->set_username(stripslashes($rez_obj->USERNAME));
            
$this->set_password(stripslashes($rez_obj->PASSWORD));

            
$ret $id;
        }

        return 
$ret;
    }


    private function 
insert(DB_Connection $dbc) {
        
$ret false;

        
$id $dbc->generate_id('users_seq');
        
$username addslashes($this->get_username());
        
$password addslashes($this->get_password());

        if (
$id) {
            
$sql "insert into users (id, ".
                                          
"username, ".
                                          
"password ".
                                          
") ".
                                
"values ".
                                          
"($id,".
                                          
"'$username', ".
                                          
"'$password' ".
                                          
")";
            
$dbc->execute($sql);
            
$ret $id;
        } else {
            throw new 
TE_Exception('ID not generated - users_seq');
        }

        return 
$ret;
    }


    private function 
update(DB_Connection $dbc) {
        
$id $this->get_id();
        
$username addslashes($this->get_username());
        
$password addslashes($this->get_password());

        
$sql "update users set ".
                        
"username = '$username', ".
                        
"password = '$password' ".
                    
"where id = ".$id;

        
$dbc->execute($sql);
        
$ret $id;

        return 
$ret;
    }


    public static function 
delete(DB_Connection $dbc$id) {
        
tangra_if_not_int_throw_e($id);
        
$sql "delete from users where id = $id";
        
$rez $dbc->execute($sql);
    }
}

I_DB_Storable interface forces classes that implement it to declare two methods: save() and load_by_id().

save() method takes as parameter connected DB connection (instance of DB_Connection). It must return ID ot the saved record regardless if it is insert or update.

load_by_id() method takes connected DB connection as first parameter and ID of record to load. It returns the ID on success, on failure returns FALSE

There are few important things to bring attention on:

  • In insert() row $id = $dbc->generate_id('users_seq');. By default TF uses sequences to generate IDs which may look strange to people that are used to MySQL which unlike more sophisticated RDBMS lacks sequences. If you are using MySQL you will have to create additional table per each table in order to simulate sequences. This sequence table must be named like table_name_seq (suffix _seq to original table name); have one unsigned integer column named id and must be initialized with value 0 (i.e. you have to insert manually one row).
  • Usage of addslashes() and stripslashes() functions. You have to escape strings before feeding them into DB and unescape them when getting from DB. Some people suggest that mysql_real_escape_string() is better alternative than addslashes() but we disagree. mysql_real_escape_string() is DB dependant function, provided by mysql PHP module which may not exist and further more a MySQL connection is required before using it.
  • Formating used for SQL statement - it is recommended that you follow above style - it is much more readable and easy for maintenance. Lazy alternative to put whole SQL statement in one row is messy and sloppy.
  • delete() method is static. That way you can delete record without instantiating the class.
  • DBC class is not strictly limited to those methods. You are encouraged to add new methods that work with same DB table.