Zend framework提供了一种class和table映射起来的方式,创建一个继承Zend_Db_Table的class。查询时,zend自动将表字段做处理,生成一个对象,对象属性都是动态创建,所以是public的。这有两个大问题,一是class的属性是public,二是class的属性只有代码执行后才确定。于是乎,自己写了一个程序,根据表信息生成对应的class。
<?php
/**
* 这个类的作用是从数据库里读出表结构,然后生成一个bean类,并将其属性与类一一映射。
* 具体生成的内容包括:
* 1. 私有变量
* 2. 表字段与属性的映射关系
* 3. 表字段的信息,用于server的验证
*/
class TableClassGenerator {
const DEFAULT_DIR = 'classes';
const DEFAULT_INDENT = 4;
const DEFAULT_MIN = 2;
private $excludedProperties;
private $database;
private $file;
private $givenTables;
private $parentClass;
public function __construct($config) {
if (!isset($config) || empty($config) || ! is_array($config)) {
die('Invalid config: '. print_r($config, true));
}
$this->database = $config['database'];
$conn = isset($config['password'])
? mysql_connect($config['host'], $config['user'], $config['password'])
: mysql_connect($config['host'], $config['user']);
if (! isset($conn)) {
die('Failed to connect.' . mysql_error());
}
$this->givenTables = $config['tables'];
if (isset($this->givenTables)
&& (!is_array($this->givenTables)
|| empty($this->givenTables))) {
die("Tables($this->givenTables) in config is not an array or it is empty.");
}
$this->parentClass = $config['parentClass'];
if ($config['excludedProperties']) {
$this->excludedProperties = $config['excludedProperties'];
if (!is_array($this->excludedProperties)
|| empty($this->excludedProperties)) {
die('excludedProperties should be an array and shoudnot be empty.');
}
}
if (! file_exists(self::DEFAULT_DIR)) {
mkdir(self::DEFAULT_DIR);
}
}
public function __destroy() {
mysql_close();
}
public function generateClasses() {
$allTables = $this->getTables();
var_dump($allTables);
$tables = $this->givenTables
? $this->givenTables
: $allTables;
if (empty($tables)) {
die("Empty given tables");
}
foreach ($tables as $table) {
$index = array_search($table, $allTables);
if (!is_int($index)) {
echo "Table($table) not found in database({$this->database}).\n";
continue;
}
$this->generateClassForTable($table);
}
}
private function generateClassForTable($table) {
$class = ucfirst($this->transform($table));
$fileName = self::DEFAULT_DIR . "/$class.php";
if (file_exists($fileName)) {
echo "The file($fileName) already exists. You need delete if first.\n";
//return;
}
$columns = $this->getTableColumns($table);
if (!isset($columns) || empty($columns)) {
echo "The table($table) doesn't have columns.\n";
return;
}
$this->file = fopen($fileName, 'w');
if (! isset($this->file)) {
die("Failed to open file: $fileName");
}
echo "Generating class for table: $table.\n";
$this->writeToFile("<?php");
if ($this->parentClass) {
$this->writeToFile("class $class extends {$this->parentClass} {");
} else {
$this->writeToFile("class $class {");
}
$this->generateConst($table);
$this->generateColumnPropMapping($columns);
$this->generateValidateConfig($table, $columns);
$this->generateProperties($columns);
$this->generateGetters($columns);
$this->generateSetters($columns);
$this->writeToFile("}");
$this->writeToFile("?>");
fclose($this->file);
echo "Class($class) was created in the file($fileName).\n\n";
}
private function generateColumnPropMapping($columns) {
$this->writeToFile('private static $_colPropMapping = array(', 1);
foreach ($columns as $key => $value) {
$prop = $this->transform($key);
$this->writeToFile("'$key' => '$prop',", 2);
}
$this->writeToFile(');', 1);
$this->writeNewLine();
}
private function generateConst($table) {
$this->writeToFile("const TABLE_NAME = '$table';", 1);
$this->writeNewLine();
}
private function generateGetters($columns) {
foreach ($columns as $key => $value) {
$prop = $this->transform($key);
if ($this->shouldExcludeProp($prop)) {
continue;
}
$method = 'get' . ucfirst($prop);
$this->writeToFile("public function $method() {", 1);
$this->writeToFile('return $' . "this->$prop;", 2);
$this->writeToFile("}", 1);
$this->writeNewLine();
}
}
private function generateProperties($columns) {
$keys = array_keys($columns);
foreach ($keys as $key) {
$prop = $this->transform($key);
if ($this->shouldExcludeProp($prop)) {
continue;
}
$this->writeToFile("private $prop;", 1);
}
$this->writeNewLine();
}
private function generateSetters($columns) {
foreach ($columns as $key => $value) {
$prop = $this->transform($key);
if ($this->shouldExcludeProp($prop)) {
continue;
}
$method = 'set' . ucfirst($prop);
$this->writeToFile("public function $method ($$prop) {", 1);
$this->writeToFile('$' . "this->$prop = $" . "$prop;", 2);
$this->writeToFile("}", 1);
$this->writeNewLine();
}
}
private function generateValidateConfig($table, $columns) {
$this->writeToFile('private static $_validator = array(', 1);
$this->writeToFile("'$table' => array(", 2);
foreach ($columns as $key => $value) {
$this->writeToFile("'$key' => array(", 3);
foreach ($value as $k => $v) {
if (is_string($v)) {
$this->writeToFile("'$k' => '$v',", 4);
} else {
$this->writeToFile("'$k' => $v,", 4);
}
}
$this->writeToFile("),", 3);
}
$this->writeToFile("), // $table", 2);
$this->writeToFile(');', 1);
$this->writeNewLine();
}
private function getMin($max, $type) {
if (!isset($max)) {
return null;
}
$min = self::DEFAULT_MIN;
if ($type == 'date' || $min > $max) {
$min = $max;
}
return $min;
}
public function getTableColumns($table) {
$fields = mysql_list_fields($this->database, $table);
$count = mysql_num_fields($fields);
if (! isset($fields)) {
die("Failed to get fields" . mysql_error());
}
$columns = array();
for ($i = 0; $i < $count; $i++) {
$flags = mysql_field_flags($fields, $i);
$isRequired = preg_match('/not_null/', $flags);
$col = mysql_field_name($fields, $i);
$max = mysql_field_len($fields, $i);
$type = mysql_field_type($fields, $i);
$min = $this->getMin($max, $type);
$columns[$col] = array(
'isRequired' => $isRequired,
'max' => $max,
'min' => $min,
'type' => $type,
);
}
$sortedColumns = array();
$keys = array_keys($columns);
sort($keys);
foreach ($keys as $key) {
$sortedColumns[$key] = $columns[$key];
}
return $sortedColumns;
}
private function getTables() {
$sql = "SHOW TABLES FROM {$this->database}";
$result = mysql_query($sql);
$tables = array();
for ($i = 0; $i < mysql_num_rows($result); $i++) {
$tables[] = mysql_tablename($result, $i);
}
return $tables;
}
private function shouldExcludeProp($prop) {
if (! isset($this->excludedProperties)) {
return false;
}
$index = array_search($prop, $this->excludedProperties);
return is_int($index);
}
private function transform($name) {
$words = explode('_', $name);
$newName = null;
foreach ($words as $word) {
if ($newName == null) {
$newName = $word;
} else {
$newName .= ucfirst($word);
}
}
return $newName;
}
private function writeNewLine() {
$this->writeToFile('');
}
private function writeToFile($str, $count = 0) {
$space = null;
$count *= self::DEFAULT_INDENT;
while ($count) {
if ($space == null) {
$space = ' ';
} else {
$space .= ' ';
}
$count--;
}
fwrite($this->file, $space);
fwrite($this->file, "$str\n");
}
} // TableClassGenerator
$gen = new TableClassGenerator(array(
'excludedProperties' => array('id', 'userId'),
'database' => 'mydb',
'host' => 'localhost',
'parentClass' => 'Base',
'password' => 'pwd',
// 'tables' => array('user'),
'user' => 'userId',
));
$gen->generateClasses();