Skip to content

Instantly share code, notes, and snippets.

@13k
Created March 27, 2010 23:50
Show Gist options
  • Select an option

  • Save 13k/346432 to your computer and use it in GitHub Desktop.

Select an option

Save 13k/346432 to your computer and use it in GitHub Desktop.
Generates PHP data transfer objects [1] from a MySQL database schema in a 1:1
relationship between tables and classes.
These DTOs can be used then in several PHP ORM's, but are specifically generated
to be used with ZendFramework's Zend_Db_Table_Abstract and, for this reason,
also data access objects [2] are generated in another 1:1 relationship between
tables and classes, so N tables will generate N * (1DAO + 1DTO) [2N] files.
All classes are named after ZF namespace naming convention, using a default module
name, e.g., if you have a "user" table, the two classes created will be:
"Application_Model_User" and "Application_Model_UserMapper" (DTO and DAO,
respectivelly).
Both classes implement a minimal set of methods (for DTOs, all accessor methods and
for DAOs none, actually) and inherits from abstract classes that should implement
common logic. Sample abstract classes of both kinds are provided in this gist too.
Command-line usage and options available are poorly written, so you'll probably
need to edit this script to fit to your needs.
Usage: $ generate-db-models.php <db_user> <db_pass> <db_name> <output_directory>
Those args are pretty self-explanatory. If you use another db or connection method
(SSL, socket, whatever), edit the line containing the DSN string [3] that looks like
engine://user:pass@host/db_name
This script also uses MySQL-specific SQL queries (like SHOW and DESCRIBE) to inspect
tables, so you might need to change them to fit others db's needs.
[1] http://en.wikipedia.org/wiki/Data_transfer_object
[2] http://en.wikipedia.org/wiki/Data_access_object
[3] http://www.php.net/manual/en/pdo.construct.php ($dsn parameter)
<?php
require_once 'util.php';
class Application_Model_Abstract
{
public function __construct(array $options = null)
{
if (is_array($options)) {
$this->setFromArray($options);
}
}
public function __set($name, $value)
{
$method = 'set' . $name;
if (('mapper' == $name) || !method_exists($this, $method)) {
throw new Exception('Invalid property');
}
$this->$method($value);
}
public function __get($name)
{
$method = 'get' . $name;
if (('mapper' == $name) || !method_exists($this, $method)) {
throw new Exception('Invalid property');
}
return $this->$method();
}
public function setFromArray(array $options)
{
$methods = get_class_methods($this);
foreach ($options as $key => $value) {
$method = 'set' . camelize($key);
if (in_array($method, $methods)) {
$this->$method($value);
}
}
return $this;
}
public function toArray()
{
$methods = get_class_methods($this);
$values = array();
foreach ($methods as $method) {
$isGetter = preg_match('/^get([A-Z]\w+)/', $method, $matches);
if ($isGetter) {
$key = underline($matches[1]);
$value = $this->$method();
$values[$key] = $value;
}
}
return $values;
}
}
?>
<?php
class Application_Model_AbstractMapper
{
protected $_dbTable;
protected $_modelName;
public function setDbTable($dbTable)
{
if (is_string($dbTable)) {
$dbTable = new $dbTable();
}
if (!$dbTable instanceof Zend_Db_Table_Abstract) {
throw new Exception('Invalid table data gateway provided');
}
$this->_dbTable = $dbTable;
return $this;
}
public function getDbTable()
{
if (null === $this->_dbTable) {
$this->setDbTable("Application_Model_DbTable_$this->_modelName");
}
return $this->_dbTable;
}
public function save($obj)
{
$clazz = "Application_Model_$this->_modelName";
if (!$obj instanceof $clazz) {
throw new Exception("Argument 1 passed to save() must be an instance of $clazz");
}
$data = $obj->toArray();
if (null === ($id = $obj->getId())) {
unset($data['id']);
$this->getDbTable()->insert($data);
} else {
$this->getDbTable()->update($data, array('id = ?' => $id));
}
}
public function find($id, $obj)
{
$clazz = "Application_Model_$this->_modelName";
if (!$obj instanceof $clazz) {
throw new Exception("Argument 2 passed to save() must be an instance of $clazz");
}
$result = $this->getDbTable()->find($id);
if (count($result) < 1) {
return false;
}
$row = $result->current();
$obj->setFromArray($row->toArray());
return $obj;
}
public function fetchAll()
{
$clazz = "Application_Model_$this->_modelName";
$resultSet = $this->getDbTable()->fetchAll();
$entries = array();
foreach ($resultSet as $row) {
$entry = new $clazz();
$entry->setFromArray($row->toArray());
$entries[] = $entry;
}
return $entries;
}
}
?>
<?php
/***
*
* Generates PHP data transfer objects [1] from a MySQL database schema in a 1:1
* relationship between tables and classes.
* These DTOs can be used then in several PHP ORM's, but are specifically generated
* to be used with ZendFramework's Zend_Db_Table_Abstract and, for this reason,
* also data access objects [2] are generated in another 1:1 relationship between
* tables and classes, so N tables will generate N * (1DAO + 1DTO) [2N] files.
*
* All classes are named after ZF namespace naming convention, using a default module
* name, e.g., if you have a "user" table, the two classes created will be:
* "Application_Model_User" and "Application_Model_UserMapper" (DTO and DAO,
* respectivelly).
*
* Both classes implement a minimal set of methods (for DTOs, all accessor methods and
* for DAOs none, actually) and inherits from abstract classes that should implement
* common logic. Sample abstract classes of both kinds are provided in this gist too.
*
* Command-line usage and options available are poorly written, so you'll probably
* need to edit this script to fit to your needs.
*
* Usage: $ generate-db-models.php <db_user> <db_pass> <db_name> <output_directory>
*
* Those args are pretty self-explanatory. If you use another db or connection method
* (SSL, socket, whatever), edit the line containing the DSN string [3] that looks like
*
* engine://user:pass@host/db_name
*
* This script also uses MySQL-specific SQL queries (like SHOW and DESCRIBE) to inspect
* tables, so you might need to change them to fit others db's needs.
*
* [1] http://en.wikipedia.org/wiki/Data_transfer_object
* [2] http://en.wikipedia.org/wiki/Data_access_object
* [3] http://www.php.net/manual/en/pdo.construct.php ($dsn parameter)
*
*/
require_once("util.php");
$tables = array();
$models = array();
$user = $_SERVER['argv'][1];
$pass = $_SERVER['argv'][2];
$database = $_SERVER['argv'][3];
$outdir = $_SERVER['argv'][4];
$dbh = new PDO("mysql:host=localhost;dbname=$database", $user, $pass);
foreach($dbh->query("SHOW TABLES") as $tblrow) {
$table = $tblrow[0];
$models[$table] = camelize($table);
$tables[$table] = array();
print "- $table\n";
foreach($dbh->query("DESCRIBE $table") as $colrow) {
$col = $colrow['Field'];
$tables[$table][$col] = camelize($col);
print " * $col\n";
}
}
$dbh = null;
foreach($tables as $table => $cols) {
$model = $models[$table];
$model_file = <<<EOT
<?php
class Application_Model_${model} extends Application_Model_Abstract
{
EOT;
foreach($cols as $col => $accessor) {
$model_file .= " protected \$_$col;\n";
}
foreach($cols as $col => $accessor) {
$model_file .= <<<EOT
public function set$accessor(\$value)
{
\$this->_$col = \$value;
return \$this;
}
public function get$accessor()
{
return \$this->_$col;
}
EOT;
}
$model_file .= "}\n\n";
$mapper_file = <<<EOT
<?php
class Application_Model_${model}Mapper extends Application_Model_AbstractMapper
{
protected \$_modelName = '$model';
}
EOT;
/*
$mapper_file .= <<<EOT
public function save(Application_Model_$model \$obj)
{
\$data = array(
EOT;
foreach($cols as $col => $accessor) {
$mapper_file .= <<<EOT
'$col' => \$obj->get$accessor(),
EOT;
}
$mapper_file .= <<<EOT
);
if (null === (\$id = \$obj->getId())) {
unset(\$data['id']);
\$this->getDbTable()->insert(\$data);
} else {
\$this->getDbTable()->update(\$data, array('id = ?' => \$id));
}
}
public function find(\$id, Application_Model_$model \$obj)
{
\$result = \$this->getDbTable()->find(\$id);
if (0 == count(\$result)) {
return;
}
\$row = \$result->current();
\$obj->setId(\$row->id)
EOT;
foreach($cols as $col => $accessor) {
if ($col == 'id') { continue; }
$mapper_file .= <<<EOT
->set$accessor(\$row->$col)
EOT;
}
$mapper_file = rtrim($mapper_file);
$mapper_file .= ";\n }\n\n";
$mapper_file .= <<<EOT
public function fetchAll()
{
\$resultSet = \$this->getDbTable()->fetchAll();
\$entries = array();
foreach (\$resultSet as \$row) {
\$entry = new Application_Model_$model();
\$entry->setId(\$row->id)
EOT;
foreach($cols as $col => $accessor) {
if ($col == 'id') { continue; }
$mapper_file .= <<<EOT
->set$accessor(\$row->$col)
EOT;
}
$mapper_file = rtrim($mapper_file);
$mapper_file .= ";\n";
$mapper_file .= <<<EOT
\$entries[] = \$entry;
}
return \$entries;
}
}
EOT;
*/
$fd = fopen(implode(DIRECTORY_SEPARATOR, array($outdir, "${model}.php")), 'w');
fwrite($fd, $model_file);
fclose($fd);
$fd = fopen(implode(DIRECTORY_SEPARATOR, array($outdir, "${model}Mapper.php")), 'w');
fwrite($fd, $mapper_file);
fclose($fd);
}
<?php
function camelize($str)
{
return implode('', explode(' ', ucwords(implode(' ', explode('_', $str)))));
}
function underline($str)
{
$words = array();
$split = preg_split('/([A-Z])/', $str, -1, PREG_SPLIT_DELIM_CAPTURE|PREG_SPLIT_NO_EMPTY);
for ($i = 0; $i < count($split); $i += 2) {
$words[] = strtolower($split[$i] . $split[$i+1]);
}
return implode('_', $words);
}
?>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment