четверг, 27 марта 2008 г.

DB Layer for PHP / Слой для работы с базой данных

Предлагаю своё решения для простой работы с базой данных в PHP. Идея проста, максимально автоматизировать загрузку данный из базы, так чтобы использование этого решения было простым и разработка быстрой.

И так ...

На работе я программирую на Джаве и привык работать только с объектами, так что использую для каждой таблицы класс-обложку, где в каждом классе определяю статические поля, которые хранят информацию о имени таблицы и названий полей. Конструктор который умеет создавать экземпляр класса из "резут сета" который возвращает база и методы помощники которые возвращают SQL для вставки и обновления объекта в базу.


/*
CREATE TABLE `classLanguage` (
`id` int(11) NOT NULL auto_increment,
`name` varchar(30) NOT NULL default '',
`nameInInglish` varchar(30) default NULL,
`nationalFlag` varchar(50) default NULL,
`abbr` varchar(5) default NULL,
`ui` char(1) NOT NULL default 'N',
`coding` int(11) default NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
*/
class MyLang {
// mapping to db fields
var $id;
var $name;
var $english_name;
var $picture;
var $abbr;
var $ui;
var $coding;

// table name in DB
static $_table = "classLanguage";

// All fields from DB , be used in load select
static $_fields = " id , name , nameInInglish , nationalFlag , abbr , ui , coding";

// Constructor
// if row is not null, create object from array
function MyLang( $row = null){
if ($row != null){
$this->id = $row['id'];
$this->name = $row['name'];
$this->english_name = $row['nameInInglish'];
$this->picture = $row['nationalFlag'];
$this->abbr = $row['abbr'];
$this->ui = $row['ui'];
$this->coding = $row['coding'];
}
}

// SQL use for insert, if your primary key is auto_increment, then must not contain it
function getSqlForInsert(){
return "(name , nameInInglish , nationalFlag , abbr ,ui ) VALUES ( '".$this->name."' ,'".$this->english_name."' , '".$this->picture."' , '".$this->abbr."' , '".$this->ui."' )";
}

// SQL use for update
function getSqlForUpdate(){
return " name = '".$this->name."' ,nameInInglish = '".$this->english_name."' , nationalFlag = '".$this->picture."' , abbr ='".$this->abbr."' , ui = '".$this->ui."' ";
}

}

Для того чтобы не не делать эту работу вручную, я создал для этого маленький генератор.

Теперь я написал класс "Лоадер" который умеет самостоятельно загружать объекты из базы по первичному ключу:


// Load data entry from database by primary key, return object
// className - class name will be loaded
// id - primary key
// primarykey - primary key field name. by default "id"
function load($className , $id , $primarykey = "id" ){
$result = null;
eval('$f = '.$className.'::$_fields; '.
'$t = '.$className.'::$_table; ');
$rows = $this->sqlSelect("SELECT ".$f." FROM ".$this->db->dbprefix.$t." WHERE $primarykey = " .$id );
foreach( $rows as $row){
eval('$rt = new '.$className.'($row); ');
return $rt;
}
return $result;
}

... по такому же принципу я написал методы для загрузки списков объектов и даже вспомогательный метод для автоматического листания. Ну и конечно же были необходимы методы сохранения и удаления объектов:


// Save or insert object to database
function set($object){
$evl = '$f = '.get_class($object).'::$_fields; '.
'$t = '.get_class($object).'::$_table; ';
eval($evl) ;
if ($object->id){
$this->sqlUpdate( "UPDATE ".$this->db->dbprefix.$t." SET ".$object->getSqlForUpdate().
" WHERE id = " . $object->id );
}else{
$object->id = $this->sqlInsert( "INSERT INTO ".$this->db->dbprefix.$t." ".
$object->getSqlForInsert());
$this->debug("New ".get_class($object)." created and new inserted id is " . $object->id);
}
return $this->load( get_class($object), $object->id);
}

Потом немного доработал и добавил вспомогательных методов, вообщем весь исходный код можно посмотреть тут phpDBLayer.zip

В заключении хочу описать как стало просто создавать и работать с базой.

1. Создаю таблицу в базе данных ( выбираю тип InnoDB что бы поддерживались транзакции)
2. Запускаю генератор объектов ( у меня это файл generate_objects.php)
3. Вставляю полученный код в
objects.php (если у таблицы перв. ключ "аито инкремент", то удаляю из методов обновления и встаки с генерируемого кода перв. ключ)
4. Всё готово осталось только вызывать методы поиска/загрузки.

Пример использования:



<?
include("loader.php");
$loader = new Loader;
$allLangs = $loader->loadlist('MyLang'); // See definition odf MyLang in objects.php
?>

</code><h1>All languages</h1>
<ul>
<?foreach($allLangs as $lang){?>
<li><?=$lang->name?></li>
<?}?>
</ul>
<? $loader->releaseConnection(); ?>




Полный исходный код phpDBLayer.zip