| [ Index ] |
PHP Cross Reference of Akelos Framework |
[Summary view] [Print] [Text view]
1 <?php 2 /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */ 3 4 // +----------------------------------------------------------------------+ 5 // | Akelos Framework - http://www.akelos.org | 6 // +----------------------------------------------------------------------+ 7 // | Copyright (c) 2002-2006, Akelos Media, S.L. & Bermi Ferrer Martinez | 8 // | Released under the GNU Lesser General Public License, see LICENSE.txt| 9 // +----------------------------------------------------------------------+ 10 11 /** 12 * @package ActiveSupport 13 * @subpackage Installer 14 * @author Bermi Ferrer <bermi a.t akelos c.om> 15 * @copyright Copyright (c) 2002-2006, Akelos Media, S.L. http://www.akelos.org 16 * @license GNU Lesser General Public License <http://www.gnu.org/copyleft/lesser.html> 17 */ 18 19 require_once (AK_LIB_DIR.DS.'Ak.php'); 20 require_once (AK_LIB_DIR.DS.'AkActiveRecord.php'); 21 file_exists(AK_APP_DIR.DS.'shared_model.php') ? require_once(AK_APP_DIR.DS.'shared_model.php') : null; 22 defined('AK_APP_INSTALLERS_DIR') ? null : define('AK_APP_INSTALLERS_DIR', AK_APP_DIR.DS.'installers'); 23 24 defined('AK_VERBOSE_INSTALLER') ? null : define('AK_VERBOSE_INSTALLER', AK_DEV_MODE); 25 26 // Install scripts might use more RAM than normal requests. 27 @ini_set('memory_limit', -1); 28 29 /** 30 * 31 * == Column Types == 32 * 33 * Akelos natively supports the following column data types. 34 * 35 * integer|int, float, decimal, 36 * string, text, 37 * datetime|timestamp, date, 38 * binary, 39 * boolean 40 * 41 * Caution: Because boolean is virtual tinyint on mysql, you can't use tinyint for other things! 42 * 43 * 44 * == Default settings for columns == 45 * 46 * AkInstaller suggests some default values for the column-details. 47 * 48 * So 49 * <code> 50 * $this->createTable('Post','title,body,created_at,is_draft'); 51 * </code> 52 * 53 * will actually create something like this: 54 * 55 * title => string(255), body => text, created_at => datetime, is_draft => boolean not null default 0 index 56 * 57 * 58 * column_name | default setting 59 * -------------------------------+-------------------------------------------- 60 * id | integer not null auto_increment primary_key 61 * *_id,*_by | integer index 62 * description,content,body | text 63 * position | integer index 64 * *_count | integer default 0 65 * lock_version | integer default 1 66 * *_at | datetime 67 * *_on | date 68 * is_*,has_*,do_*,does_*,are_* | boolean not null default 0 index 69 * *somename | multilingual column => en_somename, es_somename 70 * default | string 71 * 72 */ 73 class AkInstaller 74 { 75 var $db; 76 var $data_dictionary; 77 var $available_tables = array(); 78 var $vervose = AK_VERBOSE_INSTALLER; 79 var $module; 80 var $warn_if_same_version = true; 81 82 function AkInstaller($db_connection = null) 83 { 84 if(empty($db_connection)){ 85 $this->db =& AkDbAdapter::getInstance(); 86 }else { 87 $this->db =& $db_connection; 88 } 89 90 $this->data_dictionary =& $this->db->getDictionary(); 91 $this->available_tables = $this->getAvailableTables(); 92 } 93 94 function install($version = null, $options = array()) 95 { 96 $version = (is_null($version)) ? max($this->getAvailableVersions()) : $version; 97 return $this->_upgradeOrDowngrade('up', $version , $options); 98 } 99 100 function up($version = null, $options = array()) 101 { 102 return $this->_upgradeOrDowngrade('up', $version, $options); 103 } 104 105 function uninstall($version = null, $options = array()) 106 { 107 $version = (is_null($version)) ? 0 : $version; 108 return $this->_upgradeOrDowngrade('down', $version, $options); 109 } 110 111 function down($version = null, $options = array()) 112 { 113 return $this->_upgradeOrDowngrade('down', $version, $options); 114 } 115 116 function _upgradeOrDowngrade($action, $version = null, $options = array()) 117 { 118 if(in_array('quiet',$options) && AK_DEV_MODE){ 119 $this->vervose = false; 120 }elseif(!empty($this->vervose) && AK_DEV_MODE){ 121 $this->debug(true); 122 } 123 124 $current_version = $this->getInstalledVersion($options); 125 $available_versions = $this->getAvailableVersions(); 126 127 $action = stristr($action,'down') ? 'down' : 'up'; 128 129 if($action == 'up'){ 130 $newest_version = max($available_versions); 131 $version = isset($version) && is_numeric($version) ? $version : 132 (isset($version[0]) && is_numeric($version[0]) ? $version[0] : $newest_version); 133 134 $versions = range($current_version+1,$version); 135 136 if($current_version > $version){ 137 echo Ak::t("You can't upgrade to version %version on the installer %installer_name, when you are currently on version %current_version", array('%version'=>$version,'%current_version'=>$current_version, '%installer_name' => $this->getInstallerName()))."\n"; 138 return false; 139 } 140 }else{ 141 $version = isset($version) && is_numeric($version) ? $version : 142 (isset($version[0]) && is_numeric($version[0]) ? $version[0] : 0); 143 144 $versions = range($current_version, empty($version) ? 1 : $version+1); 145 146 if($current_version == 0){ 147 return true; 148 }elseif($current_version < $version){ 149 echo Ak::t("You can't downgrade to version %version on the installer %installer_name, when you just have installed version %current_version", array('%version'=>$version,'%current_version'=>$current_version, '%installer_name' => $this->getInstallerName()))."\n"; 150 return false; 151 } 152 } 153 154 if($this->warn_if_same_version && $current_version == $version && AK_ENVIRONMENT != 'setup'){ 155 echo Ak::t("Can't go %action to version %version on the installer %installer_name, you're already on version %version", array('%version'=>$version, '%installer_name' => $this->getInstallerName(), '%action' => $action))."\n"; 156 return false; 157 } 158 159 if(AK_CLI && !empty($this->vervose) && AK_DEV_MODE){ 160 echo Ak::t(ucfirst($action).'grading'); 161 } 162 163 if(!empty($versions) && is_array($versions)){ 164 foreach ($versions as $version){ 165 if(!$this->_runInstallerMethod($action, $version, $options)){ 166 return false; 167 } 168 } 169 }else{ 170 return false; 171 } 172 173 return true; 174 } 175 176 function installVersion($version, $options = array()) 177 { 178 return $this->_runInstallerMethod('up', $version, $options); 179 } 180 181 function uninstallVersion($version, $options = array()) 182 { 183 return $this->_runInstallerMethod('down', $version, $options); 184 } 185 186 /** 187 * Runs a a dow_1, up_3 method and wraps it into a transaction. 188 */ 189 function _runInstallerMethod($method_prefix, $version, $options = array(), $version_number = null) 190 { 191 $method_name = $method_prefix.'_'.$version; 192 if(!method_exists($this, $method_name)){ 193 return false; 194 } 195 196 $version_number = empty($version_number) ? ($method_prefix=='down' ? $version-1 : $version) : $version_number; 197 198 $this->transactionStart(); 199 200 if($this->$method_name($options) === false){ 201 $this->transactionFail(); 202 } 203 $success = !$this->transactionHasFailed(); 204 $this->transactionComplete(); 205 if($success){ 206 $this->setInstalledVersion($version_number, $options); 207 } 208 return $success; 209 } 210 211 function getInstallerName() 212 { 213 return str_replace('installer','',strtolower(get_class($this))); 214 } 215 216 function _versionPath($options = array()) 217 { 218 $mode = empty($options['mode']) ? AK_ENVIRONMENT : $options['mode']; 219 return AK_TMP_DIR.DS.'installer_versions'.DS.(empty($this->module)?'':$this->module.DS).$mode.'_'.$this->getInstallerName().'_version.txt'; 220 } 221 222 // DEPRECATED 223 function _versionPath_Deprecated($options = array()) 224 { 225 $mode = empty($options['mode']) ? AK_ENVIRONMENT : $options['mode']; 226 return AK_APP_INSTALLERS_DIR.DS.(empty($this->module)?'':$this->module.DS).'versions'.DS.$mode.'_'.$this->getInstallerName().'_version.txt'; 227 } 228 229 // DEPRECATED 230 function _moveOldVersionsFileToNewLocation($options) 231 { 232 $old_filename = $this->_versionPath_Deprecated($options); 233 if (is_file($old_filename)){ 234 $this->setInstalledVersion(Ak::file_get_contents($old_filename),$options); 235 Ak::file_delete($old_filename); 236 Ak::file_put_contents(AK_APP_INSTALLERS_DIR.DS.'versions'.DS.'NOTE',"Version information is now stored in the temp folder. \n\rYou can safely move this files here over there to tmp/installer_versions/* or delete this directory if empty."); 237 } 238 } 239 240 function getInstalledVersion($options = array()) 241 { 242 $version_file = $this->_versionPath($options); 243 244 $this->_moveOldVersionsFileToNewLocation($options); 245 246 if(!is_file($version_file)){ 247 $this->setInstalledVersion(0, $options); 248 } 249 return Ak::file_get_contents($this->_versionPath($options)); 250 } 251 252 253 function setInstalledVersion($version, $options = array()) 254 { 255 return Ak::file_put_contents($this->_versionPath($options), $version); 256 } 257 258 259 function getAvailableVersions() 260 { 261 $versions = array(); 262 foreach(get_class_methods($this) as $method_name){ 263 if(preg_match('/^up_([0-9]*)$/',$method_name, $match)){ 264 $versions[] = intval($match[1]); 265 } 266 } 267 sort($versions); 268 return $versions; 269 } 270 271 272 function modifyTable($table_name, $column_options = null, $table_options = array()) 273 { 274 return $this->_createOrModifyTable($table_name, $column_options, $table_options); 275 } 276 277 /** 278 * Adds a new column to the table called $table_name 279 */ 280 function addColumn($table_name, $column_details) 281 { 282 require_once (AK_LIB_DIR.DS.'AkActiveRecord'.DS.'AkDbSchemaCache.php'); 283 AkDbSchemaCache::clear($table_name); 284 $this->timestamps = false; 285 $column_details = $this->_getColumnsAsAdodbDataDictionaryString($column_details); 286 return $this->data_dictionary->ExecuteSQLArray($this->data_dictionary->AddColumnSQL($table_name, $column_details)); 287 } 288 289 function changeColumn($table_name, $column_details) 290 { 291 require_once (AK_LIB_DIR.DS.'AkActiveRecord'.DS.'AkDbSchemaCache.php'); 292 AkDbSchemaCache::clear($table_name); 293 $this->timestamps = false; 294 $column_details = $this->_getColumnsAsAdodbDataDictionaryString($column_details); 295 return $this->data_dictionary->ExecuteSQLArray($this->data_dictionary->AlterColumnSQL($table_name, $column_details)); 296 } 297 298 function removeColumn($table_name, $column_name) 299 { 300 require_once (AK_LIB_DIR.DS.'AkActiveRecord'.DS.'AkDbSchemaCache.php'); 301 AkDbSchemaCache::clear($table_name); 302 return $this->data_dictionary->ExecuteSQLArray($this->data_dictionary->DropColumnSQL($table_name, $column_name)); 303 } 304 305 function renameColumn($table_name, $old_column_name, $new_column_name) 306 { 307 require_once (AK_LIB_DIR.DS.'AkActiveRecord'.DS.'AkDbSchemaCache.php'); 308 AkDbSchemaCache::clear($table_name); 309 return $this->db->renameColumn($table_name, $old_column_name, $new_column_name); 310 } 311 312 313 function createTable($table_name, $column_options = null, $table_options = array()) 314 { 315 if($this->tableExists($table_name)){ 316 trigger_error(Ak::t('Table %table_name already exists on the database', array('%table_name'=>$table_name)), E_USER_NOTICE); 317 return false; 318 } 319 $this->timestamps = (!isset($table_options['timestamp']) || (isset($table_options['timestamp']) && $table_options['timestamp'])) && 320 (!strstr($column_options, 'created') && !strstr($column_options, 'updated')); 321 return $this->_createOrModifyTable($table_name, $column_options, $table_options); 322 } 323 324 function _createOrModifyTable($table_name, $column_options = null, $table_options = array()) 325 { 326 require_once (AK_LIB_DIR.DS.'AkActiveRecord'.DS.'AkDbSchemaCache.php'); 327 AkDbSchemaCache::clear($table_name); 328 if(empty($column_options) && $this->_loadDbDesignerDbSchema()){ 329 $column_options = $this->db_designer_schema[$table_name]; 330 }elseif(empty($column_options)){ 331 trigger_error(Ak::t('You must supply details for the table you are creating.'), E_USER_ERROR); 332 return false; 333 } 334 335 $column_options = is_string($column_options) ? array('columns'=>$column_options) : $column_options; 336 337 $default_column_options = array( 338 'sequence_table' => false 339 ); 340 $column_options = array_merge($default_column_options, $column_options); 341 342 $default_table_options = array( 343 'mysql' => 'TYPE=InnoDB', 344 //'REPLACE' 345 ); 346 $table_options = array_merge($default_table_options, $table_options); 347 348 $column_string = $this->_getColumnsAsAdodbDataDictionaryString($column_options['columns']); 349 350 $create_or_alter_table_sql = $this->data_dictionary->ChangeTableSQL($table_name, str_replace(array(' UNIQUE', ' INDEX', ' FULLTEXT', ' HASH'), '', $column_string), $table_options); 351 $result = $this->data_dictionary->ExecuteSQLArray($create_or_alter_table_sql, false); 352 353 if($result){ 354 $this->available_tables[] = $table_name; 355 }else{ 356 trigger_error(Ak::t("Could not create or alter table %name using the SQL \n--------\n%sql\n--------\n", array('%name'=>$table_name, '%sql'=>$create_or_alter_table_sql[0])), E_USER_ERROR); 357 } 358 359 $columns_to_index = $this->_getColumnsToIndex($column_string); 360 361 foreach ($columns_to_index as $column_to_index => $index_type){ 362 $this->addIndex($table_name, $column_to_index.($index_type != 'INDEX' ? ' '.$index_type : '')); 363 } 364 365 if(isset($column_options['index_columns'])){ 366 $this->addIndex($table_name, $column_options['index_columns']); 367 368 } 369 if($column_options['sequence_table'] || $this->_requiresSequenceTable($column_string)){ 370 $this->createSequence($table_name); 371 } 372 373 return $result; 374 } 375 376 function dropTable($table_name, $options = array()) 377 { 378 require_once (AK_LIB_DIR.DS.'AkActiveRecord'.DS.'AkDbSchemaCache.php'); 379 AkDbSchemaCache::clear(AkInflector::classify($table_name)); 380 $result = $this->tableExists($table_name) ? $this->db->execute('DROP TABLE '.$table_name) : true; 381 if($result){ 382 unset($this->available_tables[array_search($table_name, $this->available_tables)]); 383 if(!empty($options['sequence'])){ 384 $this->dropSequence($table_name); 385 } 386 } 387 } 388 389 function dropTables() 390 { 391 $args = func_get_args(); 392 if(!empty($args)){ 393 $num_args = count($args); 394 $options = $num_args > 1 && is_array($args[$num_args-1]) ? array_shift($args) : array(); 395 $tables = count($args) > 1 ? $args : (is_array($args[0]) ? $args[0] : Ak::toArray($args[0])); 396 foreach ($tables as $table){ 397 $this->dropTable($table, $options); 398 } 399 } 400 } 401 402 function addIndex($table_name, $columns, $index_name = '') 403 { 404 $index_name = ($index_name == '') ? 'idx_'.$table_name.'_'.$columns : $index_name; 405 $index_options = array(); 406 if(preg_match('/(UNIQUE|FULLTEXT|HASH)/',$columns,$match)){ 407 $columns = trim(str_replace($match[1],'',$columns),' '); 408 $index_options[] = $match[1]; 409 } 410 return $this->tableExists($table_name) ? $this->data_dictionary->ExecuteSQLArray($this->data_dictionary->CreateIndexSQL($index_name, $table_name, $columns, $index_options)) : false; 411 } 412 413 function removeIndex($table_name, $columns_or_index_name) 414 { 415 require_once (AK_LIB_DIR.DS.'AkActiveRecord'.DS.'AkDbSchemaCache.php'); 416 AkDbSchemaCache::clear($table_name); 417 if(!$this->tableExists($table_name)){ 418 return false; 419 } 420 $available_indexes = $this->db->getIndexes($table_name); 421 $index_name = isset($available_indexes[$columns_or_index_name]) ? $columns_or_index_name : 'idx_'.$table_name.'_'.$columns_or_index_name; 422 if(!isset($available_indexes[$index_name])){ 423 trigger_error(Ak::t('Index %index_name does not exist.', array('%index_name'=>$index_name)), E_USER_NOTICE); 424 return false; 425 } 426 return $this->data_dictionary->ExecuteSQLArray($this->data_dictionary->DropIndexSQL($index_name, $table_name)); 427 } 428 429 function dropIndex($table_name, $columns_or_index_name) 430 { 431 require_once (AK_LIB_DIR.DS.'AkActiveRecord'.DS.'AkDbSchemaCache.php'); 432 AkDbSchemaCache::clear($table_name); 433 return $this->removeIndex($table_name,$columns_or_index_name); 434 } 435 436 function createSequence($table_name) 437 { 438 require_once (AK_LIB_DIR.DS.'AkActiveRecord'.DS.'AkDbSchemaCache.php'); 439 AkDbSchemaCache::clear($table_name); 440 $result = $this->tableExists('seq_'.$table_name) ? false : $this->db->connection->CreateSequence('seq_'.$table_name); 441 $this->available_tables[] = 'seq_'.$table_name; 442 return $result; 443 } 444 445 function dropSequence($table_name) 446 { 447 require_once (AK_LIB_DIR.DS.'AkActiveRecord'.DS.'AkDbSchemaCache.php'); 448 AkDbSchemaCache::clear($table_name); 449 $result = $this->tableExists('seq_'.$table_name) ? $this->db->connection->DropSequence('seq_'.$table_name) : true; 450 if($result){ 451 unset($this->available_tables[array_search('seq_'.$table_name, $this->available_tables)]); 452 453 } 454 return $result; 455 } 456 457 function getAvailableTables() 458 { 459 if(empty($this->available_tables)){ 460 $this->available_tables = $this->db->availableTables(); 461 } 462 return $this->available_tables; 463 } 464 465 function tableExists($table_name) 466 { 467 return in_array($table_name,$this->getAvailableTables()); 468 } 469 470 function _getColumnsAsAdodbDataDictionaryString($columns) 471 { 472 $columns = $this->_setColumnDefaults($columns); 473 $this->_ensureColumnNameCompatibility($columns); 474 475 $equivalences = array( 476 '/ ((limit|max|length) ?= ?)([0-9]+)([ \n\r,]+)/'=> ' (\3) ', 477 '/([ \n\r,]+)default([ =]+)([^\'^,^\n]+)/i'=> ' DEFAULT \'\3\'', 478 '/([ \n\r,]+)(integer|int)([( \n\r,]+)/'=> '\1 I \3', 479 '/([ \n\r,]+)float([( \n\r,]+)/'=> '\1 F \2', 480 '/([ \n\r,]+)decimal([( \n\r,]+)/'=> '\1 N \2', 481 '/([ \n\r,]+)datetime([( \n\r,]+)/'=> '\1 T \2', 482 '/([ \n\r,]+)date([( \n\r,]+)/'=> '\1 D \2', 483 '/([ \n\r,]+)timestamp([( \n\r,]+)/'=> '\1 T \2', 484 '/([ \n\r,]+)time([( \n\r,]+)/'=> '\1 T \2', 485 '/([ \n\r,]+)text([( \n\r,]+)/'=> '\1 XL \2', 486 '/([ \n\r,]+)string([( \n\r,]+)/'=> '\1 C \2', 487 '/([ \n\r,]+)binary([( \n\r,]+)/'=> '\1 B \2', 488 '/([ \n\r,]+)boolean([( \n\r,]+)/'=> '\1 L'.($this->db->type()=='mysql'?'(1)':'').' \2', 489 '/ NOT( |_)?NULL/i'=> ' NOTNULL', 490 '/ AUTO( |_)?INCREMENT/i'=> ' AUTO ', 491 '/ +/'=> ' ', 492 '/ ([\(,]+)/'=> '\1', 493 '/ INDEX| IDX/i'=> ' INDEX ', 494 '/ UNIQUE/i'=> ' UNIQUE ', 495 '/ HASH/i'=> ' HASH ', 496 '/ FULL_?TEXT/i'=> ' FULLTEXT ', 497 '/ ((PRIMARY( |_)?)?KEY|pk)/i'=> ' KEY', 498 ); 499 500 return trim(preg_replace(array_keys($equivalences),array_values($equivalences), ' '.$columns.' '), ' '); 501 } 502 503 function _setColumnDefaults($columns) 504 { 505 $columns = Ak::toArray($columns); 506 foreach ((array)$columns as $column){ 507 $column = trim($column, "\n\t\r, "); 508 if(!empty($column)){ 509 $single_columns[$column] = $this->_setColumnDefault($column); 510 } 511 } 512 if(!empty($this->timestamps) && !isset($single_columns['created_at']) && !isset($single_columns['updated_at'])){ 513 $single_columns['updated_at'] = $this->_setColumnDefault('updated_at'); 514 $single_columns['created_at'] = $this->_setColumnDefault('created_at'); 515 } 516 return join(",\n", $single_columns); 517 } 518 519 function _setColumnDefault($column) 520 { 521 return $this->_needsDefaultAttributes($column) ? $this->_setDefaultAttributes($column) : $column; 522 } 523 524 function _needsDefaultAttributes($column) 525 { 526 return preg_match('/^(([A-Z0-9_\(\)]+)|(.+ string[^\(.]*)|(\*.*))$/i',$column); 527 } 528 529 function _setDefaultAttributes($column) 530 { 531 $rules = $this->getDefaultColumnAttributesRules(); 532 foreach ($rules as $regex=>$replacement){ 533 if(is_string($replacement)){ 534 $column = preg_replace($regex,$replacement,$column); 535 }elseif(preg_match($regex,$column,$match)){ 536 $column = call_user_func_array($replacement,$match); 537 } 538 } 539 return $column; 540 } 541 542 /** 543 * Returns a key => value pair of regular expressions that will trigger methods 544 * to cast database columns to their respective default values or a replacement expression. 545 */ 546 function getDefaultColumnAttributesRules() 547 { 548 return array( 549 '/^\*(.*)$/i' => array(&$this,'_castToMultilingualColumn'), 550 '/^(description|content|body|details)$/i' => '\1 text', 551 '/^(lock_version)$/i' => '\1 integer default \'1\'', 552 '/^(.+_count)$/i' => '\1 integer default \'0\'', 553 '/^(id)$/i' => 'id integer not null auto_increment primary_key', 554 '/^(.+)_(id|by)$/i' => '\1_\2 integer index', 555 '/^(position)$/i' => '\1 integer index', 556 '/^(.+_at)$/i' => '\1 datetime', 557 '/^(.+_on)$/i' => '\1 date', 558 '/^(is_|has_|do_|does_|are_)([A-Z0-9_]+)$/i' => '\1\2 boolean not null default \'0\' index', // 559 '/^([A-Z0-9_]+) *(\([0-9]+\))?$/i' => '\1 string\2', // Everything else will default to string 560 '/^((.+ )string([^\(.]*))$/i' => '\2string(255)\3', // If we don't set the string lenght it will fail, so if not present will set it to 255 561 ); 562 } 563 564 function _castToMultilingualColumn($found, $column) 565 { 566 $columns = array(); 567 foreach (Ak::langs() as $lang){ 568 $columns[] = $lang.'_'.ltrim($column); 569 } 570 return $this->_setColumnDefaults($columns); 571 } 572 573 function _getColumnsToIndex($column_string) 574 { 575 $columns_to_index = array(); 576 foreach (explode(',',$column_string.',') as $column){ 577 if(preg_match('/([A-Za-z0-9_]+) (.*) (INDEX|UNIQUE|FULLTEXT|HASH) ?(.*)$/i',$column,$match)){ 578 $columns_to_index[$match[1]] = $match[3]; 579 } 580 } 581 return $columns_to_index; 582 } 583 584 function _getUniqueValueColumns($column_string) 585 { 586 $unique_columns = array(); 587 foreach (explode(',',$column_string.',') as $column){ 588 if(preg_match('/([A-Za-z0-9_]+) (.*) UNIQUE ?(.*)$/',$column,$match)){ 589 $unique_columns[] = $match[1]; 590 } 591 } 592 return $unique_columns; 593 } 594 595 function _requiresSequenceTable($column_string) 596 { 597 if(in_array($this->db->type(),array('mysql','postgre'))){ 598 return false; 599 } 600 foreach (explode(',',$column_string.',') as $column){ 601 if(preg_match('/([A-Za-z0-9_]+) (.*) AUTO (.*)$/',$column)){ 602 return true; 603 } 604 if(preg_match('/^id /',$column)){ 605 return true; 606 } 607 } 608 return false; 609 } 610 611 612 /** 613 * Transaction support for database operations 614 * 615 * Transactions are enabled automatically for Intaller objects, But you can nest transactions within models. 616 * This transactions are nested, and only the othermost will be executed 617 * 618 * $UserInstalller->transactionStart(); 619 * $UserInstalller->addTable('id, name'); 620 * 621 * if(!isCompatible()){ 622 * $User->transactionFail(); 623 * } 624 * 625 * $User->transactionComplete(); 626 */ 627 function transactionStart() 628 { 629 return $this->db->startTransaction(); 630 } 631 632 function transactionComplete() 633 { 634 return $this->db->stopTransaction(); 635 } 636 637 function transactionFail() 638 { 639 return $this->db->failTransaction(); 640 } 641 642 function transactionHasFailed() 643 { 644 return $this->db->hasTransactionFailed(); 645 } 646 647 /** 648 * Promts for a variable on console scripts 649 */ 650 function promptUserVar($message, $options = array()) 651 { 652 $f = fopen("php://stdin","r"); 653 $default_options = array( 654 'default' => null, 655 'optional' => false, 656 ); 657 658 $options = array_merge($default_options, $options); 659 660 echo "\n".$message.(empty($options['default'])?'': ' ['.$options['default'].']').': '; 661 $user_input = fgets($f, 25600); 662 $value = trim($user_input,"\n\r\t "); 663 $value = empty($value) ? $options['default'] : $value; 664 if(empty($value) && empty($options['optional'])){ 665 echo "\n\nThis setting is not optional."; 666 fclose($f); 667 return AkInstaller::promptUserVar($message, $options); 668 } 669 fclose($f); 670 return empty($value) ? $options['default'] : $value; 671 } 672 673 //DEPRECATED 674 function promtUserVar($message, $options = array()) 675 { 676 trigger_error(Ak::t('AkInstaller::promtUserVar is deprecated and will be removed on future versions. Please use AkInstaller::promptUserVar instead.'), E_USER_NOTICE); 677 return $this->promptUserVar($message, $options); 678 } 679 680 function _loadDbDesignerDbSchema() 681 { 682 if($path = $this->_getDbDesignerFilePath()){ 683 $this->db_designer_schema = Ak::convert('DBDesigner','AkelosDatabaseDesign', Ak::file_get_contents($path)); 684 return !empty($this->db_designer_schema); 685 } 686 return false; 687 } 688 689 function _getDbDesignerFilePath() 690 { 691 $path = AK_APP_INSTALLERS_DIR.DS.$this->getInstallerName().'.xml'; 692 return file_exists($path) ? $path : false; 693 } 694 695 function _ensureColumnNameCompatibility($columns) 696 { 697 $columns = explode(',',$columns.','); 698 foreach ($columns as $column){ 699 $column = trim($column); 700 $column = substr($column, 0, strpos($column.' ',' ')); 701 $this->_canUseColumn($column); 702 } 703 } 704 705 function _canUseColumn($column_name) 706 { 707 $invalid_columns = $this->_getInvalidColumnNames(); 708 if(in_array($column_name, $invalid_columns)){ 709 710 $method_name_part = AkInflector::camelize($column_name); 711 require_once (AK_LIB_DIR.DS.'AkActiveRecord.php'); 712 $method_name = (method_exists(new AkActiveRecord(), 'set'.$method_name_part)?'set':'get').$method_name_part; 713 714 trigger_error(Ak::t('A method named %method_name exists in the AkActiveRecord class'. 715 ' which will cause a recusion problem if you use the column %column_name in your database. '. 716 'You can disable automatic %type by setting the constant %constant to false '. 717 'in your configuration file.', array( 718 '%method_name'=> $method_name, 719 '%column_name' => $column_name, 720 '%type' => Ak::t($method_name[0] == 's' ? 'setters' : 'getters'), 721 '%constant' => Ak::t($method_name[0] == 's' ? 'AK_ACTIVE_RECORD_ENABLE_CALLBACK_SETTERS' : 'AK_ACTIVE_RECORD_ENABLE_CALLBACK_GETTERS'), 722 '' 723 )), E_USER_ERROR); 724 } 725 } 726 727 function _getInvalidColumnNames() 728 { 729 return defined('AK_INVALID_ACTIVE_RECORD_COLUMNS') ? explode(',',AK_INVALID_ACTIVE_RECORD_COLUMNS) : array('sanitized_conditions_array','conditions','inheritance_column','inheritance_column', 730 'subclasses','attribute','attributes','attribute','attributes','accessible_attributes','protected_attributes', 731 'serialized_attributes','available_attributes','attribute_caption','primary_key','column_names','content_columns', 732 'attribute_names','combined_subattributes','available_combined_attributes','connection','connection','primary_key', 733 'table_name','table_name','only_available_atrributes','columns_for_atrributes','columns_with_regex_boundaries','columns', 734 'column_settings','column_settings','akelos_data_type','class_for_database_table_mapping','display_field','display_field', 735 'internationalized_columns','available_locales','current_locale','attribute_by_locale','attribute_locales', 736 'attribute_by_locale','attribute_locales','attributes_before_type_cast','attribute_before_type_cast','serialize_attribute', 737 'available_attributes_quoted','attributes_quoted','column_type','value_for_date_column','observable_state', 738 'observable_state','observers','errors','base_errors','errors_on','full_error_messages','array_from_ak_string', 739 'attribute_condition','association_handler','associated','associated_finder_sql_options','association_option', 740 'association_option','association_id','associated_ids','associated_handler_name','associated_type','association_type', 741 'collection_handler_name','model_name','model_name','parent_model_name','parent_model_name'); 742 } 743 744 function execute($sql) 745 { 746 return $this->db->execute($sql); 747 } 748 749 function debug($toggle = null) 750 { 751 $this->db->connection->debug = $toggle === null ? !$this->db->connection->debug : $toggle; 752 } 753 754 function usage() 755 { 756 echo Ak::t("Description: 757 Database migrations is a sort of SCM like subversion, but for database settings. 758 759 The migration command takes the name of an installer located on your 760 /app/installers folder and runs one of the following commands: 761 762 - \"install\" + (options version number): Will update to the provided version 763 number or to the latest one in no version is given. 764 765 - \"uninstall\" + (options version number): Will downgrade to the provided 766 version number or to the lowest one in no version is given. 767 768 Current version number will be sorted at app/installers/installer_name_version.txt. 769 770 Example: 771 >> migrate framework install 772 773 Will run the default database schema for the framework. 774 This generates the tables for handling database driven sessions and cache. 775 776 "); 777 } 778 779 780 } 781 782 783 ?>
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
| Generated: Mon Oct 27 12:43:49 2008 | Cross-referenced by PHPXref 0.6 |