[ Index ]

PHP Cross Reference of Akelos Framework

title

Body

[close]

/ -> AkInstaller.php (source)

   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  ?>


Generated: Mon Oct 27 12:43:49 2008 Cross-referenced by PHPXref 0.6