Adding Schema support, updated fixtures, deprecated loadInfo, changed Dbo::describe in all dbos, fixed tests,

git-svn-id: https://svn.cakephp.org/repo/branches/1.2.x.x@5563 3807eeeb-6ff5-0310-8944-8be069107fe0
This commit is contained in:
gwoo 2007-08-21 21:46:59 +00:00
parent 2ff0e4f54f
commit 8bb67d9f6a
29 changed files with 1514 additions and 697 deletions

View file

@ -0,0 +1,223 @@
<?php
/* SVN FILE: $Id$ */
/**
* Command-line database management utility to automate programmer chores.
*
* Schema is CakePHP's database management utility. This helps you maintain versions of
* of your database.
*
* PHP versions 4 and 5
*
* CakePHP(tm) : Rapid Development Framework <http://www.cakephp.org/>
* Copyright 2005-2007, Cake Software Foundation, Inc.
* 1785 E. Sahara Avenue, Suite 490-204
* Las Vegas, Nevada 89104
*
* Licensed under The MIT License
* Redistributions of files must retain the above copyright notice.
*
* @filesource
* @copyright Copyright 2005-2007, Cake Software Foundation, Inc.
* @link http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
* @package cake
* @subpackage cake.cake.console.libs
* @since CakePHP(tm) v 1.2.0.5550
* @version $Revision$
* @modifiedby $LastChangedBy$
* @lastmodified $Date$
* @license http://www.opensource.org/licenses/mit-license.php The MIT License
*/
uses('file', 'model' . DS . 'schema');
/**
* Schema is a command-line database management utility for automating programmer chores.
*
* @package cake
* @subpackage cake.cake.console.libs
*/
class SchemaShell extends Shell {
/**
* Override initialize
*
* @access public
* @return void
*/
function initialize() {
$this->out('Cake Schema Shell');
$this->hr();
}
/**
* Override startup
*
* @access public
* @return void
*/
function startup() {
$this->Schema =& new CakeSchema(array('path'=> CONFIGS .'sql'));
}
/**
* Read and output contents od schema object
* path to read as second arg
*
* @access public
* @return void
*/
function view() {
$path = TMP;
if (!empty($this->args[0])) {
$path = $this->args[0];
}
$File = new File($path . DS .'schema.php');
if ($File->exists()) {
$this->out($File->read());
exit();
} else {
$this->err(__('Schema could not be found', true));
exit();
}
}
/**
* Read database and Write schema object
* accepts a connection as first arg or path to save as second arg
*
* @access public
* @return void
*/
function generate() {
$this->out('Generating Schema...');
if (!empty($this->args[0])) {
$this->Schema->connection = $this->args[0];
}
if (!empty($this->args[1])) {
$this->Schema->path = $this->args[1];
}
$content = $this->Schema->read();
if ($this->Schema->write($content)) {
$this->out(__('Schema file created.', true));
exit();
} else {
$this->err(__('Schema could not be created', true));
exit();
}
}
/**
* Dump Schema object to sql file
* if first arg == write, file will be written to sql file
* or it will output sql
*
* @access public
* @return void
*/
function dump() {
$write = false;
if (!empty($this->args[0])) {
if($this->args[0] == 'write') {
$write = true;
}
}
if (!empty($this->args[1])) {
$this->Schema->path = $this->args[1];
}
$Schema = $this->Schema->load();
$db =& ConnectionManager::getDataSource($this->Schema->connection);
$contents = $db->dropSchema($Schema) . $db->createSchema($Schema);
if($write === true) {
$File = new File($this->Schema->path . DS . Inflector::underscore($this->Schema->name) .'.sql', true);
if($File->write($contents)) {
$this->out(__('SQL dump file created in '. $File->pwd(), true));
exit();
} else {
$this->err(__('SQL dump could not be created', true));
exit();
}
}
$this->out($contents);
return $contents;
}
/**
* Create database from Schema object
*
* @access public
* @return void
*/
function create() {
$Schema = $this->Schema->load();
$db =& ConnectionManager::getDataSource($this->Schema->connection);
$drop = $db->dropSchema($Schema);
$this->out($drop);
if('y' == $this->in('Are you sure you want to drop tables and create your database?', array('y', 'n'), 'n')) {
$contents = $db->createSchema($Schema);
$this->out('Updating Database...');
if ($db->_execute($contents)) {
$this->out(__('Database created', true));
exit();
} else {
$this->err(__('Database could not be created', true));
$this->err($db->lastError());
exit();
}
}
}
/**
* Update database with Schema object
*
* @access public
* @return void
*/
function update() {
$this->out('Comparing Database to Schema...');
if (!empty($this->args[0])) {
$this->Schema->connection = $this->args[0];
}
if (!empty($this->args[1])) {
$this->Schema->path = $this->args[1];
}
$Old = $this->Schema->read();
$Schema = $this->Schema->load();
$compare = $this->Schema->compare($Old, $Schema);
$db =& ConnectionManager::getDataSource($this->Schema->connection);
$db->fullDebug = true;
Configure::write('debug', 2);
$contents = $db->alterSchema($compare);
if(empty($contents)) {
$this->out(__('Current database is up to date.', true));
exit();
} else {
$this->out($contents);
}
if('y' == $this->in('Are you sure you want to update your database?', array('y', 'n'), 'n')) {
$this->out('Updating Database...');
if ($db->_execute($contents)) {
$this->out(__('Database updated', true));
exit();
} else {
$this->err(__('Database could not be updated', true));
$this->err($db->lastError());
exit();
}
}
}
/**
* Displays help contents
*
* @return void
*/
function help() {
$this->out('The Schema Shell generates a schema object from the database and updates the database from the schema.');
$this->hr();
$this->out("Usage: cake schema <command> <arg1> <arg2>...");
$this->hr();
$this->out('Commands:');
$this->out("\n\tschema help\n\t\tshows this help message.");
$this->out("\n\tschema view <path>\n\t\tread and output contents of schema file");
$this->out("\n\tschema generate <connection> <path>\n\t\treads from 'connection' writes to 'path'");
$this->out("\n\tschema dump 'write' <path>\n\t\tdump database sql based on schema file");
$this->out("\n\tschema create <path>\n\t\tdrop tables and create database based on schema file");
$this->out("\n\tschema update <path>\n\t\tmodify database based on schema file");
$this->out("");
exit();
}
}
?>

View file

