00001 <?php
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025 App::import('Model', 'ConnectionManager');
00026
00027
00028
00029
00030
00031
00032 class CakeSchema extends Object {
00033
00034
00035
00036
00037
00038
00039 var $name = null;
00040
00041
00042
00043
00044
00045
00046 var $path = null;
00047
00048
00049
00050
00051
00052
00053 var $file = 'schema.php';
00054
00055
00056
00057
00058
00059
00060 var $connection = 'default';
00061
00062
00063
00064
00065
00066
00067 var $tables = array();
00068
00069
00070
00071
00072
00073 function __construct($options = array()) {
00074 parent::__construct();
00075
00076 if (empty($options['name'])) {
00077 $this->name = preg_replace('/schema$/i', '', get_class($this));
00078 }
00079
00080 if (strtolower($this->name) === 'cake') {
00081 $this->name = Inflector::camelize(Inflector::slug(Configure::read('App.dir')));
00082 }
00083
00084 if (empty($options['path'])) {
00085 $this->path = CONFIGS . 'sql';
00086 }
00087
00088 $options = array_merge(get_object_vars($this), $options);
00089 $this->_build($options);
00090 }
00091
00092
00093
00094
00095
00096
00097
00098 function _build($data) {
00099 $file = null;
00100 foreach ($data as $key => $val) {
00101 if (!empty($val)) {
00102 if (!in_array($key, array('name', 'path', 'file', 'connection', 'tables', '_log'))) {
00103 $this->tables[$key] = $val;
00104 unset($this->{$key});
00105 } elseif ($key !== 'tables') {
00106 if ($key === 'name' && $val !== $this->name && !isset($data['file'])) {
00107 $file = Inflector::underscore($val) . '.php';
00108 }
00109 $this->{$key} = $val;
00110 }
00111 }
00112 }
00113
00114 if (file_exists($this->path . DS . $file) && is_file($this->path . DS . $file)) {
00115 $this->file = $file;
00116 }
00117 }
00118
00119
00120
00121
00122
00123
00124
00125 function before($event = array()) {
00126 return true;
00127 }
00128
00129
00130
00131
00132
00133
00134 function after($event = array()) {
00135 }
00136
00137
00138
00139
00140
00141
00142
00143 function load($options = array()) {
00144 if (is_string($options)) {
00145 $options = array('path' => $options);
00146 }
00147
00148 $this->_build($options);
00149 extract(get_object_vars($this));
00150
00151 $class = $name .'Schema';
00152 if (!class_exists($class)) {
00153 if (file_exists($path . DS . $file) && is_file($path . DS . $file)) {
00154 require_once($path . DS . $file);
00155 } elseif (file_exists($path . DS . 'schema.php') && is_file($path . DS . 'schema.php')) {
00156 require_once($path . DS . 'schema.php');
00157 }
00158 }
00159
00160 if (class_exists($class)) {
00161 $Schema =& new $class($options);
00162 return $Schema;
00163 }
00164
00165 return false;
00166 }
00167
00168
00169
00170
00171
00172
00173
00174
00175
00176
00177
00178
00179 function read($options = array()) {
00180 extract(array_merge(
00181 array(
00182 'connection' => $this->connection,
00183 'name' => $this->name,
00184 'models' => true,
00185 ),
00186 $options
00187 ));
00188 $db =& ConnectionManager::getDataSource($connection);
00189
00190 App::import('Model', 'AppModel');
00191
00192 $tables = array();
00193 $currentTables = $db->listSources();
00194
00195 $prefix = null;
00196 if (isset($db->config['prefix'])) {
00197 $prefix = $db->config['prefix'];
00198 }
00199
00200 if (!is_array($models) && $models !== false) {
00201 $models = Configure::listObjects('model');
00202 }
00203
00204 if (is_array($models)) {
00205 foreach ($models as $model) {
00206 if (PHP5) {
00207 $Object = ClassRegistry::init(array('class' => $model, 'ds' => $connection));
00208 } else {
00209 $Object =& ClassRegistry::init(array('class' => $model, 'ds' => $connection));
00210 }
00211
00212 if (is_object($Object) && $Object->useTable !== false) {
00213 $Object->setDataSource($connection);
00214 $table = $db->fullTableName($Object, false);
00215
00216 if (in_array($table, $currentTables)) {
00217 $key = array_search($table, $currentTables);
00218 if (empty($tables[$Object->table])) {
00219 $tables[$Object->table] = $this->__columns($Object);
00220 $tables[$Object->table]['indexes'] = $db->index($Object);
00221 unset($currentTables[$key]);
00222 }
00223 if (!empty($Object->hasAndBelongsToMany)) {
00224 foreach ($Object->hasAndBelongsToMany as $Assoc => $assocData) {
00225 if (isset($assocData['with'])) {
00226 $class = $assocData['with'];
00227 } elseif ($assocData['_with']) {
00228 $class = $assocData['_with'];
00229 }
00230 if (is_object($Object->$class)) {
00231 $table = $db->fullTableName($Object->$class, false);
00232 if (in_array($table, $currentTables)) {
00233 $key = array_search($table, $currentTables);
00234 $tables[$Object->$class->table] = $this->__columns($Object->$class);
00235 $tables[$Object->$class->table]['indexes'] = $db->index($Object->$class);
00236 unset($currentTables[$key]);
00237 }
00238 }
00239 }
00240 }
00241 }
00242 }
00243 }
00244 }
00245
00246 if (!empty($currentTables)) {
00247 foreach ($currentTables as $table) {
00248 if ($prefix) {
00249 if (strpos($table, $prefix) !== 0) {
00250 continue;
00251 }
00252 $table = str_replace($prefix, '', $table);
00253 }
00254 $Object = new AppModel(array(
00255 'name' => Inflector::classify($table), 'table' => $table, 'ds' => $connection
00256 ));
00257
00258 $systemTables = array(
00259 'aros', 'acos', 'aros_acos', Configure::read('Session.table'), 'i18n'
00260 );
00261
00262 if (in_array($table, $systemTables)) {
00263 $tables[$Object->table] = $this->__columns($Object);
00264 $tables[$Object->table]['indexes'] = $db->index($Object);
00265 } elseif ($models === false) {
00266 $tables[$table] = $this->__columns($Object);
00267 $tables[$table]['indexes'] = $db->index($Object);
00268 } else {
00269 $tables['missing'][$table] = $this->__columns($Object);
00270 $tables['missing'][$table]['indexes'] = $db->index($Object);
00271 }
00272 }
00273 }
00274
00275 ksort($tables);
00276 return compact('name', 'tables');
00277 }
00278
00279
00280
00281
00282
00283
00284
00285
00286 function write($object, $options = array()) {
00287 if (is_object($object)) {
00288 $object = get_object_vars($object);
00289 $this->_build($object);
00290 }
00291
00292 if (is_array($object)) {
00293 $options = $object;
00294 unset($object);
00295 }
00296
00297 extract(array_merge(
00298 get_object_vars($this), $options
00299 ));
00300
00301 $out = "class {$name}Schema extends CakeSchema {\n";
00302
00303 $out .= "\tvar \$name = '{$name}';\n\n";
00304
00305 if ($path !== $this->path) {
00306 $out .= "\tvar \$path = '{$path}';\n\n";
00307 }
00308
00309 if ($file !== $this->file) {
00310 $out .= "\tvar \$file = '{$file}';\n\n";
00311 }
00312
00313 if ($connection !== 'default') {
00314 $out .= "\tvar \$connection = '{$connection}';\n\n";
00315 }
00316
00317 $out .= "\tfunction before(\$event = array()) {\n\t\treturn true;\n\t}\n\n\tfunction after(\$event = array()) {\n\t}\n\n";
00318
00319 if (empty($tables)) {
00320 $this->read();
00321 }
00322
00323 foreach ($tables as $table => $fields) {
00324 if (!is_numeric($table) && $table !== 'missing') {
00325 $out .= "\tvar \${$table} = array(\n";
00326 if (is_array($fields)) {
00327 $cols = array();
00328 foreach ($fields as $field => $value) {
00329 if ($field != 'indexes') {
00330 if (is_string($value)) {
00331 $type = $value;
00332 $value = array('type'=> $type);
00333 }
00334 $col = "\t\t'{$field}' => array('type' => '" . $value['type'] . "', ";
00335 unset($value['type']);
00336 $col .= join(', ', $this->__values($value));
00337 } else {
00338 $col = "\t\t'indexes' => array(";
00339 $props = array();
00340 foreach ((array)$value as $key => $index) {
00341 $props[] = "'{$key}' => array(" . join(', ', $this->__values($index)) . ")";
00342 }
00343 $col .= join(', ', $props);
00344 }
00345 $col .= ")";
00346 $cols[] = $col;
00347 }
00348 $out .= join(",\n", $cols);
00349 }
00350 $out .= "\n\t);\n";
00351 }
00352 }
00353 $out .="}\n";
00354
00355
00356 $File =& new File($path . DS . $file, true);
00357 $header = '$Id';
00358 $content = "<?php \n/* SVN FILE: {$header}$ */\n/* {$name} schema generated on: " . date('Y-m-d H:m:s') . " : ". time() . "*/\n{$out}?>";
00359 $content = $File->prepare($content);
00360 if ($File->write($content)) {
00361 return $content;
00362 }
00363 return false;
00364 }
00365
00366
00367
00368
00369
00370
00371
00372
00373 function compare($old, $new = null) {
00374 if (empty($new)) {
00375 $new = $this;
00376 }
00377 if (is_array($new)) {
00378 if (isset($new['tables'])) {
00379 $new = $new['tables'];
00380 }
00381 } else {
00382 $new = $new->tables;
00383 }
00384
00385 if (is_array($old)) {
00386 if (isset($old['tables'])) {
00387 $old = $old['tables'];
00388 }
00389 } else {
00390 $old = $old->tables;
00391 }
00392 $tables = array();
00393 foreach ($new as $table => $fields) {
00394 if ($table == 'missing') {
00395 break;
00396 }
00397 if (!array_key_exists($table, $old)) {
00398 $tables[$table]['add'] = $fields;
00399 } else {
00400 $diff = array_diff_assoc($fields, $old[$table]);
00401 if (!empty($diff)) {
00402 $tables[$table]['add'] = $diff;
00403 }
00404 $diff = array_diff_assoc($old[$table], $fields);
00405 if (!empty($diff)) {
00406 $tables[$table]['drop'] = $diff;
00407 }
00408 }
00409 foreach ($fields as $field => $value) {
00410 if (isset($old[$table][$field])) {
00411 $diff = array_diff_assoc($value, $old[$table][$field]);
00412 if (!empty($diff) && $field !== 'indexes') {
00413 $tables[$table]['change'][$field] = array_merge($old[$table][$field], $diff);
00414 }
00415 }
00416
00417 if (isset($add[$table][$field])) {
00418 $wrapper = array_keys($fields);
00419 if ($column = array_search($field, $wrapper)) {
00420 if (isset($wrapper[$column - 1])) {
00421 $tables[$table]['add'][$field]['after'] = $wrapper[$column - 1];
00422 }
00423 }
00424 }
00425 }
00426
00427 if (isset($old[$table]['indexes']) && isset($new[$table]['indexes'])) {
00428 $diff = $this->_compareIndexes($new[$table]['indexes'], $old[$table]['indexes']);
00429 if ($diff) {
00430 $tables[$table]['drop']['indexes'] = $diff['drop'];
00431 $tables[$table]['add']['indexes'] = $diff['add'];
00432 }
00433 }
00434 }
00435 return $tables;
00436 }
00437
00438
00439
00440
00441
00442
00443
00444 function __values($values) {
00445 $vals = array();
00446 if (is_array($values)) {
00447 foreach ($values as $key => $val) {
00448 if (is_array($val)) {
00449 $vals[] = "'{$key}' => array('" . join("', '", $val) . "')";
00450 } else if (!is_numeric($key)) {
00451 $val = var_export($val, true);
00452 $vals[] = "'{$key}' => {$val}";
00453 }
00454 }
00455 }
00456 return $vals;
00457 }
00458
00459
00460
00461
00462
00463
00464
00465 function __columns(&$Obj) {
00466 $db =& ConnectionManager::getDataSource($Obj->useDbConfig);
00467 $fields = $Obj->schema(true);
00468 $columns = $props = array();
00469 foreach ($fields as $name => $value) {
00470 if ($Obj->primaryKey == $name) {
00471 $value['key'] = 'primary';
00472 }
00473 if (!isset($db->columns[$value['type']])) {
00474 trigger_error('Schema generation error: invalid column type ' . $value['type'] . ' does not exist in DBO', E_USER_NOTICE);
00475 continue;
00476 } else {
00477 $defaultCol = $db->columns[$value['type']];
00478 if (isset($defaultCol['limit']) && $defaultCol['limit'] == $value['length']) {
00479 unset($value['length']);
00480 } elseif (isset($defaultCol['length']) && $defaultCol['length'] == $value['length']) {
00481 unset($value['length']);
00482 }
00483 unset($value['limit']);
00484 }
00485
00486 if (isset($value['default']) && ($value['default'] === '' || $value['default'] === false)) {
00487 unset($value['default']);
00488 }
00489 if (empty($value['length'])) {
00490 unset($value['length']);
00491 }
00492 if (empty($value['key'])) {
00493 unset($value['key']);
00494 }
00495 $columns[$name] = $value;
00496 }
00497
00498 return $columns;
00499 }
00500
00501
00502
00503
00504
00505
00506
00507 function _compareIndexes($new, $old) {
00508 if (!is_array($new) || !is_array($old)) {
00509 return false;
00510 }
00511
00512 $add = $drop = array();
00513
00514 $diff = array_diff_assoc($new, $old);
00515 if (!empty($diff)) {
00516 $add = $diff;
00517 }
00518
00519 $diff = array_diff_assoc($old, $new);
00520 if (!empty($diff)) {
00521 $drop = $diff;
00522 }
00523
00524 foreach ($new as $name => $value) {
00525 if (isset($old[$name])) {
00526 $newUnique = isset($value['unique']) ? $value['unique'] : 0;
00527 $oldUnique = isset($old[$name]['unique']) ? $old[$name]['unique'] : 0;
00528 $newColumn = $value['column'];
00529 $oldColumn = $old[$name]['column'];
00530
00531 $diff = false;
00532
00533 if ($newUnique != $oldUnique) {
00534 $diff = true;
00535 } elseif (is_array($newColumn) && is_array($oldColumn)) {
00536 $diff = ($newColumn !== $oldColumn);
00537 } elseif (is_string($newColumn) && is_string($oldColumn)) {
00538 $diff = ($newColumn != $oldColumn);
00539 } else {
00540 $diff = true;
00541 }
00542 if ($diff) {
00543 $drop[$name] = null;
00544 $add[$name] = $value;
00545 }
00546 }
00547 }
00548 return array_filter(compact('add', 'drop'));
00549 }
00550 }
00551 ?>