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:
Each DB table is represented by two classes:
Users. Model does not contain any DB related stuff.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:
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).
delete() method is static. That way you can delete record without instantiating the class.