@ -112,9 +112,9 @@ class ModelTask extends Shell {
$validate = array();
if (array_search($useTable, $this->__tables) !== false && (low($wannaDoValidation) == 'y' || low($wannaDoValidation) == 'yes')) {
foreach ($modelFields as $field) {
foreach ($modelFields as $fieldName => $field) {
$this->out('');
$prompt = 'Name: ' . $field['name'] . "\n";
$prompt = 'Name: ' . $fieldName . "\n";
$prompt .= 'Type: ' . $field['type'] . "\n";
$prompt .= '---------------------------------------------------------------'."\n";
$prompt .= 'Please select one of the following validation options:'."\n";
@ -126,7 +126,7 @@ class ModelTask extends Shell {
$prompt .= "5- Do not do any validation on this field.\n\n";
$prompt .= "... or enter in a valid regex validation string.\n\n";
if ($field['null'] == 1 || $field['name'] == $primaryKey || $field['name'] == 'created' || $field['name'] == 'modified') {
if ($field['null'] == 1 || $fieldName == $primaryKey || $fieldName == 'created' || $fieldName == 'modified') {
$validation = $this->in($prompt, null, '5');
} else {
$validation = $this->in($prompt, null, '1');
@ -134,21 +134,21 @@ class ModelTask extends Shell {
switch ($validation) {
case '1':
$validate[$field['name']] = 'VALID_NOT_EMPTY';
$validate[$fieldName] = 'VALID_NOT_EMPTY';
break;
case '2':
$validate[$field['name']] = 'VALID_EMAIL';
$validate[$fieldName] = 'VALID_EMAIL';
break;
case '3':
$validate[$field['name']] = 'VALID_NUMBER';
$validate[$fieldName] = 'VALID_NUMBER';
break;
case '4':
$validate[$field['name']] = 'VALID_YEAR';
$validate[$fieldName] = 'VALID_YEAR';
break;
case '5':
break;
default:
$validate[$field['name']] = $validation;
$validate[$fieldName] = $validation;
break;
}
}
@ -161,13 +161,13 @@ class ModelTask extends Shell {
$possibleKeys = array();
//Look for belongsTo
$i = 0;
foreach ($modelFields as $field) {
$offset = strpos($field['name'], '_id');
if ($field['name'] != $primaryKey && $offset !== false) {
$tmpModelName = $this->_modelNameFromKey($field['name']);
foreach ($modelFields as $fieldName => $field) {
$offset = strpos($fieldName, '_id');
if ($fieldName != $primaryKey && $offset !== false) {
$tmpModelName = $this->_modelNameFromKey($fieldName);
$associations['belongsTo'][$i]['alias'] = $tmpModelName;
$associations['belongsTo'][$i]['className'] = $tmpModelName;
$associations['belongsTo'][$i]['foreignKey'] = $field['name'];
$associations['belongsTo'][$i]['foreignKey'] = $fieldName;
$i++;
}
}
@ -179,17 +179,17 @@ class ModelTask extends Shell {
$modelFieldsTemp = $db->describe($tempOtherModel);
foreach ($modelFieldsTemp as $field) {
if ($field['type'] == 'integer' || $field['type'] == 'string') {
$possibleKeys[$otherTable][] = $field['name'];
$possibleKeys[$otherTable][] = $fieldName;
}
if ($field['name'] != $primaryKey && $field['name'] == $this->_modelKey($currentModelName)) {
if ($fieldName != $primaryKey && $fieldName == $this->_modelKey($currentModelName)) {
$tmpModelName = $this->_modelName($otherTable);
$associations['hasOne'][$j]['alias'] = $tmpModelName;
$associations['hasOne'][$j]['className'] = $tmpModelName;
$associations['hasOne'][$j]['foreignKey'] = $field['name'];
$associations['hasOne'][$j]['foreignKey'] = $fieldName;
$associations['hasMany'][$j]['alias'] = $tmpModelName;
$associations['hasMany'][$j]['className'] = $tmpModelName;
$associations['hasMany'][$j]['foreignKey'] = $field['name'];
$associations['hasMany'][$j]['foreignKey'] = $fieldName;
$j++;
}
}

View file

@ -64,14 +64,12 @@ class ErrorHandler extends Object{
require CAKE . 'dispatcher.php';
}
$this->__dispatch =& new Dispatcher();
if (!class_exists('appcontroller')) {
loadController(null);
}
if ($__previousError != array($method, $messages)) {
$__previousError = array($method, $messages);
if (!class_exists('AppController')) {
loadController(null);
}
$this->controller =& new AppController();
if (!empty($this->controller->uses)) {
$this->controller->constructClasses();
@ -84,7 +82,7 @@ class ErrorHandler extends Object{
return $this->controller->appError($method, $messages);
}
} else {
$this->controller =& new Controller();
$this->controller =& new AppController();
$this->controller->cacheAction = false;
}
if (Configure::read() > 0 || $method == 'error') {

View file

@ -192,6 +192,270 @@ class DataSource extends Object {
$this->setConfig(func_get_arg(0));
}
}
/**
* Datsrouce Query abstraction
*
* @return resource Result resource identifier
*/
function query() {
$args = func_get_args();
$fields = null;
$order = null;
$limit = null;
$page = null;
$recursive = null;
if (count($args) == 1) {
return $this->fetchAll($args[0]);
} elseif (count($args) > 1 && (strpos(low($args[0]), 'findby') === 0 || strpos(low($args[0]), 'findallby') === 0)) {
$params = $args[1];
if (strpos(strtolower($args[0]), 'findby') === 0) {
$all = false;
$field = Inflector::underscore(preg_replace('/findBy/i', '', $args[0]));
} else {
$all = true;
$field = Inflector::underscore(preg_replace('/findAllBy/i', '', $args[0]));
}
$or = (strpos($field, '_or_') !== false);
if ($or) {
$field = explode('_or_', $field);
} else {
$field = explode('_and_', $field);
}
$off = count($field) - 1;
if (isset($params[1 + $off])) {
$fields = $params[1 + $off];
}
if (isset($params[2 + $off])) {
$order = $params[2 + $off];
}
if (!array_key_exists(0, $params)) {
return false;
}
$c = 0;
$query = array();
foreach ($field as $f) {
if (!is_array($params[$c]) && !empty($params[$c]) && $params[$c] !== true && $params[$c] !== false) {
$query[$args[2]->name . '.' . $f] = '= ' . $params[$c];
} else {
$query[$args[2]->name . '.' . $f] = $params[$c];
}
$c++;
}
if ($or) {
$query = array('OR' => $query);
}
if ($all) {
if (isset($params[3 + $off])) {
$limit = $params[3 + $off];
}
if (isset($params[4 + $off])) {
$page = $params[4 + $off];
}
if (isset($params[5 + $off])) {
$recursive = $params[5 + $off];
}
return $args[2]->findAll($query, $fields, $order, $limit, $page, $recursive);
} else {
if (isset($params[3 + $off])) {
$recursive = $params[3 + $off];
}
return $args[2]->find($query, $fields, $order, $recursive);
}
} else {
if (isset($args[1]) && $args[1] === true) {
return $this->fetchAll($args[0], true);
}
return $this->fetchAll($args[0], false);
}
}
/**
* Returns an array of all result rows for a given SQL query.
* Returns false if no rows matched.
*
* @param string $sql SQL statement
* @param boolean $cache Enables returning/storing cached query results
* @return array Array of resultset rows, or false if no rows matched
*/
function fetchAll($sql, $cache = true, $modelName = null) {
if ($cache && isset($this->_queryCache[$sql])) {
if (preg_match('/^\s*select/i', $sql)) {
return $this->_queryCache[$sql];
}
}
if ($this->execute($sql)) {
$out = array();
while ($item = $this->fetchRow()) {
$out[] = $item;
}
if ($cache) {
if (strpos(trim(strtolower($sql)), 'select') !== false) {
$this->_queryCache[$sql] = $out;
}
}
return $out;
} else {
return false;
}
}
/**
* Caches/returns cached results for child instances
*
* @return array
*/
function listSources($data = null) {
if ($this->cacheSources === false) {
return null;
}
if ($this->_sources != null) {
return $this->_sources;
}
if (Configure::read() > 0) {
$expires = "+30 seconds";
} else {
$expires = "+999 days";
}
if ($data != null) {
$data = serialize($data);
}
$filename = ConnectionManager::getSourceName($this) . '_' . preg_replace("/[^A-Za-z0-9_-]/", "_", $this->config['database']) . '_list';
$new = cache('models' . DS . $filename, $data, $expires);
if ($new != null) {
$new = unserialize($new);
$this->_sources = $new;
}
return $new;
}
/**
* Convenience method for DboSource::listSources(). Returns source names in lowercase.
*
* @return array
*/
function sources() {
$return = array_map('strtolower', $this->listSources());
return $return;
}
/**
* Returns a Model description (metadata) or null if none found.
*
* @param Model $model
* @return mixed
*/
function describe($model) {
if ($this->cacheSources === false) {
return null;
}
if (isset($this->__descriptions[$model->tablePrefix.$model->table])) {
return $this->__descriptions[$model->tablePrefix.$model->table];
}
$cache = $this->__cacheDescription($model->tablePrefix.$model->table);
if ($cache !== null) {
$this->__descriptions[$model->tablePrefix.$model->table] =& $cache;
return $cache;
}
return null;
}
/**
* Converts column types to basic types
*
* @param string $real Real column type (i.e. "varchar(255)")
* @return string Abstract column type (i.e. "string")
*/
function column($real) {
return false;
}
/**
* To-be-overridden in subclasses.
*
* @param unknown_type $model
* @param unknown_type $fields
* @param unknown_type $values
* @return unknown
*/
function create(&$model, $fields = null, $values = null) {
return false;
}
/**
* To-be-overridden in subclasses.
*
* @param unknown_type $model
* @param unknown_type $queryData
* @return unknown
*/
function read(&$model, $queryData = array()) {
return false;
}
/**
* To-be-overridden in subclasses.
*
* @param unknown_type $model
* @param unknown_type $fields
* @param unknown_type $values
* @return unknown
*/
function update(&$model, $fields = null, $values = null) {
return false;
}
/**
* To-be-overridden in subclasses.
*
* @param unknown_type $model
* @param unknown_type $id
*/
function delete(&$model, $id = null) {
if ($id == null) {
$id = $model->id;
}
}
/**
* Returns the ID generated from the previous INSERT operation.
*
* @param unknown_type $source
* @return in
*/
function lastInsertId($source = null) {
return false;
}
/**
* Returns the ID generated from the previous INSERT operation.
*
* @param unknown_type $source
* @return in
*/
function lastNumRows($source = null) {
return false;
}
/**
* Returns the ID generated from the previous INSERT operation.
*
* @param unknown_type $source
* @return in
*/
function lastAffected($source = null) {
return false;
}
/**
* Returns true if the DataSource supports the given interface (method)
*
@ -249,116 +513,6 @@ class DataSource extends Object {
}
return $new;
}
/**
* To-be-overridden in subclasses.
*
* @return string
*/
function conditions($conditions) {
return $conditions;
}
/**
* To-be-overridden in subclasses.
*
* @param unknown_type $name
* @return unknown
*/
function name($name) {
return $name;
}
/**
* To-be-overridden in subclasses.
*
* @param unknown_type $value
* @return unknown
*/
function value($value) {
return $value;
}
/**
* Returns a Model description (metadata) or null if none found.
*
* @param Model $model
* @return mixed
*/
function describe($model) {
if ($this->cacheSources === false) {
return null;
}
if (isset($this->__descriptions[$model->tablePrefix.$model->table])) {
return $this->__descriptions[$model->tablePrefix.$model->table];
}
$cache = $this->__cacheDescription($model->tablePrefix.$model->table);
if ($cache !== null) {
$this->__descriptions[$model->tablePrefix.$model->table] =& $cache;
return $cache;
}
return null;
}
/**
* To-be-overridden in subclasses.
*
* @param unknown_type $model
* @param unknown_type $fields
* @param unknown_type $values
* @return unknown
*/
function create(&$model, $fields = null, $values = null) {
return false;
}
/**
* To-be-overridden in subclasses.
*
* @param unknown_type $model
* @param unknown_type $queryData
* @return unknown
*/
function read(&$model, $queryData = array()) {
return false;
}
/**
* To-be-overridden in subclasses.
*
* @param unknown_type $model
* @param unknown_type $fields
* @param unknown_type $values
* @return unknown
*/
function update(&$model, $fields = null, $values = null) {
return false;
}
/**
* To-be-overridden in subclasses.
*
* @param unknown_type $model
* @param unknown_type $id
*/
function delete(&$model, $id = null) {
if ($id == null) {
$id = $model->id;
}
}
/**
* To-be-overridden in subclasses.
*
* @param mixed $fields
* @return mixed
*/
function fields($fields) {
return $fields;
}
/**
* To-be-overridden in subclasses.
*
* @param Model $model
* @param unknown_type $fields
* @return unknown
*/
function getColumnType(&$model, $fields) {
return false;
}
/**
* Enter description here...
*

View file

@ -221,8 +221,9 @@ class DboAdodb extends DboSource {
$cols = $this->_adodb->MetaColumns($this->fullTableName($model, false));
foreach ($cols as $column) {
$fields[] = array('name' => $column->name,
'type' => $this->column($column->type));
$fields[$column->name] = array(
'type' => $this->column($column->type)
);
}
$this->__cacheDescription($this->fullTableName($model, false), $fields);

View file

@ -216,8 +216,7 @@ class DboDb2 extends DboSource {
$result = db2_columns($this->connection, '', '', strtoupper($this->fullTableName($model)));
while (db2_fetch_row($result)) {
$fields[] = array(
'name' => strtolower(db2_result($result, 'COLUMN_NAME')),
$fields[strtolower(db2_result($result, 'COLUMN_NAME'))] = array(
'type' => db2_result($result, 'TYPE_NAME'),
'null' => db2_result($result, 'NULLABLE'),
'default' => db2_result($result, 'COLUMN_DEF'));

View file

@ -205,11 +205,11 @@ class DboFirebird extends DboSource {
$col_info = ibase_field_info($rs, $i);
$col_info['type'] = $this->column($col_info['type']);
$fields[] = array(
'name' => strtolower($col_info['name']),
'type' => $col_info['type'],
'null' => '',
'length' => $col_info['length']);
$fields[strtolower($col_info['name'])] = array(
'type' => $col_info['type'],
'null' => '',
'length' => $col_info['length']
);
}
$this->__cacheDescription($model->tablePrefix . $model->table, $fields);
return $fields;

View file

@ -575,16 +575,6 @@ class DboMssql extends DboSource {
return false;
}
}
function buildSchemaQuery($schema) {
$search = array('{AUTOINCREMENT}', '{PRIMARY}', '{UNSIGNED}', '{FULLTEXT}', '{BOOLEAN}', '{UTF_8}');
$replace = array('int(11) not null auto_increment', 'primary key', 'unsigned', 'FULLTEXT',
'enum (\'true\', \'false\') NOT NULL default \'true\'', '/*!40100 CHARACTER SET utf8 COLLATE utf8_unicode_ci */');
$query = trim(r($search, $replace, $schema));
return $query;
}
/**
* Inserts multiple values into a join table
*
@ -598,5 +588,6 @@ class DboMssql extends DboSource {
$this->query("INSERT INTO {$table} ({$fields}) VALUES {$values[$x]}");
}
}
}
?>

View file

@ -163,12 +163,10 @@ class DboMysql extends DboSource {
* @return array Fields in table. Keys are name and type
*/
function describe(&$model) {
$cache = parent::describe($model);
if ($cache != null) {
return $cache;
}
$fields = false;
$cols = $this->query('DESCRIBE ' . $this->fullTableName($model));
@ -178,16 +176,20 @@ class DboMysql extends DboSource {
$column[0] = $column[$colKey[0]];
}
if (isset($column[0])) {
$fields[] = array(
'name' => $column[0]['Field'],
$fields[$column[0]['Field']] = array(
'type' => $this->column($column[0]['Type']),
'null' => ($column[0]['Null'] == 'YES' ? true : false),
'default' => $column[0]['Default'],
'length' => $this->length($column[0]['Type'])
'length' => $this->length($column[0]['Type']),
);
if(!empty($column[0]['Key']) && isset($this->index[$column[0]['Key']])) {
$fields[$column[0]['Field']]['key'] = $this->index[$column[0]['Key']];
}
if(!empty($column[0]['Extra'])) {
$fields[$column[0]['Field']]['extra'] = $column[0]['Extra'];
}
}
}
$this->__cacheDescription($this->fullTableName($model, false), $fields);
return $fields;
}
@ -451,6 +453,33 @@ class DboMysql extends DboSource {
function getEncoding() {
return mysql_client_encoding($this->connection);
}
/**
* Returns an array of the indexes in given table name.
*
* @param string $model Name of model to inspect
* @return array Fields in table. Keys are column and unique
*/
function index($model) {
$index = array();
$table = $this->fullTableName($model, false);
if($table) {
$indexes = $this->query('SHOW INDEX FROM ' . $table);
$keys = Set::extract($indexes, '{n}.STATISTICS');
foreach ($keys as $i => $key) {
if(!isset($index[$key['Key_name']])) {
$index[$key['Key_name']]['column'] = $key['Column_name'];
$index[$key['Key_name']]['unique'] = ife($key['Non_unique'] == 0, 1, 0);
} else {
if(!is_array($index[$key['Key_name']]['column'])) {
$col[] = $index[$key['Key_name']]['column'];
}
$col[] = $key['Column_name'];
$index[$key['Key_name']]['column'] = $col;
}
}
}
return $index;
}
/**
* Generate a MySQL schema for the given Schema object
*
@ -459,31 +488,110 @@ class DboMysql extends DboSource {
* Otherwise, all tables defined in the schema are generated.
* @return string
*/
function generateSchema($schema, $table = null) {
function createSchema($schema, $table = null) {
if (!is_a($schema, 'CakeSchema')) {
trigger_error(__('Invalid schema object', true), E_USER_WARNING);
return null;
}
$out = '';
foreach ($schema->tables as $curTable => $columns) {
if (empty($table) || $table == $curTable) {
if (!$table || $table == $curTable) {
$out .= 'CREATE TABLE ' . $this->fullTableName($curTable) . " (\n";
$colList = array();
$cols = $colList = $index = array();
$primary = null;
foreach ($columns as $col) {
if (isset($col['key']) && $col['key'] == 'primary') {
$primary = $col;
foreach ($columns as $name => $col) {
if (is_string($col)) {
$col = array('type' => $col);
}
$colList[] = $this->generateColumnSchema($col);
if (isset($col['key']) && $col['key'] == 'primary') {
$primary = $name;
}
if($name !== 'indexes') {
$col['name'] = $name;
if(!isset($col['type'])) {
$col['type'] = 'string';
}
$cols[] = $this->buildColumn($col);
} else {
$index[] = $this->buildIndex($col);
}
}
if (empty($primary)) {
$primary = array('id', 'integer', 'key' => 'primary');
array_unshift($colList, $this->generateColumnSchema($primary));
if (empty($index) && empty($primary)) {
$primary = 'id';
$col = array('type'=>'integer', 'key' => 'primary');
array_unshift($cols, $this->buildColumn($col));
}
$colList[] = 'PRIMARY KEY (' . $this->name($primary[0]) . ')';
$out .= "\t" . join(",\n\t", $colList) . "\n);\n\n";
if(empty($index) && !empty($primary)) {
$col = array('PRIMARY'=> array('column'=> $primary, 'unique' => 1));
$index[] = $this->buildIndex($col);
}
$out .= "\t" . join(",\n\t", $cols) . ",\n\t". join(",\n\t", $index) . "\n);\n\n";
}
}
return $out;
}
/**
* Generate a MySQL Alter Table syntax for the given Schema comparison
*
* @param unknown_type $schema
* @return unknown
*/
function alterSchema($compare) {
if(!is_array($compare)) {
return false;
}
$out = '';
$colList = array();
foreach($compare as $table => $types) {
$out .= 'ALTER TABLE ' . $this->fullTableName($table) . " \n";
foreach($types as $type => $column) {
switch($type) {
case 'add':
foreach($column as $field => $col) {
$col['name'] = $field;
$alter = 'ADD '.$this->buildColumn($col);
if(isset($col['after'])) {
$alter .= ' AFTER '. $this->name($col['after']);
}
$colList[] = $alter;
}
break;
case 'drop':
foreach($column as $field => $col) {
$col['name'] = $field;
$colList[] = 'DROP '.$this->name($field);
}
break;
case 'change':
foreach($column as $field => $col) {
$col['name'] = $field;
$colList[] = 'CHANGE '. $this->name($field).' '.$this->buildColumn($col);
}
break;
}
}
$out .= "\t" . join(",\n\t", $colList) . ";\n\n";
}
return $out;
}
/**
* Generate a MySQL Drop table for the given Schema object
*
* @param object $schema An instance of a subclass of CakeSchema
* @param string $table Optional. If specified only the table name given will be generated.
* Otherwise, all tables defined in the schema are generated.
* @return string
*/
function dropSchema($schema, $table = null) {
if (!is_a($schema, 'CakeSchema')) {
trigger_error(__('Invalid schema object', true), E_USER_WARNING);
return null;
}
$out = '';
foreach ($schema->tables as $curTable => $columns) {
if (!$table || $table == $curTable) {
$out .= 'DROP TABLE ' . $this->fullTableName($curTable) . ";\n";
}
}
return $out;
@ -491,23 +599,25 @@ class DboMysql extends DboSource {
/**
* Generate a MySQL-native column schema string
*
* @param array $column An array structured like the following: array('name', 'type'[, options]),
* @param array $column An array structured like the following: array('name'=>'value', 'type'=>'value'[, options]),
* where options can be 'default', 'length', or 'key'.
* @return string
*/
function generateColumnSchema($column) {
function buildColumn($column) {
$name = $type = null;
$column = am(array('null' => true), $column);
list($name, $type) = $column;
extract($column);
if (empty($name) || empty($type)) {
trigger_error('Column name or type not defined in schema', E_USER_WARNING);
return null;
}
if (!isset($this->columns[$type])) {
trigger_error("Column type {$type} does not exist", E_USER_WARNING);
return null;
}
$real = $this->columns[$type];
$out = $this->name($name) . ' ' . $real['name'];
@ -523,9 +633,10 @@ class DboMysql extends DboSource {
}
$out .= '(' . $length . ')';
}
if (isset($column['key']) && $column['key'] == 'primary') {
if (isset($column['key']) && $column['key'] == 'primary' && (!isset($column['extra']) || (isset($column['extra']) && $column['extra'] == 'auto_increment'))) {
$out .= ' NOT NULL AUTO_INCREMENT';
} elseif (isset($column['key']) && $column['key'] == 'primary') {
$out .= ' NOT NULL';
} elseif (isset($column['default'])) {
$out .= ' DEFAULT ' . $this->value($column['default'], $type);
} elseif (isset($column['null']) && $column['null'] == true) {
@ -538,19 +649,30 @@ class DboMysql extends DboSource {
return $out;
}
/**
* Enter description here...
* Format indexes for create table
*
* @param unknown_type $schema
* @return unknown
* @param array $indexes
* @return string
*/
function buildSchemaQuery($schema) {
$search = array('{AUTOINCREMENT}', '{PRIMARY}', '{UNSIGNED}', '{FULLTEXT}',
'{FULLTEXT_MYSQL}', '{BOOLEAN}', '{UTF_8}');
$replace = array('int(11) not null auto_increment', 'primary key', 'unsigned',
'FULLTEXT', 'FULLTEXT', 'enum (\'true\', \'false\') NOT NULL default \'true\'',
'/*!40100 CHARACTER SET utf8 COLLATE utf8_unicode_ci */');
$query = trim(r($search, $replace, $schema));
return $query;
function buildIndex($indexes) {
$join = array();
foreach ($indexes as $name => $value) {
$out = null;
if ($name == 'PRIMARY') {
$out .= 'PRIMARY KEY (' . $this->name($value['column']) . ')';
} else {
if (!empty($value['unique'])) {
$out .= 'UNIQUE ';
}
if (is_array($value['column'])) {
$out .= 'KEY '. $name .' (' . join(', ', array_map(array(&$this, 'name'), $value['column'])) . ')';
} else {
$out .= 'KEY '. $name .' (' . $this->name($value['column']) . ')';
}
}
$join[] = $out;
}
return join(",\n\t", $join);
}
}
?>

View file

@ -167,8 +167,7 @@ class DboMysqli extends DboSource {
$column[0] = $column[$colKey[0]];
}
if (isset($column[0])) {
$fields[] = array(
'name' => $column[0]['Field'],
$fields[$column[0]['Field']] = array(
'type' => $this->column($column[0]['Type']),
'null' => ($column[0]['Null'] == 'YES' ? true : false),
'default' => $column[0]['Default'],
@ -438,106 +437,5 @@ class DboMysqli extends DboSource {
function getEncoding() {
return mysqli_client_encoding($this->connection);
}
/**
* Generate a MySQL schema for the given Schema object
*
* @param object $schema An instance of a subclass of CakeSchema
* @param string $table Optional. If specified only the table name given will be generated.
* Otherwise, all tables defined in the schema are generated.
* @return string
*/
function generateSchema($schema, $table = null) {
if (!is_a($schema, 'CakeSchema')) {
trigger_error(__('Invalid schema object', true), E_USER_WARNING);
return null;
}
$out = '';
foreach ($schema->tables as $curTable => $columns) {
if (empty($table) || $table == $curTable) {
$out .= 'CREATE TABLE ' . $this->fullTableName($curTable) . " (\n";
$colList = array();
$primary = null;
foreach ($columns as $col) {
if (isset($col['key']) && $col['key'] == 'primary') {
$primary = $col;
}
$colList[] = $this->generateColumnSchema($col);
}
if (empty($primary)) {
$primary = array('id', 'integer', 'key' => 'primary');
array_unshift($colList, $this->generateColumnSchema($primary));
}
$colList[] = 'PRIMARY KEY (' . $this->name($primary[0]) . ')';
$out .= "\t" . join(",\n\t", $colList) . "\n);\n\n";
}
}
return $out;
}
/**
* Generate a MySQL-native column schema string
*
* @param array $column An array structured like the following: array('name', 'type'[, options]),
* where options can be 'default', 'length', or 'key'.
* @return string
*/
function generateColumnSchema($column) {
$name = $type = null;
$column = am(array('null' => true), $column);
list($name, $type) = $column;
if (empty($name) || empty($type)) {
trigger_error('Column name or type not defined in schema', E_USER_WARNING);
return null;
}
if (!isset($this->columns[$type])) {
trigger_error("Column type {$type} does not exist", E_USER_WARNING);
return null;
}
$real = $this->columns[$type];
$out = $this->name($name) . ' ' . $real['name'];
if (isset($real['limit']) || isset($real['length']) || isset($column['limit']) || isset($column['length'])) {
if (isset($column['length'])) {
$length = $column['length'];
} elseif (isset($column['limit'])) {
$length = $column['limit'];
} elseif (isset($real['length'])) {
$length = $real['length'];
} else {
$length = $real['limit'];
}
$out .= '(' . $length . ')';
}
if (isset($column['key']) && $column['key'] == 'primary') {
$out .= ' NOT NULL AUTO_INCREMENT';
} elseif (isset($column['default'])) {
$out .= ' DEFAULT ' . $this->value($column['default'], $type);
} elseif (isset($column['null']) && $column['null'] == true) {
$out .= ' DEFAULT NULL';
} elseif (isset($column['default']) && isset($column['null']) && $column['null'] == false) {
$out .= ' DEFAULT ' . $this->value($column['default'], $type) . ' NOT NULL';
} elseif (isset($column['null']) && $column['null'] == false) {
$out .= ' NOT NULL';
}
return $out;
}
/**
* Enter description here...
*
* @param unknown_type $schema
* @return unknown
*/
function buildSchemaQuery($schema) {
$search = array('{AUTOINCREMENT}', '{PRIMARY}', '{UNSIGNED}', '{FULLTEXT}',
'{FULLTEXT_MYSQL}', '{BOOLEAN}', '{UTF_8}');
$replace = array('int(11) not null auto_increment', 'primary key', 'unsigned',
'FULLTEXT', 'FULLTEXT', 'enum (\'true\', \'false\') NOT NULL default \'true\'',
'/*!40100 CHARACTER SET utf8 COLLATE utf8_unicode_ci */');
$query = trim(r($search, $replace, $schema));
return $query;
}
}
?>

View file

@ -174,23 +174,19 @@ class DboOdbc extends DboSource{
return $cache;
}
$fields=array();
$sql='SELECT * FROM ' . $this->fullTableName($model) . ' LIMIT 1';
$result=odbc_exec($this->connection, $sql);
$fields = array();
$sql = 'SELECT * FROM ' . $this->fullTableName($model) . ' LIMIT 1';
$result = odbc_exec($this->connection, $sql);
$count=odbc_num_fields($result);
$count = odbc_num_fields($result);
for ($i = 1; $i <= $count; $i++) {
$cols[$i - 1] = odbc_field_name($result, $i);
}
foreach ($cols as $column) {
$type
= odbc_field_type(
odbc_exec($this->connection, "SELECT " . $column . " FROM " . $this->fullTableName($model)),
1);
array_push($fields, array('name' => $column,
'type' => $type));
$type = odbc_field_type(odbc_exec($this->connection, "SELECT " . $column . " FROM " . $this->fullTableName($model)), 1);
$fields[$column] array('type' => $type));
}
$this->__cacheDescription($model->tablePrefix . $model->table, $fields);
@ -202,7 +198,7 @@ class DboOdbc extends DboSource{
return '*';
}
$pos=strpos($data, '`');
$pos = strpos($data, '`');
if ($pos === false) {
$data = '' . str_replace('.', '.', $data) . '';
@ -441,26 +437,5 @@ class DboOdbc extends DboSource{
return false;
}
}
function buildSchemaQuery($schema) {
$search=array('{AUTOINCREMENT}',
'{PRIMARY}',
'{UNSIGNED}',
'{FULLTEXT}',
'{FULLTEXT_MYSQL}',
'{BOOLEAN}',
'{UTF_8}');
$replace=array('int(11) not null auto_increment',
'primary key',
'unsigned',
'FULLTEXT',
'FULLTEXT',
'enum (\'true\', \'false\') NOT NULL default \'true\'',
'/*!40100 CHARACTER SET utf8 COLLATE utf8_unicode_ci */');
$query=trim(str_replace($search, $replace, $schema));
return $query;
}
}
?>

View file

@ -424,9 +424,8 @@ class DboOracle extends DboSource {
$fields = array();
for($i=0; $row = $this->fetchRow(); $i++) {
$fields[$i]['name'] = strtolower($row[0]['COLUMN_NAME']);
$fields[$i]['length'] = $row[0]['DATA_LENGTH'];
$fields[$i]['type'] = $this->column($row[0]['DATA_TYPE']);
$fields[strtolower($row[0]['COLUMN_NAME'])] = array('type'=> $this->column($row[0]['DATA_TYPE']),
'length'=> $row[0]['DATA_LENGTH']);
}
$this->__cacheDescription($this->fullTableName($model, false), $fields);
return $fields;
@ -617,56 +616,6 @@ class DboOracle extends DboSource {
function lastAffected() {
return $this->_statementId ? ocirowcount($this->_statementId): false;
}
/**
* Generate a Oracle-native column schema string
*
* @param array $column An array structured like the following: array('name', 'type'[, options]),
* where options can be 'default', 'length', or 'key'.
* @return string
*/
function generateColumnSchema($column) {
$name = $type = null;
$column = am(array('null' => true), $column);
list($name, $type) = $column;
if (empty($name) || empty($type)) {
trigger_error('Column name or type not defined in schema', E_USER_WARNING);
return null;
}
if (!isset($this->columns[$type])) {
trigger_error("Column type {$type} does not exist", E_USER_WARNING);
return null;
}
$real = $this->columns[$type];
$out = $this->name($name) . ' ' . $real['name'];
if (isset($real['limit']) || isset($real['length']) || isset($column['limit']) || isset($column['length'])) {
if (isset($column['length'])) {
$length = $column['length'];
} elseif (isset($column['limit'])) {
$length = $column['limit'];
} elseif (isset($real['length'])) {
$length = $real['length'];
} else {
$length = $real['limit'];
}
$out .= '(' . $length . ')';
}
if (isset($column['key']) && $column['key'] == 'primary') {
$out .= ' NOT NULL ';
} elseif (isset($column['default'])) {
$out .= ' DEFAULT ' . $this->value($column['default'], $type);
} elseif (isset($column['null']) && $column['null'] == true) {
$out .= ' DEFAULT NULL';
} elseif (isset($column['default']) && isset($column['null']) && $column['null'] == false) {
$out .= ' DEFAULT ' . $this->value($column['default'], $type) . ' NOT NULL';
} elseif (isset($column['null']) && $column['null'] == false) {
$out .= ' NOT NULL';
}
return $out;
}
/**
* Inserts multiple values into a join table
*

View file

@ -190,8 +190,7 @@ class DboPostgres extends DboSource {
} else {
$length = $this->length($c['type']);
}
$fields[] = array(
'name' => $c['name'],
$fields[$c['name']] = array(
'type' => $this->column($c['type']),
'null' => ($c['null'] == 'NO' ? false : true),
'default' => $c['default'],
@ -579,59 +578,6 @@ class DboPostgres extends DboSource {
function getEncoding() {
return pg_client_encoding($this->connection);
}
/**
* Generate a PostgreSQL-native column schema string
*
* @param array $column An array structured like the following: array('name', 'type'[, options]),
* where options can be 'default', 'length', or 'key'.
* @return string
*/
function generateColumnSchema($column) {
$name = $type = $out = null;
$column = am(array('null' => true), $column);
list($name, $type) = $column;
if (empty($name) || empty($type)) {
trigger_error('Column name or type not defined in schema', E_USER_WARNING);
return null;
}
if (!isset($this->columns[$type])) {
trigger_error("Column type {$type} does not exist", E_USER_WARNING);
return null;
}
$out = "\t" . $this->name($name) . ' ';
if (!isset($column['key']) || $column['key'] != 'primary') {
$real = $this->columns[$type];
$out .= $real['name'];
if (isset($real['limit']) || isset($real['length']) || isset($column['limit']) || isset($column['length'])) {
if (isset($column['length'])) {
$length = $column['length'];
} elseif (isset($column['limit'])) {
$length = $column['limit'];
} elseif (isset($real['length'])) {
$length = $real['length'];
} else {
$length = $real['limit'];
}
$out .= '(' . $length . ')';
}
}
if (isset($column['key']) && $column['key'] == 'primary') {
$out .= $this->columns['primary_key']['name'];
} elseif (isset($column['default'])) {
$out .= ' DEFAULT ' . $this->value($column['default'], $type);
} elseif (isset($column['null']) && $column['null'] == true) {
$out .= ' DEFAULT NULL';
} elseif (isset($column['default']) && isset($column['null']) && $column['null'] == false) {
$out .= ' DEFAULT ' . $this->value($column['default'], $type) . ' NOT NULL';
} elseif (isset($column['null']) && $column['null'] == false) {
$out .= ' NOT NULL';
}
return $out;
}
}
?>

View file

@ -160,8 +160,7 @@ class DboSqlite extends DboSource {
$result = $this->fetchAll('PRAGMA table_info(' . $model->tablePrefix . $model->table . ')');
foreach ($result as $column) {
$fields[] = array(
'name' => $column[0]['name'],
$fields[$column[0]['name']] = array(
'type' => $this->column($column[0]['type']),
'null' => ! $column[0]['notnull'],
'default' => $column[0]['dflt_value']
@ -405,6 +404,6 @@ class DboSqlite extends DboSource {
for ($x = 0; $x < $count; $x++) {
$this->query("INSERT INTO {$table} ({$fields}) VALUES {$values[$x]}");
}
}
}
}
?>

View file

@ -170,7 +170,9 @@ class DboSybase extends DboSource {
$column[0] = $column[$colKey[0]];
}
if (isset($column[0])) {
$fields[] = array('name' => $column[0]['Field'], 'type' => $this->column($column[0]['Type']), 'null' => $column[0]['Null']);
$fields[$column[0]['Field']] = array('type' => $this->column($column[0]['Type']),
'null' => $column[0]['Null']
);
}
}
@ -377,21 +379,6 @@ class DboSybase extends DboSource {
return false;
}
}
/**
* Enter description here...
*
* @param unknown_type $schema
* @return unknown
*/
function buildSchemaQuery($schema) {
$search = array('{AUTOINCREMENT}', '{PRIMARY}', '{UNSIGNED}', '{FULLTEXT}',
'{FULLTEXT_SYBASE}', '{BOOLEAN}', '{UTF_8}');
$replace = array('int(11) not null auto_increment', 'primary key', 'unsigned',
'FULLTEXT', 'FULLTEXT', 'enum (\'true\', \'false\') NOT NULL default \'true\'',
'/*!40100 CHARACTER SET utf8 COLLATE utf8_unicode_ci */');
$query = trim(r($search, $replace, $schema));
return $query;
}
/**
* Inserts multiple values into a join table
*
@ -404,6 +391,6 @@ class DboSybase extends DboSource {
for ($x = 0; $x < $count; $x++) {
$this->query("INSERT INTO {$table} ({$fields}) VALUES {$values[$x]}");
}
}
}
}
?>

View file

@ -43,11 +43,11 @@ class DboSource extends DataSource {
*/
var $description = "Database Data Source";
/**
* Enter description here...
* index definition, standard cake, primary, index, unique
*
* @var unknown_type
* @var array
*/
var $__bypass = false;
var $index = array('PRI'=> 'primary', 'MUL'=> 'index', 'UNI'=>'unique');
/**
* Enter description here...
*
@ -66,18 +66,24 @@ class DboSource extends DataSource {
* @var unknown_type
*/
var $alias = 'AS ';
/**
* The set of valid SQL operations usable in a WHERE statement
*
* @var array
*/
var $__sqlOps = array('like', 'ilike', 'or', 'not', 'in', 'between', 'regexp', 'similar to');
/**
* Enter description here...
*
* @var unknown_type
*/
var $goofyLimit = false;
/**
* Enter description here...
*
* @var unknown_type
*/
var $__bypass = false;
/**
* The set of valid SQL operations usable in a WHERE statement
*
* @var array
*/
var $__sqlOps = array('like', 'ilike', 'or', 'not', 'in', 'between', 'regexp', 'similar to');
/**
* Constructor
*/
@ -126,135 +132,6 @@ class DboSource extends DataSource {
return null;
}
}
/**
* Caches/returns cached results for child instances
*
* @return array
*/
function listSources($data = null) {
if ($this->cacheSources === false) {
return null;
}
if ($this->_sources != null) {
return $this->_sources;
}
if (Configure::read() > 0) {
$expires = "+30 seconds";
} else {
$expires = "+999 days";
}
if ($data != null) {
$data = serialize($data);
}
$filename = ConnectionManager::getSourceName($this) . '_' . preg_replace("/[^A-Za-z0-9_-]/", "_", $this->config['database']) . '_list';
$new = cache('models' . DS . $filename, $data, $expires);
if ($new != null) {
$new = unserialize($new);
$this->_sources = $new;
}
return $new;
}
/**
* Convenience method for DboSource::listSources(). Returns source names in lowercase.
*
* @return array
*/
function sources() {
$return = array_map('strtolower', $this->listSources());
return $return;
}
/**
* SQL Query abstraction
*
* @return resource Result resource identifier
*/
function query() {
$args = func_get_args();
$fields = null;
$order = null;
$limit = null;
$page = null;
$recursive = null;
if (count($args) == 1) {
return $this->fetchAll($args[0]);
} elseif (count($args) > 1 && (strpos(low($args[0]), 'findby') === 0 || strpos(low($args[0]), 'findallby') === 0)) {
$params = $args[1];
if (strpos(strtolower($args[0]), 'findby') === 0) {
$all = false;
$field = Inflector::underscore(preg_replace('/findBy/i', '', $args[0]));
} else {
$all = true;
$field = Inflector::underscore(preg_replace('/findAllBy/i', '', $args[0]));
}
$or = (strpos($field, '_or_') !== false);
if ($or) {
$field = explode('_or_', $field);
} else {
$field = explode('_and_', $field);
}
$off = count($field) - 1;
if (isset($params[1 + $off])) {
$fields = $params[1 + $off];
}
if (isset($params[2 + $off])) {
$order = $params[2 + $off];
}
if (!array_key_exists(0, $params)) {
return false;
}
$c = 0;
$query = array();
foreach ($field as $f) {
if (!is_array($params[$c]) && !empty($params[$c]) && $params[$c] !== true && $params[$c] !== false) {
$query[$args[2]->name . '.' . $f] = '= ' . $params[$c];
} else {
$query[$args[2]->name . '.' . $f] = $params[$c];
}
$c++;
}
if ($or) {
$query = array('OR' => $query);
}
if ($all) {
if (isset($params[3 + $off])) {
$limit = $params[3 + $off];
}
if (isset($params[4 + $off])) {
$page = $params[4 + $off];
}
if (isset($params[5 + $off])) {
$recursive = $params[5 + $off];
}
return $args[2]->findAll($query, $fields, $order, $limit, $page, $recursive);
} else {
if (isset($params[3 + $off])) {
$recursive = $params[3 + $off];
}
return $args[2]->find($query, $fields, $order, $recursive);
}
} else {
if (isset($args[1]) && $args[1] === true) {
return $this->fetchAll($args[0], true);
}
return $this->fetchAll($args[0], false);
}
}
/**
* Executes given SQL statement.
*
@ -1829,5 +1706,64 @@ class DboSource extends DataSource {
$values = implode(', ', $values);
$this->query("INSERT INTO {$table} ({$fields}) VALUES {$values}");
}
/**
* Returns an array of the indexes in given datasource name.
*
* @param string $model Name of model to inspect
* @return array Fields in table. Keys are column and unique
*/
function index($model) {
return false;
}
/**
* Generate a create syntax from CakeSchema
*
* @param object $schema An instance of a subclass of CakeSchema
* @param string $table Optional. If specified only the table name given will be generated.
* Otherwise, all tables defined in the schema are generated.
* @return string
*/
function createSchema($schema, $table = null) {
return false;
}
/**
* Generate a alter syntax from CakeSchema::compare()
*
* @param unknown_type $schema
* @return unknown
*/
function alterSchema($compare) {
return false;
}
/**
* Generate a drop syntax from CakeSchema
*
* @param object $schema An instance of a subclass of CakeSchema
* @param string $table Optional. If specified only the table name given will be generated.
* Otherwise, all tables defined in the schema are generated.
* @return string
*/
function dropSchema($schema, $table = null) {
return false;
}
/**
* Generate a column schema string
*
* @param array $column An array structured like the following: array('name'=>'value', 'type'=>'value'[, options]),
* where options can be 'default', 'length', or 'key'.
* @return string
*/
function buildColumn($column) {
return false;
}
/**
* Format indexes for create table
*
* @param array $indexes
* @return string
*/
function buildIndex($indexes) {
return false;
}
}
?>

View file

@ -106,7 +106,12 @@ class Model extends Overloadable {
* Table metadata
*
* @var array
* @access private
* @access protected
*/
var $_schema = null;
/**
*
* @deprecated see $_schema
*/
var $_tableInfo = null;
/**
@ -706,12 +711,14 @@ class Model extends Overloadable {
$this->{$type}[$assocKey]['joinTable'] = $this->{$with}->table;
} elseif ($type == 'hasAndBelongsToMany') {
$joinClass = Inflector::camelize($this->name . $assocKey);
$this->{$type}[$assocKey]['_with'] = $joinClass;
$this->{$joinClass} = new AppModel(array(
'name' => $joinClass,
'table' => $this->{$type}[$assocKey]['joinTable'],
'ds' => $this->useDbConfig
));
if(!class_exists(low($joinClass))) {
$this->{$type}[$assocKey]['_with'] = $joinClass;
$this->{$joinClass} = new AppModel(array(
'name' => $joinClass,
'table' => $this->{$type}[$assocKey]['joinTable'],
'ds' => $this->useDbConfig
));
}
}
}
}
@ -724,7 +731,6 @@ class Model extends Overloadable {
$this->setDataSource($this->useDbConfig);
$db =& ConnectionManager::getDataSource($this->useDbConfig);
$db->cacheSources = $this->cacheSources;
if ($db->isInterfaceSupported('listSources')) {
$sources = $db->listSources();
if (is_array($sources) && !in_array(low($this->tablePrefix . $tableName), array_map('low', $sources))) {
@ -733,13 +739,13 @@ class Model extends Overloadable {
'table' => $this->tablePrefix . $tableName
)));
} else {
$this->table = $tableName;
$this->table = $this->useTable = $tableName;
$this->tableToModel[$this->table] = $this->name;
$this->_tableInfo = null;
$this->loadInfo();
}
} else {
$this->table = $tableName;
$this->table = $this->useTable = $tableName;
$this->tableToModel[$this->table] = $this->name;
$this->loadInfo();
}
@ -792,20 +798,34 @@ class Model extends Overloadable {
}
/**
* Returns an array of table metadata (column names and types) from the database.
* $field => keys(type, null, default, key, length, extra)
*
* @return array Array of table metadata
*/
function loadInfo($clear = false) {
if (!is_object($this->_tableInfo) || $clear) {
function schema($clear = false) {
if (!is_object($this->_schema) || $clear) {
$db =& ConnectionManager::getDataSource($this->useDbConfig);
$db->cacheSources = $this->cacheSources;
if ($db->isInterfaceSupported('describe') && $this->useTable !== false) {
$this->_tableInfo = new Set($db->describe($this, $clear));
$this->_schema = new Set($db->describe($this, $clear));
} elseif ($this->useTable === false) {
$this->_tableInfo = new Set();
$this->_schema = new Set();
}
}
return $this->_schema;
}
/**
* See Model::schema
*
* @deprecated
*/
function loadInfo($clear = false) {
$info = $this->schema($clear);
foreach($info->value as $field => $value) {
$fields[] = am(array('name'=> $field), $value);
}
unset($info);
$this->_tableInfo = new Set($fields);
return $this->_tableInfo;
}
/**

390
cake/libs/model/schema.php Normal file
View file

@ -0,0 +1,390 @@
<?php
/* SVN FILE: $Id$ */
/**
* Schema database management for CakePHP.
*
* PHP versions 4 and 5
*
* CakePHP(tm) : Rapid Development Framework <http://www.cakephp.org/>
* Copyright 2005-2007, Cake Software Foundation, Inc.
* 1785 E. Sahara Avenue, Suite 490-204
* Las Vegas, Nevada 89104
*
* Licensed under The MIT License
* Redistributions of files must retain the above copyright notice.
*
* @filesource
* @copyright Copyright 2005-2007, Cake Software Foundation, Inc.
* @link http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
* @package cake
* @subpackage cake.cake.libs
* @since CakePHP(tm) v 1.2.0.5550
* @version $Revision$
* @modifiedby $LastChangedBy$
* @lastmodified $Date$
* @license http://www.opensource.org/licenses/mit-license.php The MIT License
*/
if (!class_exists('connectionmanager')) {
uses('model' . DS . 'connection_manager');
}
/**
* Base Class for Schema management
*
* @package cake.libs
* @subpackage cake.libs
*/
class CakeSchema extends Object {
/**
* name of the App Schema
*
* @var string
* @access public
*/
var $name = null;
/**
* path to write location
*
* @var string
* @access public
*/
var $path = TMP;
/**
* connection used for read
*
* @var string
* @access public
*/
var $connection = 'default';
/**
* array of tables
*
* @var array
* @access public
*/
var $tables = array();
/**
* Constructor
*
* @param array $data optional load object properties
* @access private
*/
function __construct($data = array()) {
$this->path = CONFIGS . 'sql';
$data = am(get_object_vars($this), $data);
$this->_build($data);
if (empty($this->name)) {
$this->name = preg_replace('/schema$/i', '', get_class($this));
}
parent::__construct();
}
/**
* Builds schema object properties
*
* @param array $data loaded object properties
* @access protected
*/
function _build($data) {
foreach ($data as $key => $val) {
if (!in_array($key, array('name', 'path', 'connection', 'tables', '_log'))) {
$this->tables[$key] = $val;
unset($this->{$key});
} elseif ($key != 'tables' && !empty($val)) {
$this->{$key} = $val;
}
}
}
/**
* Reads database and creates schema tables
*
* @param array $options schema object properties
* @access public
* @return array $name, $tables
*/
function load($options = array()) {
if (is_string($options)) {
$options = array('path'=> $options);
}
if (!isset($options['name'])) {
$options['name'] = Inflector::camelize(Configure::read('App.dir'));
}
$options = am(
get_object_vars($this), $options
);
extract($options);
if (file_exists($path . DS . 'schema.php')) {
require_once($path . DS . 'schema.php');
$class = $name .'Schema';
if(class_exists($class)) {
$Schema =& new $class();
$this->_build($options);
return $Schema;
}
}
return false;
}
/**
* Reads database and creates schema tables
*
* @param array $options schema object properties
* @access public
* @return array $name, $tables
*/
function read($options = array()) {
extract(am(
array(
'connection' => $this->connection,
'name' => Inflector::camelize(Configure::read('App.dir')),
),
$options
));
$db =& ConnectionManager::getDataSource($connection);
if (empty($models)) {
$models = Configure::listObjects('model');
}
$tables = array();
foreach ($models as $model) {
if (!class_exists($model)) {
loadModel($model);
}
$Object =& new $model();
$Object->setDataSource($connection);
if (is_object($Object)) {
if(empty($tables[$Object->table])) {
$tables[$Object->table] = $this->__columns($Object);
$tables[$Object->table]['indexes'] = $db->index($Object);
if(!empty($Object->hasAndBelongsToMany)) {
foreach($Object->hasAndBelongsToMany as $Assoc => $assocData) {
$tables[$Object->$Assoc->table] = $this->__columns($Object->$Assoc);
$tables[$Object->$Assoc->table]['indexes'] = $db->index($Object->$Assoc);
}
}
}
} else {
trigger_error('Schema generation error: model class ' . $class . ' not found', E_USER_WARNING);
}
}
return compact('name', 'tables');
}
/**
* Writes schema file from object or options
*
* @param mixed $object schema object or options array
* @param array $options schema object properties to override object
* @access public
* @return mixed false or string written to file
*/
function write($object, $options = array()) {
if (is_object($object)) {
$object = get_object_vars($object);
$this->_build($object);
}
if (is_array($object)) {
$options = $object;
unset($object);
}
extract(am(
get_object_vars($this), $options
));
$out = "\n\nclass {$name}Schema extends CakeSchema {\n\n";
$out .= "\tvar \$name = '{$name}';\n\n";
if ($path !== $this->path) {
$out .= "\tvar \$path = '{$path}';\n\n";
}
if ($connection !== 'default') {
$out .= "\tvar \$connection = '{$connection}';\n\n";
}
if(empty($tables)) {
$this->read();
}
foreach ($tables as $table => $fields) {
if(!is_numeric($table)) {
$out .= "\tvar \${$table} = array(\n";
if (is_array($fields)) {
$cols = array();
foreach ($fields as $field => $value) {
if($field != 'indexes') {
if (is_string($value)) {
$type = $value;
$value = array('type'=> $type);
}
$col = "\t\t\t'{$field}' => array('type'=>'" . $value['type'] . "', ";
unset($value['type']);
$col .= join(', ', $this->__values($value));
} else {
$col = "\t\t\t'indexes' => array(";
$props = array();
foreach ($value as $key => $index) {
$props[] = "'{$key}' => array(".join(', ', $this->__values($index)).")";
}
$col .= join(', ', $props);
}
$col .= ")";
$cols[] = $col;
}
$out .= join(",\n", $cols);
}
$out .= "\n\t\t);\n";
$out .="\n";
}
}
$out .= "\n\tfunction setup(\$version) {\n\t}\n\n\tfunction teardown(\$version) {\n";
$out .= "\t}\n}\n\n";
$File =& new File($path . DS . 'schema.php', true);
$content = "<?php \n/*<!--". $name ." schema generated on: " . date('Y-m-d H:m:s') . " : ". time() . "-->*/\n{$out}?>";
if ($File->write($content)) {
return $content;
}
return false;
}
/**
* Writes schema file from object or options
*
* @param mixed $old schema object or array
* @param mixed $new schema object or array
* @access public
* @return array $add, $drop, $change
*/
function compare($old, $new = null) {
if (empty($new)) {
$new = $this;
}
if (is_array($new)) {
if (isset($new['tables'])) {
$new = $new['tables'];
}
} else {
$new = $new->tables;
}
if (is_array($old)) {
if (isset($old['tables'])) {
$old = $old['tables'];
}
} else {
$old = $old->tables;
}
$tables = array();
foreach ($new as $table => $fields) {
if (!array_key_exists($table, $old)) {
$tables[$table]['add'] = $fields;
} else {
$diff = array_diff_assoc($fields, $old[$table]);
if (!empty($diff)) {
$tables[$table]['add'] = $diff;
}
$diff = array_diff_assoc($old[$table], $fields);
if (!empty($diff)) {
$tables[$table]['drop'] = $diff;
}
}
foreach ($fields as $field => $value) {
if (isset($old[$table][$field])) {
$diff = array_diff($value, $old[$table][$field]);
if (!empty($diff)) {
$tables[$table]['change'][$field] = am($old[$table][$field], $diff);
}
}
if (isset($add[$table][$field])) {
$wrapper = array_keys($fields);
if ($column = array_search($field, $wrapper)) {
if (isset($wrapper[$column - 1])) {
$tables[$table]['add'][$field]['after'] = $wrapper[$column - 1];
}
}
}
}
}
return $tables;
}
/**
* Formats Schema columns from Model Object
*
* @param array $value options keys(type, null, default, key, length, extra)
* @access public
* @return array $name, $tables
*/
function __values($values) {
$vals = array();
if(is_array($values)) {
foreach ($values as $key => $val) {
if(is_array($val)) {
$vals[] = "'{$key}' => array('".join("', '", $val)."')";
} else if (!is_numeric($key)) {
$prop = "'{$key}' => ";
if (is_bool($val)) {
$prop .= $val ? 'true' : 'false';
} elseif (is_numeric($val)) {
$prop .= $val;
} elseif ($val === null) {
$prop .= 'null';
} else {
$prop .= "'{$val}'";
}
$vals[] = $prop;
}
}
}
return $vals;
}
/**
* Formats Schema columns from Model Object
*
* @param array $Obj model object
* @access public
* @return array $name, $tables
*/
function __columns(&$Obj) {
$db =& ConnectionManager::getDataSource($Obj->useDbConfig);
$fields = $Obj->schema(true);
$columns = $props = array();
foreach ($fields->value as $name => $value) {
if ($Obj->primaryKey == $name) {
$value['key'] = 'primary';
}
if (!isset($db->columns[$value['type']])) {
trigger_error('Schema generation error: invalid column type ' . $value['type'] . ' does not exist in DBO', E_USER_WARNING);
continue;
} else {
$defaultCol = $db->columns[$value['type']];
if (isset($defaultCol['limit']) && $defaultCol['limit'] == $value['length']) {
unset($value['length']);
} elseif (isset($defaultCol['length']) && $defaultCol['length'] == $value['length']) {
unset($value['length']);
}
unset($value['limit']);
}
if (empty($value['default']) && $value['null'] == true) {
unset($value['default']);
}
if (empty($value['length'])) {
unset($value['length']);
}
if (empty($value['key'])) {
unset($value['key']);
}
if (empty($value['extra'])) {
unset($value['extra']);
}
$columns[$name] = $value;
}
return $columns;
}
}
?>

View file

@ -39,6 +39,7 @@ uses('controller'.DS.'components'.DS.'acl', 'model'.DS.'db_acl');
if(!class_exists('aclnodetestbase')) {
class AclNodeTestBase extends AclNode {
var $useDbConfig = 'test_suite';
var $cacheSources = false;
}
}

View file

@ -39,6 +39,7 @@ uses('controller'.DS.'components'.DS.'acl', 'model'.DS.'db_acl');
if(!class_exists('aclnodetestbase')) {
class AclNodeTestBase extends AclNode {
var $useDbConfig = 'test_suite';
var $cacheSources = false;
}
}
@ -329,41 +330,49 @@ class AuthTest extends CakeTestCase {
$this->Controller->Session->del('Auth');
//$this->Controller->Acl->Aro->execute('truncate aros;');
//$this->Controller->Acl->Aro->execute('truncate acos;');
//$this->Controller->Acl->Aro->execute('truncate aros_acos;');
}
function testLoginRedirect() {
$backup = $_SERVER['HTTP_REFERER'];
$_SERVER['HTTP_REFERER'] = false;
$this->Controller->data = array();
$this->Controller->Auth->loginRedirect = array('controller' => 'pages', 'action' => 'display', 'welcome');
$this->Controller->Session->write('Auth', array('AuthUser' => array('id'=>'1', 'username'=>'nate')));
$this->Controller->params['url']['url'] = 'users/login';
$this->Controller->Auth->initialize($this->Controller);
$this->Controller->Auth->userModel = 'AuthUser';
$this->Controller->Auth->loginRedirect = array('controller' => 'pages', 'action' => 'display', 'welcome');
$this->Controller->Auth->startup($this->Controller);
$expected = $this->Controller->Auth->_normalizeURL($this->Controller->Auth->loginRedirect);
$this->assertEqual($expected, $this->Controller->Auth->redirect());
$this->Controller->Session->del('Auth');
$this->Controller->Session->del('Auth');
$this->Controller->params['url']['url'] = 'admin/';
$this->Controller->Auth->initialize($this->Controller);
$this->Controller->Auth->userModel = 'AuthUser';
$this->Controller->Auth->loginRedirect = null;
$this->Controller->Auth->startup($this->Controller);
$expected = $this->Controller->Auth->_normalizeURL('admin/');
$this->assertEqual($expected, $this->Controller->Auth->redirect());
$this->Controller->Session->del('Auth');
$this->Controller->Session->del('Auth');
$_SERVER['HTTP_REFERER'] = '/admin/';
$this->Controller->Session->write('Auth', array('AuthUser' => array('id'=>'1', 'username'=>'nate')));
$this->Controller->params['url']['url'] = 'users/login';
$this->Controller->Auth->initialize($this->Controller);
$this->Controller->Auth->userModel = 'AuthUser';
$this->Controller->Auth->loginRedirect = null;
$this->Controller->Auth->startup($this->Controller);
$expected = '/admin/';
$expected = $this->Controller->Auth->_normalizeURL('admin');
$this->assertEqual($expected, $this->Controller->Auth->redirect());
$this->Controller->Session->del('Auth');
$_SERVER['HTTP_REFERER'] = $backup;
$this->Controller->Session->del('Auth');
}
function testEmptyUsernameOrPassword() {

View file

@ -69,7 +69,7 @@ class NumberTree extends CakeTestModel {
}
class NumberTreeCase extends CakeTestCase {
var $fixtures = array('number_tree');
var $fixtures = array('core.number_tree');
function testInitialize() {
$this->NumberTree = & new NumberTree();
@ -721,5 +721,9 @@ class NumberTreeCase extends CakeTestCase {
array('NumberTree' => array('name' => '1.2.2')));
$this->assertIdentical($result, $expects);
}
function tearDown() {
unset($this->NumberTree);
}
}
?>

View file

@ -41,6 +41,7 @@ uses('controller'.DS.'components'.DS.'acl', 'model'.DS.'db_acl');
if(!class_exists('aclnodetestbase')) {
class AclNodeTestBase extends AclNode {
var $useDbConfig = 'test_suite';
var $cacheSources = false;
}
}
@ -131,6 +132,45 @@ if(!class_exists('db_acl_test')) {
function testNodeNesting() {
}
function testNode(){
$aco = new AcoTest();
$result = Set::extract($aco->node('Controller1'), '{n}.AcoTest.id');
$expected = array(2, 1);
$this->assertEqual($result, $expected);
$result = Set::extract($aco->node('Controller1/action1'), '{n}.AcoTest.id');
$expected = array(3, 2, 1);
$this->assertEqual($result, $expected);
$result = Set::extract($aco->node('Controller2/action1'), '{n}.AcoTest.id');
$expected = array(7, 6, 1);
$this->assertEqual($result, $expected);
$result = Set::extract($aco->node('Controller1/action2'), '{n}.AcoTest.id');
$expected = array(5, 2, 1);
$this->assertEqual($result, $expected);
$result = Set::extract($aco->node('Controller1/action1/record1'), '{n}.AcoTest.id');
$expected = array(4, 3, 2, 1);
$this->assertEqual($result, $expected);
$result = Set::extract($aco->node('Controller2/action1/record1'), '{n}.AcoTest.id');
$expected = array(8, 7, 6, 1);
$this->assertEqual($result, $expected);
//action3 is an action with no ACO entry
//the default returned ACOs should be its parents
$result = Set::extract($aco->node('Controller2/action3'), '{n}.AcoTest.id');
$expected = array(6, 1);
$this->assertEqual($result, $expected);
//action3 and record5 have none ACO entry
//the default returned ACOs should be their parents ACO
$result = Set::extract($aco->node('Controller2/action3/record5'), '{n}.AcoTest.id');
$expected = array(6, 1);
$this->assertEqual($result, $expected);
}
}
?>

View file

@ -48,7 +48,7 @@ class Test extends Model {
function loadInfo() {
return new Set(array(
array('name' => 'id', 'type' => 'integer', 'null' => '', 'default' => '1', 'length' => '8'),
array('name' => 'id', 'type' => 'integer', 'null' => '', 'default' => '1', 'length' => '8', 'key'=>'primary'),
array('name' => 'name', 'type' => 'string', 'null' => '', 'default' => '', 'length' => '255'),
array('name' => 'email', 'type' => 'string', 'null' => '1', 'default' => '', 'length' => '155'),
array('name' => 'notes', 'type' => 'text', 'null' => '1', 'default' => 'write some notes here', 'length' => ''),
@ -56,6 +56,17 @@ class Test extends Model {
array('name' => 'updated', 'type' => 'datetime', 'null' => '1', 'default' => '', 'length' => null)
));
}
function schema() {
return new Set(array(
'id'=> array('type' => 'integer', 'null' => '', 'default' => '1', 'length' => '8', 'key'=>'primary'),
'name'=> array('type' => 'string', 'null' => '', 'default' => '', 'length' => '255'),
'email'=> array('type' => 'string', 'null' => '1', 'default' => '', 'length' => '155'),
'notes'=> array('type' => 'text', 'null' => '1', 'default' => 'write some notes here', 'length' => ''),
'created'=> array('type' => 'date', 'null' => '1', 'default' => '', 'length' => ''),
'updated'=> array('type' => 'datetime', 'null' => '1', 'default' => '', 'length' => null)
));
}
}
/**
@ -174,6 +185,24 @@ class Featured extends CakeTestModel {
class Tag extends CakeTestModel {
var $name = 'Tag';
}
/**
* Short description for class.
*
* @package cake.tests
* @subpackage cake.tests.cases.libs.model
*/
class ArticleTag extends CakeTestModel {
var $name = 'ArticleTag';
}
/**
* Short description for class.
*
* @package cake.tests
* @subpackage cake.tests.cases.libs.model
*/
class ArticleFeaturedTag extends CakeTestModel {
var $name = 'ArticleFeaturedTag';
}
/**
* Short description for class.
*
@ -354,7 +383,7 @@ class ModelTest extends CakeTestCase {
var $fixtures = array(
'core.category', 'core.category_thread', 'core.user', 'core.article', 'core.featured', 'core.article_featureds_tags',
'core.article_featured', 'core.tag', 'core.articles_tag', 'core.comment', 'core.attachment',
'core.article_featured', 'core.articles', 'core.tag', 'core.articles_tag', 'core.comment', 'core.attachment',
'core.apple', 'core.sample', 'core.another_article', 'core.advertisement', 'core.home', 'core.post', 'core.author',
'core.project', 'core.thread', 'core.message', 'core.bid'
);
@ -430,9 +459,7 @@ class ModelTest extends CakeTestCase {
'Thread' => array()
)
);
$this->assertEqual($result, $expected);
unset($this->Project);
}
@ -521,7 +548,7 @@ class ModelTest extends CakeTestCase {
}
$expected = array(
array('name' => 'id', 'type' => 'integer', 'null' => false, 'default' => null, 'length' => $intLength),
array('name' => 'id', 'type' => 'integer', 'null' => false, 'default' => null, 'length' => $intLength, 'key' => 'primary', 'extra' => 'auto_increment'),
array('name' => 'user', 'type' => 'string', 'null' => false, 'default' => '', 'length' => 255),
array('name' => 'password', 'type' => 'string', 'null' => false, 'default' => '', 'length' => 255),
array('name' => 'created', 'type' => 'datetime', 'null' => true, 'default' => null, 'length' => null),

View file

@ -0,0 +1,185 @@
<?php
/* SVN FILE: $Id$ */
/**
* Test for Schema database management
*
*
* PHP versions 4 and 5
*
* CakePHP(tm) Tests <https://trac.cakephp.org/wiki/Developement/TestSuite>
* Copyright 2005-2007, Cake Software Foundation, Inc.
* 1785 E. Sahara Avenue, Suite 490-204
* Las Vegas, Nevada 89104
*
* Licensed under The Open Group Test Suite License
* Redistributions of files must retain the above copyright notice.
*
* @filesource
* @copyright Copyright 2005-2007, Cake Software Foundation, Inc.
* @link https://trac.cakephp.org/wiki/Developement/TestSuite CakePHP(tm) Tests
* @package cake.tests
* @subpackage cake.tests.cases.libs
* @since CakePHP(tm) v 1.2.0.5550
* @version $Revision$
* @modifiedby $LastChangedBy$
* @lastmodified $Date$
* @license http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
*/
uses('model' . DS .'schema');
/**
* Test for Schema database management
*
* @package cake.tests
* @subpackage cake.tests.cases.libs
*/
class MyAppSchema extends CakeSchema {
var $name = 'MyApp';
var $connection = 'test_suite';
var $posts = array(
'id' => array('type'=>'integer', 'null' => false, 'default' => null, 'key' => 'primary', 'extra'=> 'auto_increment'),
'author_id' => array('type'=>'integer', 'null' => false, 'default' => ''),
'title' => array('type'=>'string', 'null' => false, 'default' => 'Title'),
'summary' => array('type'=>'text', 'null' => true),
'body' => array('type'=>'text', 'null' => true),
'published' => array('type'=>'string', 'null' => true, 'default' => 'Y', 'length' => 1),
'created' => array('type'=>'datetime', 'null' => true),
'updated' => array('type'=>'datetime', 'null' => true),
'indexes' => array('PRIMARY'=>array('column'=>'id', 'unique' => true)),
);
var $comments = array(
'id' => array('type'=>'integer', 'null' => false, 'default' => null, 'key' => 'primary', 'extra'=> 'auto_increment'),
'post_id' => array('type'=>'integer', 'null' => false, 'default' => ''),
'user_id' => array('type'=>'integer', 'null' => false, 'default' => ''),
'title' => array('type'=>'string', 'null' => false, 'length' => 100),
'comment' => array('type'=>'text', 'null' => false),
'published' => array('type'=>'string', 'null' => true, 'default' => 'N', 'length' => 1),
'created' => array('type'=>'datetime', 'null' => true),
'updated' => array('type'=>'datetime', 'null' => true),
'indexes' => array('PRIMARY'=>array('column'=>'id', 'unique' => true)),
);
function setup($version) {
}
function teardown($version) {
}
}
class TestAppSchema extends CakeSchema {
var $name = 'MyApp';
var $posts = array(
'id' => array('type'=>'integer', 'null' => false, 'default' => null, 'key' => 'primary', 'extra'=> 'auto_increment'),
'author_id' => array('type'=>'integer', 'null' => false, 'default' => ''),
'title' => array('type'=>'string', 'null' => false, 'default' => ''),
'body' => array('type'=>'text', 'null' => true),
'published' => array('type'=>'string', 'null' => true, 'default' => 'N', 'length' => 1),
'created' => array('type'=>'datetime', 'null' => true),
'updated' => array('type'=>'datetime', 'null' => true),
'indexes' => array('PRIMARY'=>array('column'=>'id', 'unique' => true)),
);
var $comments = array(
'id' => array('type'=>'integer', 'null' => false, 'default' => null, 'key' => 'primary', 'extra'=> 'auto_increment'),
'article_id' => array('type'=>'integer', 'null' => false, 'default' => ''),
'user_id' => array('type'=>'integer', 'null' => false, 'default' => ''),
'comment' => array('type'=>'text', 'null' => true),
'published' => array('type'=>'string', 'null' => true, 'default' => 'N', 'length' => 1),
'created' => array('type'=>'datetime', 'null' => true),
'updated' => array('type'=>'datetime', 'null' => true),
'indexes' => array('PRIMARY'=>array('column'=>'id', 'unique' => true)),
);
function setup($version) {
}
function teardown($version) {
}
}
/**
* Short description for class.
*
* @package cake.tests
* @subpackage cake.tests.cases.libs.model
*/
class SchemaPost extends CakeTestModel {
var $name = 'SchemaPost';
//var $useTable = 'posts';
var $hasMany = array('SchemaComment');
}
/**
* Short description for class.
*
* @package cake.tests
* @subpackage cake.tests.cases.libs.model
*/
class SchemaComment extends CakeTestModel {
var $name = 'SchemaComment';
//var $useTable = 'comments';
var $belongsTo = array('SchemaPost');
}
/**
* Short description for class.
*
* @package cake.tests
* @subpackage cake.tests.cases.libs
*/
class CakeSchemaTest extends CakeTestCase {
var $fixtures = array('core.post', 'core.comment', 'core.author');
function setUp() {
$this->Schema = new TestAppSchema();
}
function testSchemaGeneration() {
$read = $this->Schema->read(array('connection'=>'test_suite', 'name'=>'TestApp', 'models'=>array('post', 'comment')));
$this->assertEqual($read['tables'], $this->Schema->tables);
$write = $this->Schema->write(array('name'=>'MyOtherApp', 'tables'=> $read['tables'], 'path'=> TMP . 'tests'));
$file = file_get_contents(TMP . 'tests' . DS .'schema.php');
$this->assertEqual($write, $file);
require_once( TMP . 'tests' . DS .'schema.php');
$OtherSchema = new MyOtherAppSchema();
$this->assertEqual($read['tables'], $OtherSchema->tables);
}
function testSchemaComparison() {
$New = new MyAppSchema();
$compare = $New->compare($this->Schema);
$expected = array(
'posts'=> array(
'add'=> array('summary'=>array('type'=> 'text', 'null'=> 1)),
'change'=> array('title'=>array('type'=>'string', 'null'=> false, 'default'=> 'Title'), 'published'=>array('type'=>'string', 'null'=> true, 'default'=>'Y', 'length'=> '1')),
),
'comments'=> array(
'add'=>array('post_id'=>array('type'=> 'integer', 'null'=> false, 'default'=>''), 'title'=>array('type'=> 'string', 'null'=> false, 'length'=> 100)),
'drop'=>array('article_id'=>array('type'=> 'integer', 'null'=> false, 'default'=>'')),
'change'=>array('comment'=>array('type'=>'text', 'null'=> false))
),
);
$this->assertEqual($expected, $compare);
}
function testSchemaLoading() {
$Other = $this->Schema->load(array('name'=>'MyOtherApp', 'path'=> TMP . 'tests'));
$this->assertEqual($Other->name, 'MyOtherApp');
$this->assertEqual($Other->tables, $this->Schema->tables);
}
function tearDown() {
unset($this->Schema);
}
}
?>

View file

@ -44,6 +44,15 @@ class AcoFixture extends CakeTestFixture {
'rght' => array('type' => 'integer', 'length' => 10, 'null' => true)
);
var $records = array(
array ('id' => 1, 'parent_id' => null, 'model' => '', 'foreign_key' => '', 'alias' => 'ROOT', 'lft' => 1, 'rght' => 18),
array ('id' => 2, 'parent_id' => 1, 'model' => '', 'foreign_key' => '', 'alias' => 'Controller1', 'lft' => 2, 'rght' => 9),
array ('id' => 3, 'parent_id' => 2, 'model' => '', 'foreign_key' => '', 'alias' => 'action1', 'lft' => 3, 'rght' => 6),
array ('id' => 4, 'parent_id' => 3, 'model' => '', 'foreign_key' => '', 'alias' => 'record1', 'lft' => 4, 'rght' => 5),
array ('id' => 5, 'parent_id' => 2, 'model' => '', 'foreign_key' => '', 'alias' => 'action2', 'lft' => 7, 'rght' => 8),
array ('id' => 6, 'parent_id' => 1, 'model' => '', 'foreign_key' => '', 'alias' => 'Controller2', 'lft' => 10, 'rght' => 17),
array ('id' => 7, 'parent_id' => 6, 'model' => '', 'foreign_key' => '', 'alias' => 'action1', 'lft' => 11, 'rght' => 14),
array ('id' => 8, 'parent_id' => 7, 'model' => '', 'foreign_key' => '', 'alias' => 'record1', 'lft' => 12, 'rght' => 13),
array ('id' => 9, 'parent_id' => 6, 'model' => '', 'foreign_key' => '', 'alias' => 'action2', 'lft' => 15, 'rght' => 16),
);
}

View file

@ -34,11 +34,11 @@
*/
class ArticleFeaturedsTagsFixture extends CakeTestFixture {
var $name = 'ArticleFeaturedsTags';
var $primaryKey = array('article_featured_id', 'tag_id');
var $fields = array(
'article_featured_id' => array('type' => 'integer', 'null' => false),
'tag_id' => array('type' => 'integer', 'null' => false),
'indexes' => array('UNIQUE_FEATURED' => array('column'=> array('article_featured_id', 'tag_id'), 'unique'=> 1))
);
}

View file

@ -37,8 +37,8 @@ class ArticlesTagFixture extends CakeTestFixture {
var $fields = array(
'article_id' => array('type' => 'integer', 'null' => false),
'tag_id' => array('type' => 'integer', 'null' => false),
'indexes' => array('UNIQUE_TAG' => array('column'=> array('article_id', 'tag_id'), 'unique'=>1))
);
var $primaryKey = array('article_id', 'tag_id');
var $records = array(
array('article_id' => 1, 'tag_id' => 1),
array('article_id' => 1, 'tag_id' => 2),

View file

@ -181,10 +181,9 @@ class CakeTestCase extends UnitTestCase {
foreach ($models as $model) {
$object =& $classRegistry->getObject($model['key']);
if ($object !== false) {
$object->useDbConfig = 'test_suite';
$object->setDataSource();
$object->setDataSource('test_suite');
$object->cacheSources = false;
}
}
}
@ -336,7 +335,6 @@ class CakeTestCase extends UnitTestCase {
if (isset($this->_fixtures) && isset($this->db)) {
foreach ($this->_fixtures as $fixture) {
$query = $fixture->create();
if (isset($query) && $query !== false) {
$this->db->_execute($query);
}
@ -352,7 +350,6 @@ class CakeTestCase extends UnitTestCase {
if (isset($this->_fixtures) && isset($this->db)) {
foreach (array_reverse($this->_fixtures) as $fixture) {
$query = $fixture->drop();
if (isset($query) && $query !== false) {
$this->db->_execute($query);
}
@ -370,7 +367,6 @@ class CakeTestCase extends UnitTestCase {
if (isset($this->_fixtures) && isset($this->db) && !in_array(low($method), array('start', 'end'))) {
foreach ($this->_fixtures as $fixture) {
$query = $fixture->truncate();
if (isset($query) && $query !== false) {
$this->db->_execute($query);
}
@ -393,9 +389,7 @@ class CakeTestCase extends UnitTestCase {
*/
function getTests() {
$methods = array_diff(parent::getTests(), array('testAction', 'testaction'));
$methods = am(am(array('start', 'startCase'), $methods), array('endCase', 'end'));
return $methods;
}
/**
@ -433,6 +427,7 @@ class CakeTestCase extends UnitTestCase {
// Get db connection
$this->db =& ConnectionManager::getDataSource('test_suite');
$this->db->cacheSources = false;
$this->db->fullDebug = false;
}
/**

View file

@ -44,12 +44,17 @@ class CakeTestFixture extends Object {
function __construct(&$db) {
$this->db =& $db;
$this->init();
if(!class_exists('cakeschema')) {
uses('model' . DS .'schema');
}
$this->Schema = new CakeSchema(array('name'=>'TestSuite', 'connection'=>'test_suite'));
}
/**
* Initialize the fixture.
*
*/
function init() {
if (isset($this->import) && (is_string($this->import) || is_array($this->import))) {
$import = array();
@ -63,33 +68,29 @@ class CakeTestFixture extends Object {
if (isset($import['model']) && (class_exists($import['model']) || loadModel($import['model']))) {
$model =& new $import['model'];
$modelDb =& ConnectionManager::getDataSource($model->useDbConfig);
$info = $model->loadInfo();
$this->fields = array_combine(Set::extract($info->value, '{n}.name'), $info->value);
$db =& ConnectionManager::getDataSource($model->useDbConfig);
$db->cacheSources = false;
$this->table = $this->useTable;
$schema = $model->schema(true);
$this->fields = $schema->value;
$this->fields[$model->primaryKey]['key'] = 'primary';
$this->primaryKey = array( $model->primaryKey );
} elseif (isset($import['table'])) {
$model =& new stdClass();
$modelDb =& ConnectionManager::getDataSource($import['connection']);
$model =& new Model(null, $import['table'], $import['connection']);
$db =& ConnectionManager::getDataSource($import['connection']);
$db->cacheSources = false;
$model->name = Inflector::camelize(Inflector::singularize($import['table']));
$model->table = $import['table'];
$model->tablePrefix = $modelDb->config['prefix'];
$info = $modelDb->describe($model);
$this->fields = array_combine(Set::extract($info, '{n}.name'), $info);
$model->tablePrefix = $db->config['prefix'];
$schema = $model->schema(true);
$this->fields = $schema->value;
}
if ($import['records'] !== false && isset($model) && isset($modelDb)) {
if ($import['records'] !== false && isset($model) && isset($db)) {
$this->records = array();
$query = array(
'fields' => Set::extract($this->fields, '{n}.name'),
'table' => $modelDb->name($model->table),
'fields' => array_keys($this->fields),
'table' => $db->name($model->table),
'alias' => $model->name,
'conditions' => array(),
'order' => null,
@ -97,10 +98,10 @@ class CakeTestFixture extends Object {
);
foreach ($query['fields'] as $index => $field) {
$query['fields'][$index] = $modelDb->name($query['alias']) . '.' . $modelDb->name($field);
$query['fields'][$index] = $db->name($query['alias']) . '.' . $db->name($field);
}
$records = $modelDb->fetchAll($modelDb->buildStatement($query, $model), false, $model->name);
$records = $db->fetchAll($db->buildStatement($query, $model), false, $model->name);
if ($records !== false && !empty($records)) {
$this->records = Set::extract($records, '{n}.' . $model->name);
@ -116,14 +117,6 @@ class CakeTestFixture extends Object {
$this->primaryKey = 'id';
}
if (isset($this->primaryKey) && !is_array($this->primaryKey)) {
$this->primaryKey = array( $this->primaryKey );
}
if (isset($this->primaryKey) && isset($this->fields[$this->primaryKey[0]])) {
$this->fields[$this->primaryKey[0]]['key'] = 'primary';
}
if (isset($this->fields)) {
foreach ($this->fields as $index => $field) {
if (empty($field['default'])) {
@ -144,43 +137,9 @@ class CakeTestFixture extends Object {
if (!isset($this->fields) || empty($this->fields)) {
return null;
}
$create = 'CREATE TABLE ' . $this->db->name($this->db->config['prefix'] . $this->table) . ' (' . "\n";
foreach ($this->fields as $field => $attributes) {
if (!is_array($attributes)) {
$attributes = array('type' => $attributes);
} elseif (isset($attributes['key']) && low($attributes['key']) == 'primary' && !isset($this->primaryKey)) {
$this->primaryKey = array ( $field );
}
$column = array($field, $attributes['type']);
unset($attributes['type']);
if (!empty($attributes)) {
$column = array_merge($column, $attributes);
}
$create .= $this->db->generateColumnSchema($column) . ',' . "\n";
}
if (isset($this->primaryKey)) {
foreach ($this->primaryKey as $index => $field) {
$this->primaryKey[$index] = $this->db->name($field);
}
}
if (!isset($this->primaryKey)) {
$create = substr($create, 0, -1);
} else {
$create .= 'PRIMARY KEY(' . implode(', ', $this->primaryKey) . ')' . "\n";
}
$create .= ')';
$this->_create = $create;
$this->Schema->_build(array($this->table => $this->fields));
$this->_create = $this->db->createSchema($this->Schema);
}
return $this->_create;
}
/**
@ -192,9 +151,9 @@ class CakeTestFixture extends Object {
*/
function drop() {
if (!isset($this->_drop)) {
$this->_drop = 'DROP TABLE ' . $this->db->name($this->db->config['prefix'] . $this->table);
$this->Schema->_build(array($this->table => $this->fields));
$this->_drop = $this->db->dropSchema($this->Schema);
}
return $this->_drop;
}
/**