Added new (clean) yii boilerplate
This commit is contained in:
228
framework/cli/commands/MessageCommand.php
Normal file
228
framework/cli/commands/MessageCommand.php
Normal file
@@ -0,0 +1,228 @@
|
||||
<?php
|
||||
/**
|
||||
* MessageCommand class file.
|
||||
*
|
||||
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||
* @link http://www.yiiframework.com/
|
||||
* @copyright 2008-2013 Yii Software LLC
|
||||
* @license http://www.yiiframework.com/license/
|
||||
*/
|
||||
|
||||
/**
|
||||
* MessageCommand extracts messages to be translated from source files.
|
||||
* The extracted messages are saved as PHP message source files
|
||||
* under the specified directory.
|
||||
*
|
||||
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||
* @package system.cli.commands
|
||||
* @since 1.0
|
||||
*/
|
||||
class MessageCommand extends CConsoleCommand
|
||||
{
|
||||
public function getHelp()
|
||||
{
|
||||
return <<<EOD
|
||||
USAGE
|
||||
yiic message <config-file>
|
||||
|
||||
DESCRIPTION
|
||||
This command searches for messages to be translated in the specified
|
||||
source files and compiles them into PHP arrays as message source.
|
||||
|
||||
PARAMETERS
|
||||
* config-file: required, the path of the configuration file. You can find
|
||||
an example in framework/messages/config.php.
|
||||
|
||||
The file can be placed anywhere and must be a valid PHP script which
|
||||
returns an array of name-value pairs. Each name-value pair represents
|
||||
a configuration option.
|
||||
|
||||
The following options are available:
|
||||
|
||||
- sourcePath: string, root directory of all source files.
|
||||
- messagePath: string, root directory containing message translations.
|
||||
- languages: array, list of language codes that the extracted messages
|
||||
should be translated to. For example, array('zh_cn','en_au').
|
||||
- fileTypes: array, a list of file extensions (e.g. 'php', 'xml').
|
||||
Only the files whose extension name can be found in this list
|
||||
will be processed. If empty, all files will be processed.
|
||||
- exclude: array, a list of directory and file exclusions. Each
|
||||
exclusion can be either a name or a path. If a file or directory name
|
||||
or path matches the exclusion, it will not be copied. For example,
|
||||
an exclusion of '.svn' will exclude all files and directories whose
|
||||
name is '.svn'. And an exclusion of '/a/b' will exclude file or
|
||||
directory 'sourcePath/a/b'.
|
||||
- translator: the name of the function for translating messages.
|
||||
Defaults to 'Yii::t'. This is used as a mark to find messages to be
|
||||
translated. Accepts both string for single function name or array for
|
||||
multiple function names.
|
||||
- overwrite: if message file must be overwritten with the merged messages.
|
||||
- removeOld: if message no longer needs translation it will be removed,
|
||||
instead of being enclosed between a pair of '@@' marks.
|
||||
- sort: sort messages by key when merging, regardless of their translation
|
||||
state (new, obsolete, translated.)
|
||||
|
||||
EOD;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the action.
|
||||
* @param array $args command line parameters specific for this command
|
||||
*/
|
||||
public function run($args)
|
||||
{
|
||||
if(!isset($args[0]))
|
||||
$this->usageError('the configuration file is not specified.');
|
||||
if(!is_file($args[0]))
|
||||
$this->usageError("the configuration file {$args[0]} does not exist.");
|
||||
|
||||
$config=require($args[0]);
|
||||
$translator='Yii::t';
|
||||
extract($config);
|
||||
|
||||
if(!isset($sourcePath,$messagePath,$languages))
|
||||
$this->usageError('The configuration file must specify "sourcePath", "messagePath" and "languages".');
|
||||
if(!is_dir($sourcePath))
|
||||
$this->usageError("The source path $sourcePath is not a valid directory.");
|
||||
if(!is_dir($messagePath))
|
||||
$this->usageError("The message path $messagePath is not a valid directory.");
|
||||
if(empty($languages))
|
||||
$this->usageError("Languages cannot be empty.");
|
||||
|
||||
if(!isset($overwrite))
|
||||
$overwrite = false;
|
||||
|
||||
if(!isset($removeOld))
|
||||
$removeOld = false;
|
||||
|
||||
if(!isset($sort))
|
||||
$sort = false;
|
||||
|
||||
$options=array();
|
||||
if(isset($fileTypes))
|
||||
$options['fileTypes']=$fileTypes;
|
||||
if(isset($exclude))
|
||||
$options['exclude']=$exclude;
|
||||
$files=CFileHelper::findFiles(realpath($sourcePath),$options);
|
||||
|
||||
$messages=array();
|
||||
foreach($files as $file)
|
||||
$messages=array_merge_recursive($messages,$this->extractMessages($file,$translator));
|
||||
|
||||
foreach($languages as $language)
|
||||
{
|
||||
$dir=$messagePath.DIRECTORY_SEPARATOR.$language;
|
||||
if(!is_dir($dir))
|
||||
@mkdir($dir);
|
||||
foreach($messages as $category=>$msgs)
|
||||
{
|
||||
$msgs=array_values(array_unique($msgs));
|
||||
$this->generateMessageFile($msgs,$dir.DIRECTORY_SEPARATOR.$category.'.php',$overwrite,$removeOld,$sort);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected function extractMessages($fileName,$translator)
|
||||
{
|
||||
echo "Extracting messages from $fileName...\n";
|
||||
$subject=file_get_contents($fileName);
|
||||
$messages=array();
|
||||
if(!is_array($translator))
|
||||
$translator=array($translator);
|
||||
|
||||
foreach ($translator as $currentTranslator)
|
||||
{
|
||||
$n=preg_match_all('/\b'.$currentTranslator.'\s*\(\s*(\'[\w.\/]*?(?<!\.)\'|"[\w.]*?(?<!\.)")\s*,\s*(\'.*?(?<!\\\\)\'|".*?(?<!\\\\)")\s*[,\)]/s',$subject,$matches,PREG_SET_ORDER);
|
||||
|
||||
for($i=0;$i<$n;++$i)
|
||||
{
|
||||
if(($pos=strpos($matches[$i][1],'.'))!==false)
|
||||
$category=substr($matches[$i][1],$pos+1,-1);
|
||||
else
|
||||
$category=substr($matches[$i][1],1,-1);
|
||||
$message=$matches[$i][2];
|
||||
$messages[$category][]=eval("return $message;"); // use eval to eliminate quote escape
|
||||
}
|
||||
}
|
||||
return $messages;
|
||||
}
|
||||
|
||||
protected function generateMessageFile($messages,$fileName,$overwrite,$removeOld,$sort)
|
||||
{
|
||||
echo "Saving messages to $fileName...";
|
||||
if(is_file($fileName))
|
||||
{
|
||||
$translated=require($fileName);
|
||||
sort($messages);
|
||||
ksort($translated);
|
||||
if(array_keys($translated)==$messages)
|
||||
{
|
||||
echo "nothing new...skipped.\n";
|
||||
return;
|
||||
}
|
||||
$merged=array();
|
||||
$untranslated=array();
|
||||
foreach($messages as $message)
|
||||
{
|
||||
if(array_key_exists($message,$translated) && strlen($translated[$message])>0)
|
||||
$merged[$message]=$translated[$message];
|
||||
else
|
||||
$untranslated[]=$message;
|
||||
}
|
||||
ksort($merged);
|
||||
sort($untranslated);
|
||||
$todo=array();
|
||||
foreach($untranslated as $message)
|
||||
$todo[$message]='';
|
||||
ksort($translated);
|
||||
foreach($translated as $message=>$translation)
|
||||
{
|
||||
if(!isset($merged[$message]) && !isset($todo[$message]) && !$removeOld)
|
||||
{
|
||||
if(substr($translation,0,2)==='@@' && substr($translation,-2)==='@@')
|
||||
$todo[$message]=$translation;
|
||||
else
|
||||
$todo[$message]='@@'.$translation.'@@';
|
||||
}
|
||||
}
|
||||
$merged=array_merge($todo,$merged);
|
||||
if($sort)
|
||||
ksort($merged);
|
||||
if($overwrite === false)
|
||||
$fileName.='.merged';
|
||||
echo "translation merged.\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
$merged=array();
|
||||
foreach($messages as $message)
|
||||
$merged[$message]='';
|
||||
ksort($merged);
|
||||
echo "saved.\n";
|
||||
}
|
||||
$array=str_replace("\r",'',var_export($merged,true));
|
||||
$content=<<<EOD
|
||||
<?php
|
||||
/**
|
||||
* Message translations.
|
||||
*
|
||||
* This file is automatically generated by 'yiic message' command.
|
||||
* It contains the localizable messages extracted from source code.
|
||||
* You may modify this file by translating the extracted messages.
|
||||
*
|
||||
* Each array element represents the translation (value) of a message (key).
|
||||
* If the value is empty, the message is considered as not translated.
|
||||
* Messages that no longer need translation will have their translations
|
||||
* enclosed between a pair of '@@' marks.
|
||||
*
|
||||
* Message string can be used with plural forms format. Check i18n section
|
||||
* of the guide for details.
|
||||
*
|
||||
* NOTE, this file must be saved in UTF-8 encoding.
|
||||
*/
|
||||
return $array;
|
||||
|
||||
EOD;
|
||||
file_put_contents($fileName, $content);
|
||||
}
|
||||
}
|
||||
585
framework/cli/commands/MigrateCommand.php
Normal file
585
framework/cli/commands/MigrateCommand.php
Normal file
@@ -0,0 +1,585 @@
|
||||
<?php
|
||||
/**
|
||||
* MigrateCommand class file.
|
||||
*
|
||||
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||
* @link http://www.yiiframework.com/
|
||||
* @copyright 2008-2013 Yii Software LLC
|
||||
* @license http://www.yiiframework.com/license/
|
||||
*/
|
||||
|
||||
/**
|
||||
* MigrateCommand manages the database migrations.
|
||||
*
|
||||
* The implementation of this command and other supporting classes referenced
|
||||
* the yii-dbmigrations extension ((https://github.com/pieterclaerhout/yii-dbmigrations),
|
||||
* authored by Pieter Claerhout.
|
||||
*
|
||||
* Since version 1.1.11 this command will exit with the following exit codes:
|
||||
* <ul>
|
||||
* <li>0 on success</li>
|
||||
* <li>1 on general error</li>
|
||||
* <li>2 on failed migration.</li>
|
||||
* </ul>
|
||||
*
|
||||
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||
* @package system.cli.commands
|
||||
* @since 1.1.6
|
||||
*/
|
||||
class MigrateCommand extends CConsoleCommand
|
||||
{
|
||||
const BASE_MIGRATION='m000000_000000_base';
|
||||
|
||||
/**
|
||||
* @var string the directory that stores the migrations. This must be specified
|
||||
* in terms of a path alias, and the corresponding directory must exist.
|
||||
* Defaults to 'application.migrations' (meaning 'protected/migrations').
|
||||
*/
|
||||
public $migrationPath='application.migrations';
|
||||
/**
|
||||
* @var string the name of the table for keeping applied migration information.
|
||||
* This table will be automatically created if not exists. Defaults to 'tbl_migration'.
|
||||
* The table structure is: (version varchar(255) primary key, apply_time integer)
|
||||
*/
|
||||
public $migrationTable='tbl_migration';
|
||||
/**
|
||||
* @var string the application component ID that specifies the database connection for
|
||||
* storing migration information. Defaults to 'db'.
|
||||
*/
|
||||
public $connectionID='db';
|
||||
/**
|
||||
* @var string the path of the template file for generating new migrations. This
|
||||
* must be specified in terms of a path alias (e.g. application.migrations.template).
|
||||
* If not set, an internal template will be used.
|
||||
*/
|
||||
public $templateFile;
|
||||
/**
|
||||
* @var string the default command action. It defaults to 'up'.
|
||||
*/
|
||||
public $defaultAction='up';
|
||||
/**
|
||||
* @var boolean whether to execute the migration in an interactive mode. Defaults to true.
|
||||
* Set this to false when performing migration in a cron job or background process.
|
||||
*/
|
||||
public $interactive=true;
|
||||
|
||||
public function beforeAction($action,$params)
|
||||
{
|
||||
$path=Yii::getPathOfAlias($this->migrationPath);
|
||||
if($path===false || !is_dir($path))
|
||||
{
|
||||
echo 'Error: The migration directory does not exist: '.$this->migrationPath."\n";
|
||||
exit(1);
|
||||
}
|
||||
$this->migrationPath=$path;
|
||||
|
||||
$yiiVersion=Yii::getVersion();
|
||||
echo "\nYii Migration Tool v1.0 (based on Yii v{$yiiVersion})\n\n";
|
||||
|
||||
return parent::beforeAction($action,$params);
|
||||
}
|
||||
|
||||
public function actionUp($args)
|
||||
{
|
||||
if(($migrations=$this->getNewMigrations())===array())
|
||||
{
|
||||
echo "No new migration found. Your system is up-to-date.\n";
|
||||
return 0;
|
||||
}
|
||||
|
||||
$total=count($migrations);
|
||||
$step=isset($args[0]) ? (int)$args[0] : 0;
|
||||
if($step>0)
|
||||
$migrations=array_slice($migrations,0,$step);
|
||||
|
||||
$n=count($migrations);
|
||||
if($n===$total)
|
||||
echo "Total $n new ".($n===1 ? 'migration':'migrations')." to be applied:\n";
|
||||
else
|
||||
echo "Total $n out of $total new ".($total===1 ? 'migration':'migrations')." to be applied:\n";
|
||||
|
||||
foreach($migrations as $migration)
|
||||
echo " $migration\n";
|
||||
echo "\n";
|
||||
|
||||
if($this->confirm('Apply the above '.($n===1 ? 'migration':'migrations')."?"))
|
||||
{
|
||||
foreach($migrations as $migration)
|
||||
{
|
||||
if($this->migrateUp($migration)===false)
|
||||
{
|
||||
echo "\nMigration failed. All later migrations are canceled.\n";
|
||||
return 2;
|
||||
}
|
||||
}
|
||||
echo "\nMigrated up successfully.\n";
|
||||
}
|
||||
}
|
||||
|
||||
public function actionDown($args)
|
||||
{
|
||||
$step=isset($args[0]) ? (int)$args[0] : 1;
|
||||
if($step<1)
|
||||
{
|
||||
echo "Error: The step parameter must be greater than 0.\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
if(($migrations=$this->getMigrationHistory($step))===array())
|
||||
{
|
||||
echo "No migration has been done before.\n";
|
||||
return 0;
|
||||
}
|
||||
$migrations=array_keys($migrations);
|
||||
|
||||
$n=count($migrations);
|
||||
echo "Total $n ".($n===1 ? 'migration':'migrations')." to be reverted:\n";
|
||||
foreach($migrations as $migration)
|
||||
echo " $migration\n";
|
||||
echo "\n";
|
||||
|
||||
if($this->confirm('Revert the above '.($n===1 ? 'migration':'migrations')."?"))
|
||||
{
|
||||
foreach($migrations as $migration)
|
||||
{
|
||||
if($this->migrateDown($migration)===false)
|
||||
{
|
||||
echo "\nMigration failed. All later migrations are canceled.\n";
|
||||
return 2;
|
||||
}
|
||||
}
|
||||
echo "\nMigrated down successfully.\n";
|
||||
}
|
||||
}
|
||||
|
||||
public function actionRedo($args)
|
||||
{
|
||||
$step=isset($args[0]) ? (int)$args[0] : 1;
|
||||
if($step<1)
|
||||
{
|
||||
echo "Error: The step parameter must be greater than 0.\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
if(($migrations=$this->getMigrationHistory($step))===array())
|
||||
{
|
||||
echo "No migration has been done before.\n";
|
||||
return 0;
|
||||
}
|
||||
$migrations=array_keys($migrations);
|
||||
|
||||
$n=count($migrations);
|
||||
echo "Total $n ".($n===1 ? 'migration':'migrations')." to be redone:\n";
|
||||
foreach($migrations as $migration)
|
||||
echo " $migration\n";
|
||||
echo "\n";
|
||||
|
||||
if($this->confirm('Redo the above '.($n===1 ? 'migration':'migrations')."?"))
|
||||
{
|
||||
foreach($migrations as $migration)
|
||||
{
|
||||
if($this->migrateDown($migration)===false)
|
||||
{
|
||||
echo "\nMigration failed. All later migrations are canceled.\n";
|
||||
return 2;
|
||||
}
|
||||
}
|
||||
foreach(array_reverse($migrations) as $migration)
|
||||
{
|
||||
if($this->migrateUp($migration)===false)
|
||||
{
|
||||
echo "\nMigration failed. All later migrations are canceled.\n";
|
||||
return 2;
|
||||
}
|
||||
}
|
||||
echo "\nMigration redone successfully.\n";
|
||||
}
|
||||
}
|
||||
|
||||
public function actionTo($args)
|
||||
{
|
||||
if(isset($args[0]))
|
||||
$version=$args[0];
|
||||
else
|
||||
$this->usageError('Please specify which version to migrate to.');
|
||||
|
||||
$originalVersion=$version;
|
||||
if(preg_match('/^m?(\d{6}_\d{6})(_.*?)?$/',$version,$matches))
|
||||
$version='m'.$matches[1];
|
||||
else
|
||||
{
|
||||
echo "Error: The version option must be either a timestamp (e.g. 101129_185401)\nor the full name of a migration (e.g. m101129_185401_create_user_table).\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
// try migrate up
|
||||
$migrations=$this->getNewMigrations();
|
||||
foreach($migrations as $i=>$migration)
|
||||
{
|
||||
if(strpos($migration,$version.'_')===0)
|
||||
return $this->actionUp(array($i+1));
|
||||
}
|
||||
|
||||
// try migrate down
|
||||
$migrations=array_keys($this->getMigrationHistory(-1));
|
||||
foreach($migrations as $i=>$migration)
|
||||
{
|
||||
if(strpos($migration,$version.'_')===0)
|
||||
{
|
||||
if($i===0)
|
||||
{
|
||||
echo "Already at '$originalVersion'. Nothing needs to be done.\n";
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
return $this->actionDown(array($i));
|
||||
}
|
||||
}
|
||||
|
||||
echo "Error: Unable to find the version '$originalVersion'.\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
public function actionMark($args)
|
||||
{
|
||||
if(isset($args[0]))
|
||||
$version=$args[0];
|
||||
else
|
||||
$this->usageError('Please specify which version to mark to.');
|
||||
$originalVersion=$version;
|
||||
if(preg_match('/^m?(\d{6}_\d{6})(_.*?)?$/',$version,$matches))
|
||||
$version='m'.$matches[1];
|
||||
else {
|
||||
echo "Error: The version option must be either a timestamp (e.g. 101129_185401)\nor the full name of a migration (e.g. m101129_185401_create_user_table).\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
$db=$this->getDbConnection();
|
||||
|
||||
// try mark up
|
||||
$migrations=$this->getNewMigrations();
|
||||
foreach($migrations as $i=>$migration)
|
||||
{
|
||||
if(strpos($migration,$version.'_')===0)
|
||||
{
|
||||
if($this->confirm("Set migration history at $originalVersion?"))
|
||||
{
|
||||
$command=$db->createCommand();
|
||||
for($j=0;$j<=$i;++$j)
|
||||
{
|
||||
$command->insert($this->migrationTable, array(
|
||||
'version'=>$migrations[$j],
|
||||
'apply_time'=>time(),
|
||||
));
|
||||
}
|
||||
echo "The migration history is set at $originalVersion.\nNo actual migration was performed.\n";
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
// try mark down
|
||||
$migrations=array_keys($this->getMigrationHistory(-1));
|
||||
foreach($migrations as $i=>$migration)
|
||||
{
|
||||
if(strpos($migration,$version.'_')===0)
|
||||
{
|
||||
if($i===0)
|
||||
echo "Already at '$originalVersion'. Nothing needs to be done.\n";
|
||||
else
|
||||
{
|
||||
if($this->confirm("Set migration history at $originalVersion?"))
|
||||
{
|
||||
$command=$db->createCommand();
|
||||
for($j=0;$j<$i;++$j)
|
||||
$command->delete($this->migrationTable, $db->quoteColumnName('version').'=:version', array(':version'=>$migrations[$j]));
|
||||
echo "The migration history is set at $originalVersion.\nNo actual migration was performed.\n";
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
echo "Error: Unable to find the version '$originalVersion'.\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
public function actionHistory($args)
|
||||
{
|
||||
$limit=isset($args[0]) ? (int)$args[0] : -1;
|
||||
$migrations=$this->getMigrationHistory($limit);
|
||||
if($migrations===array())
|
||||
echo "No migration has been done before.\n";
|
||||
else
|
||||
{
|
||||
$n=count($migrations);
|
||||
if($limit>0)
|
||||
echo "Showing the last $n applied ".($n===1 ? 'migration' : 'migrations').":\n";
|
||||
else
|
||||
echo "Total $n ".($n===1 ? 'migration has' : 'migrations have')." been applied before:\n";
|
||||
foreach($migrations as $version=>$time)
|
||||
echo " (".date('Y-m-d H:i:s',$time).') '.$version."\n";
|
||||
}
|
||||
}
|
||||
|
||||
public function actionNew($args)
|
||||
{
|
||||
$limit=isset($args[0]) ? (int)$args[0] : -1;
|
||||
$migrations=$this->getNewMigrations();
|
||||
if($migrations===array())
|
||||
echo "No new migrations found. Your system is up-to-date.\n";
|
||||
else
|
||||
{
|
||||
$n=count($migrations);
|
||||
if($limit>0 && $n>$limit)
|
||||
{
|
||||
$migrations=array_slice($migrations,0,$limit);
|
||||
echo "Showing $limit out of $n new ".($n===1 ? 'migration' : 'migrations').":\n";
|
||||
}
|
||||
else
|
||||
echo "Found $n new ".($n===1 ? 'migration' : 'migrations').":\n";
|
||||
|
||||
foreach($migrations as $migration)
|
||||
echo " ".$migration."\n";
|
||||
}
|
||||
}
|
||||
|
||||
public function actionCreate($args)
|
||||
{
|
||||
if(isset($args[0]))
|
||||
$name=$args[0];
|
||||
else
|
||||
$this->usageError('Please provide the name of the new migration.');
|
||||
|
||||
if(!preg_match('/^\w+$/',$name)) {
|
||||
echo "Error: The name of the migration must contain letters, digits and/or underscore characters only.\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
$name='m'.gmdate('ymd_His').'_'.$name;
|
||||
$content=strtr($this->getTemplate(), array('{ClassName}'=>$name));
|
||||
$file=$this->migrationPath.DIRECTORY_SEPARATOR.$name.'.php';
|
||||
|
||||
if($this->confirm("Create new migration '$file'?"))
|
||||
{
|
||||
file_put_contents($file, $content);
|
||||
echo "New migration created successfully.\n";
|
||||
}
|
||||
}
|
||||
|
||||
public function confirm($message,$default=false)
|
||||
{
|
||||
if(!$this->interactive)
|
||||
return true;
|
||||
return parent::confirm($message,$default);
|
||||
}
|
||||
|
||||
protected function migrateUp($class)
|
||||
{
|
||||
if($class===self::BASE_MIGRATION)
|
||||
return;
|
||||
|
||||
echo "*** applying $class\n";
|
||||
$start=microtime(true);
|
||||
$migration=$this->instantiateMigration($class);
|
||||
if($migration->up()!==false)
|
||||
{
|
||||
$this->getDbConnection()->createCommand()->insert($this->migrationTable, array(
|
||||
'version'=>$class,
|
||||
'apply_time'=>time(),
|
||||
));
|
||||
$time=microtime(true)-$start;
|
||||
echo "*** applied $class (time: ".sprintf("%.3f",$time)."s)\n\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
$time=microtime(true)-$start;
|
||||
echo "*** failed to apply $class (time: ".sprintf("%.3f",$time)."s)\n\n";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
protected function migrateDown($class)
|
||||
{
|
||||
if($class===self::BASE_MIGRATION)
|
||||
return;
|
||||
|
||||
echo "*** reverting $class\n";
|
||||
$start=microtime(true);
|
||||
$migration=$this->instantiateMigration($class);
|
||||
if($migration->down()!==false)
|
||||
{
|
||||
$db=$this->getDbConnection();
|
||||
$db->createCommand()->delete($this->migrationTable, $db->quoteColumnName('version').'=:version', array(':version'=>$class));
|
||||
$time=microtime(true)-$start;
|
||||
echo "*** reverted $class (time: ".sprintf("%.3f",$time)."s)\n\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
$time=microtime(true)-$start;
|
||||
echo "*** failed to revert $class (time: ".sprintf("%.3f",$time)."s)\n\n";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
protected function instantiateMigration($class)
|
||||
{
|
||||
$file=$this->migrationPath.DIRECTORY_SEPARATOR.$class.'.php';
|
||||
require_once($file);
|
||||
$migration=new $class;
|
||||
$migration->setDbConnection($this->getDbConnection());
|
||||
return $migration;
|
||||
}
|
||||
|
||||
/**
|
||||
* @var CDbConnection
|
||||
*/
|
||||
private $_db;
|
||||
protected function getDbConnection()
|
||||
{
|
||||
if($this->_db!==null)
|
||||
return $this->_db;
|
||||
elseif(($this->_db=Yii::app()->getComponent($this->connectionID)) instanceof CDbConnection)
|
||||
return $this->_db;
|
||||
|
||||
echo "Error: CMigrationCommand.connectionID '{$this->connectionID}' is invalid. Please make sure it refers to the ID of a CDbConnection application component.\n";
|
||||
exit(1);
|
||||
}
|
||||
|
||||
protected function getMigrationHistory($limit)
|
||||
{
|
||||
$db=$this->getDbConnection();
|
||||
if($db->schema->getTable($this->migrationTable,true)===null)
|
||||
{
|
||||
$this->createMigrationHistoryTable();
|
||||
}
|
||||
return CHtml::listData($db->createCommand()
|
||||
->select('version, apply_time')
|
||||
->from($this->migrationTable)
|
||||
->order('version DESC')
|
||||
->limit($limit)
|
||||
->queryAll(), 'version', 'apply_time');
|
||||
}
|
||||
|
||||
protected function createMigrationHistoryTable()
|
||||
{
|
||||
$db=$this->getDbConnection();
|
||||
echo 'Creating migration history table "'.$this->migrationTable.'"...';
|
||||
$db->createCommand()->createTable($this->migrationTable,array(
|
||||
'version'=>'string NOT NULL PRIMARY KEY',
|
||||
'apply_time'=>'integer',
|
||||
));
|
||||
$db->createCommand()->insert($this->migrationTable,array(
|
||||
'version'=>self::BASE_MIGRATION,
|
||||
'apply_time'=>time(),
|
||||
));
|
||||
echo "done.\n";
|
||||
}
|
||||
|
||||
protected function getNewMigrations()
|
||||
{
|
||||
$applied=array();
|
||||
foreach($this->getMigrationHistory(-1) as $version=>$time)
|
||||
$applied[substr($version,1,13)]=true;
|
||||
|
||||
$migrations=array();
|
||||
$handle=opendir($this->migrationPath);
|
||||
while(($file=readdir($handle))!==false)
|
||||
{
|
||||
if($file==='.' || $file==='..')
|
||||
continue;
|
||||
$path=$this->migrationPath.DIRECTORY_SEPARATOR.$file;
|
||||
if(preg_match('/^(m(\d{6}_\d{6})_.*?)\.php$/',$file,$matches) && is_file($path) && !isset($applied[$matches[2]]))
|
||||
$migrations[]=$matches[1];
|
||||
}
|
||||
closedir($handle);
|
||||
sort($migrations);
|
||||
return $migrations;
|
||||
}
|
||||
|
||||
public function getHelp()
|
||||
{
|
||||
return <<<EOD
|
||||
USAGE
|
||||
yiic migrate [action] [parameter]
|
||||
|
||||
DESCRIPTION
|
||||
This command provides support for database migrations. The optional
|
||||
'action' parameter specifies which specific migration task to perform.
|
||||
It can take these values: up, down, to, create, history, new, mark.
|
||||
If the 'action' parameter is not given, it defaults to 'up'.
|
||||
Each action takes different parameters. Their usage can be found in
|
||||
the following examples.
|
||||
|
||||
EXAMPLES
|
||||
* yiic migrate
|
||||
Applies ALL new migrations. This is equivalent to 'yiic migrate up'.
|
||||
|
||||
* yiic migrate create create_user_table
|
||||
Creates a new migration named 'create_user_table'.
|
||||
|
||||
* yiic migrate up 3
|
||||
Applies the next 3 new migrations.
|
||||
|
||||
* yiic migrate down
|
||||
Reverts the last applied migration.
|
||||
|
||||
* yiic migrate down 3
|
||||
Reverts the last 3 applied migrations.
|
||||
|
||||
* yiic migrate to 101129_185401
|
||||
Migrates up or down to version 101129_185401.
|
||||
|
||||
* yiic migrate mark 101129_185401
|
||||
Modifies the migration history up or down to version 101129_185401.
|
||||
No actual migration will be performed.
|
||||
|
||||
* yiic migrate history
|
||||
Shows all previously applied migration information.
|
||||
|
||||
* yiic migrate history 10
|
||||
Shows the last 10 applied migrations.
|
||||
|
||||
* yiic migrate new
|
||||
Shows all new migrations.
|
||||
|
||||
* yiic migrate new 10
|
||||
Shows the next 10 migrations that have not been applied.
|
||||
|
||||
EOD;
|
||||
}
|
||||
|
||||
protected function getTemplate()
|
||||
{
|
||||
if($this->templateFile!==null)
|
||||
return file_get_contents(Yii::getPathOfAlias($this->templateFile).'.php');
|
||||
else
|
||||
return <<<EOD
|
||||
<?php
|
||||
|
||||
class {ClassName} extends CDbMigration
|
||||
{
|
||||
public function up()
|
||||
{
|
||||
}
|
||||
|
||||
public function down()
|
||||
{
|
||||
echo "{ClassName} does not support migration down.\\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
// Use safeUp/safeDown to do migration with transaction
|
||||
public function safeUp()
|
||||
{
|
||||
}
|
||||
|
||||
public function safeDown()
|
||||
{
|
||||
}
|
||||
*/
|
||||
}
|
||||
EOD;
|
||||
}
|
||||
}
|
||||
146
framework/cli/commands/ShellCommand.php
Normal file
146
framework/cli/commands/ShellCommand.php
Normal file
@@ -0,0 +1,146 @@
|
||||
<?php
|
||||
/**
|
||||
* ShellCommand class file.
|
||||
*
|
||||
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||
* @link http://www.yiiframework.com/
|
||||
* @copyright 2008-2013 Yii Software LLC
|
||||
* @license http://www.yiiframework.com/license/
|
||||
*/
|
||||
|
||||
/**
|
||||
* ShellCommand executes the specified Web application and provides a shell for interaction.
|
||||
*
|
||||
* @property string $help The help information for the shell command.
|
||||
*
|
||||
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||
* @package system.cli.commands
|
||||
* @since 1.0
|
||||
*/
|
||||
class ShellCommand extends CConsoleCommand
|
||||
{
|
||||
/**
|
||||
* @return string the help information for the shell command
|
||||
*/
|
||||
public function getHelp()
|
||||
{
|
||||
return <<<EOD
|
||||
USAGE
|
||||
yiic shell [entry-script | config-file]
|
||||
|
||||
DESCRIPTION
|
||||
This command allows you to interact with a Web application
|
||||
on the command line. It also provides tools to automatically
|
||||
generate new controllers, views and data models.
|
||||
|
||||
It is recommended that you execute this command under
|
||||
the directory that contains the entry script file of
|
||||
the Web application.
|
||||
|
||||
PARAMETERS
|
||||
* entry-script | config-file: optional, the path to
|
||||
the entry script file or the configuration file for
|
||||
the Web application. If not given, it is assumed to be
|
||||
the 'index.php' file under the current directory.
|
||||
|
||||
EOD;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the action.
|
||||
* @param array $args command line parameters specific for this command
|
||||
*/
|
||||
public function run($args)
|
||||
{
|
||||
if(!isset($args[0]))
|
||||
$args[0]='index.php';
|
||||
$entryScript=isset($args[0]) ? $args[0] : 'index.php';
|
||||
if(($entryScript=realpath($args[0]))===false || !is_file($entryScript))
|
||||
$this->usageError("{$args[0]} does not exist or is not an entry script file.");
|
||||
|
||||
// fake the web server setting
|
||||
$cwd=getcwd();
|
||||
chdir(dirname($entryScript));
|
||||
$_SERVER['SCRIPT_NAME']='/'.basename($entryScript);
|
||||
$_SERVER['REQUEST_URI']=$_SERVER['SCRIPT_NAME'];
|
||||
$_SERVER['SCRIPT_FILENAME']=$entryScript;
|
||||
$_SERVER['HTTP_HOST']='localhost';
|
||||
$_SERVER['SERVER_NAME']='localhost';
|
||||
$_SERVER['SERVER_PORT']=80;
|
||||
|
||||
// reset context to run the web application
|
||||
restore_error_handler();
|
||||
restore_exception_handler();
|
||||
Yii::setApplication(null);
|
||||
Yii::setPathOfAlias('application',null);
|
||||
|
||||
ob_start();
|
||||
$config=require($entryScript);
|
||||
ob_end_clean();
|
||||
|
||||
// oops, the entry script turns out to be a config file
|
||||
if(is_array($config))
|
||||
{
|
||||
chdir($cwd);
|
||||
$_SERVER['SCRIPT_NAME']='/index.php';
|
||||
$_SERVER['REQUEST_URI']=$_SERVER['SCRIPT_NAME'];
|
||||
$_SERVER['SCRIPT_FILENAME']=$cwd.DIRECTORY_SEPARATOR.'index.php';
|
||||
Yii::createWebApplication($config);
|
||||
}
|
||||
|
||||
restore_error_handler();
|
||||
restore_exception_handler();
|
||||
|
||||
$yiiVersion=Yii::getVersion();
|
||||
echo <<<EOD
|
||||
Yii Interactive Tool v1.1 (based on Yii v{$yiiVersion})
|
||||
Please type 'help' for help. Type 'exit' to quit.
|
||||
EOD;
|
||||
$this->runShell();
|
||||
}
|
||||
|
||||
protected function runShell()
|
||||
{
|
||||
// disable E_NOTICE so that the shell is more friendly
|
||||
error_reporting(E_ALL ^ E_NOTICE);
|
||||
|
||||
$_runner_=new CConsoleCommandRunner;
|
||||
$_runner_->addCommands(dirname(__FILE__).'/shell');
|
||||
$_runner_->addCommands(Yii::getPathOfAlias('application.commands.shell'));
|
||||
if(($_path_=@getenv('YIIC_SHELL_COMMAND_PATH'))!==false)
|
||||
$_runner_->addCommands($_path_);
|
||||
$_commands_=$_runner_->commands;
|
||||
$log=Yii::app()->log;
|
||||
|
||||
while(($_line_=$this->prompt("\n>>"))!==false)
|
||||
{
|
||||
$_line_=trim($_line_);
|
||||
if($_line_==='exit')
|
||||
return;
|
||||
try
|
||||
{
|
||||
$_args_=preg_split('/[\s,]+/',rtrim($_line_,';'),-1,PREG_SPLIT_NO_EMPTY);
|
||||
if(isset($_args_[0]) && isset($_commands_[$_args_[0]]))
|
||||
{
|
||||
$_command_=$_runner_->createCommand($_args_[0]);
|
||||
array_shift($_args_);
|
||||
$_command_->init();
|
||||
$_command_->run($_args_);
|
||||
}
|
||||
else
|
||||
echo eval($_line_.';');
|
||||
}
|
||||
catch(Exception $e)
|
||||
{
|
||||
if($e instanceof ShellException)
|
||||
echo $e->getMessage();
|
||||
else
|
||||
echo $e;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class ShellException extends CException
|
||||
{
|
||||
}
|
||||
213
framework/cli/commands/WebAppCommand.php
Normal file
213
framework/cli/commands/WebAppCommand.php
Normal file
@@ -0,0 +1,213 @@
|
||||
<?php
|
||||
/**
|
||||
* WebAppCommand class file.
|
||||
*
|
||||
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||
* @link http://www.yiiframework.com/
|
||||
* @copyright 2008-2013 Yii Software LLC
|
||||
* @license http://www.yiiframework.com/license/
|
||||
*/
|
||||
|
||||
/**
|
||||
* WebAppCommand creates an Yii Web application at the specified location.
|
||||
*
|
||||
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||
* @package system.cli.commands
|
||||
* @since 1.0
|
||||
*/
|
||||
class WebAppCommand extends CConsoleCommand
|
||||
{
|
||||
private $_rootPath;
|
||||
|
||||
public function getHelp()
|
||||
{
|
||||
return <<<EOD
|
||||
USAGE
|
||||
yiic webapp <app-path> [<vcs>]
|
||||
|
||||
DESCRIPTION
|
||||
This command generates an Yii Web Application at the specified location.
|
||||
|
||||
PARAMETERS
|
||||
* app-path: required, the directory where the new application will be created.
|
||||
If the directory does not exist, it will be created. After the application
|
||||
is created, please make sure the directory can be accessed by Web users.
|
||||
* vcs: optional, version control system you're going to use in the new project.
|
||||
Application generator will create all needed files to the specified VCS
|
||||
(such as .gitignore, .gitkeep, etc.). Possible values: git, hg. Do not
|
||||
use this argument if you're going to create VCS files yourself.
|
||||
|
||||
EOD;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the action.
|
||||
* @param array $args command line parameters specific for this command
|
||||
*/
|
||||
public function run($args)
|
||||
{
|
||||
$vcs=false;
|
||||
if(isset($args[1]))
|
||||
{
|
||||
if($args[1]!='git' && $args[1]!='hg')
|
||||
$this->usageError('Unsupported VCS specified. Currently only git and hg supported.');
|
||||
$vcs=$args[1];
|
||||
}
|
||||
if(!isset($args[0]))
|
||||
$this->usageError('the Web application location is not specified.');
|
||||
$path=strtr($args[0],'/\\',DIRECTORY_SEPARATOR);
|
||||
if(strpos($path,DIRECTORY_SEPARATOR)===false)
|
||||
$path='.'.DIRECTORY_SEPARATOR.$path;
|
||||
if(basename($path)=='..')
|
||||
$path.=DIRECTORY_SEPARATOR.'.';
|
||||
$dir=rtrim(realpath(dirname($path)),'\\/');
|
||||
if($dir===false || !is_dir($dir))
|
||||
$this->usageError("The directory '$path' is not valid. Please make sure the parent directory exists.");
|
||||
if(basename($path)==='.')
|
||||
$this->_rootPath=$path=$dir;
|
||||
else
|
||||
$this->_rootPath=$path=$dir.DIRECTORY_SEPARATOR.basename($path);
|
||||
if($this->confirm("Create a Web application under '$path'?"))
|
||||
{
|
||||
$sourceDir=$this->getSourceDir();
|
||||
if($sourceDir===false)
|
||||
die("\nUnable to locate the source directory.\n");
|
||||
$ignoreFiles=array();
|
||||
$renameMap=array();
|
||||
switch($vcs)
|
||||
{
|
||||
case 'git':
|
||||
$renameMap=array('git-gitignore'=>'.gitignore','git-gitkeep'=>'.gitkeep'); // move with rename git files
|
||||
$ignoreFiles=array('hg-hgignore','hg-hgkeep'); // ignore only hg files
|
||||
break;
|
||||
case 'hg':
|
||||
$renameMap=array('hg-hgignore'=>'.hgignore','hg-hgkeep'=>'.hgkeep'); // move with rename hg files
|
||||
$ignoreFiles=array('git-gitignore','git-gitkeep'); // ignore only git files
|
||||
break;
|
||||
default:
|
||||
// no files for renaming
|
||||
$ignoreFiles=array('git-gitignore','git-gitkeep','hg-hgignore','hg-hgkeep'); // ignore both git and hg files
|
||||
break;
|
||||
}
|
||||
$list=$this->buildFileList($sourceDir,$path,'',$ignoreFiles,$renameMap);
|
||||
$this->addFileModificationCallbacks($list);
|
||||
$this->copyFiles($list);
|
||||
$this->setPermissions($path);
|
||||
echo "\nYour application has been created successfully under {$path}.\n";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adjusts created application file and directory permissions
|
||||
*
|
||||
* @param string $targetDir path to created application
|
||||
*/
|
||||
protected function setPermissions($targetDir)
|
||||
{
|
||||
@chmod($targetDir.'/assets',0777);
|
||||
@chmod($targetDir.'/protected/runtime',0777);
|
||||
@chmod($targetDir.'/protected/data',0777);
|
||||
@chmod($targetDir.'/protected/data/testdrive.db',0777);
|
||||
@chmod($targetDir.'/protected/yiic',0755);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string path to application bootstrap source files
|
||||
*/
|
||||
protected function getSourceDir()
|
||||
{
|
||||
return realpath(dirname(__FILE__).'/../views/webapp');
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds callbacks that will modify source files
|
||||
*
|
||||
* @param array $fileList
|
||||
*/
|
||||
protected function addFileModificationCallbacks(&$fileList)
|
||||
{
|
||||
$fileList['index.php']['callback']=array($this,'generateIndex');
|
||||
$fileList['index-test.php']['callback']=array($this,'generateIndex');
|
||||
$fileList['protected/tests/bootstrap.php']['callback']=array($this,'generateTestBoostrap');
|
||||
$fileList['protected/yiic.php']['callback']=array($this,'generateYiic');
|
||||
}
|
||||
|
||||
/**
|
||||
* Inserts path to framework's yii.php into application's index.php
|
||||
*
|
||||
* @param string $source source file path
|
||||
* @param array $params
|
||||
* @return string modified source file content
|
||||
*/
|
||||
public function generateIndex($source,$params)
|
||||
{
|
||||
$content=file_get_contents($source);
|
||||
$yii=realpath(dirname(__FILE__).'/../../yii.php');
|
||||
$yii=$this->getRelativePath($yii,$this->_rootPath.DIRECTORY_SEPARATOR.'index.php');
|
||||
$yii=str_replace('\\','\\\\',$yii);
|
||||
return preg_replace('/\$yii\s*=(.*?);/',"\$yii=$yii;",$content);
|
||||
}
|
||||
|
||||
/**
|
||||
* Inserts path to framework's yiit.php into application's index-test.php
|
||||
*
|
||||
* @param string $source source file path
|
||||
* @param array $params
|
||||
* @return string modified source file content
|
||||
*/
|
||||
public function generateTestBoostrap($source,$params)
|
||||
{
|
||||
$content=file_get_contents($source);
|
||||
$yii=realpath(dirname(__FILE__).'/../../yiit.php');
|
||||
$yii=$this->getRelativePath($yii,$this->_rootPath.DIRECTORY_SEPARATOR.'protected'.DIRECTORY_SEPARATOR.'tests'.DIRECTORY_SEPARATOR.'bootstrap.php');
|
||||
$yii=str_replace('\\','\\\\',$yii);
|
||||
return preg_replace('/\$yiit\s*=(.*?);/',"\$yiit=$yii;",$content);
|
||||
}
|
||||
|
||||
/**
|
||||
* Inserts path to framework's yiic.php into application's yiic.php
|
||||
*
|
||||
* @param string $source source file path
|
||||
* @param array $params
|
||||
* @return string modified source file content
|
||||
*/
|
||||
public function generateYiic($source,$params)
|
||||
{
|
||||
$content=file_get_contents($source);
|
||||
$yiic=realpath(dirname(__FILE__).'/../../yiic.php');
|
||||
$yiic=$this->getRelativePath($yiic,$this->_rootPath.DIRECTORY_SEPARATOR.'protected'.DIRECTORY_SEPARATOR.'yiic.php');
|
||||
$yiic=str_replace('\\','\\\\',$yiic);
|
||||
return preg_replace('/\$yiic\s*=(.*?);/',"\$yiic=$yiic;",$content);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns variant of $path1 relative to $path2
|
||||
*
|
||||
* @param string $path1
|
||||
* @param string $path2
|
||||
* @return string $path1 relative to $path2
|
||||
*/
|
||||
protected function getRelativePath($path1,$path2)
|
||||
{
|
||||
$segs1=explode(DIRECTORY_SEPARATOR,$path1);
|
||||
$segs2=explode(DIRECTORY_SEPARATOR,$path2);
|
||||
$n1=count($segs1);
|
||||
$n2=count($segs2);
|
||||
|
||||
for($i=0;$i<$n1 && $i<$n2;++$i)
|
||||
{
|
||||
if($segs1[$i]!==$segs2[$i])
|
||||
break;
|
||||
}
|
||||
|
||||
if($i===0)
|
||||
return "'".$path1."'";
|
||||
$up='';
|
||||
for($j=$i;$j<$n2-1;++$j)
|
||||
$up.='/..';
|
||||
for(;$i<$n1-1;++$i)
|
||||
$up.='/'.$segs1[$i];
|
||||
|
||||
return 'dirname(__FILE__).\''.$up.'/'.basename($path1).'\'';
|
||||
}
|
||||
}
|
||||
175
framework/cli/commands/shell/ControllerCommand.php
Normal file
175
framework/cli/commands/shell/ControllerCommand.php
Normal file
@@ -0,0 +1,175 @@
|
||||
<?php
|
||||
/**
|
||||
* ControllerCommand class file.
|
||||
*
|
||||
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||
* @link http://www.yiiframework.com/
|
||||
* @copyright 2008-2013 Yii Software LLC
|
||||
* @license http://www.yiiframework.com/license/
|
||||
*/
|
||||
|
||||
/**
|
||||
* ControllerCommand generates a controller class.
|
||||
*
|
||||
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||
* @package system.cli.commands.shell
|
||||
* @since 1.0
|
||||
*/
|
||||
class ControllerCommand extends CConsoleCommand
|
||||
{
|
||||
/**
|
||||
* @var string the directory that contains templates for the model command.
|
||||
* Defaults to null, meaning using 'framework/cli/views/shell/controller'.
|
||||
* If you set this path and some views are missing in the directory,
|
||||
* the default views will be used.
|
||||
*/
|
||||
public $templatePath;
|
||||
|
||||
public function getHelp()
|
||||
{
|
||||
return <<<EOD
|
||||
USAGE
|
||||
controller <controller-ID> [action-ID] ...
|
||||
|
||||
DESCRIPTION
|
||||
This command generates a controller and views associated with
|
||||
the specified actions.
|
||||
|
||||
PARAMETERS
|
||||
* controller-ID: required, controller ID, e.g., 'post'.
|
||||
If the controller should be located under a subdirectory,
|
||||
please specify the controller ID as 'path/to/ControllerID',
|
||||
e.g., 'admin/user'.
|
||||
|
||||
If the controller belongs to a module, please specify
|
||||
the controller ID as 'ModuleID/ControllerID' or
|
||||
'ModuleID/path/to/Controller' (assuming the controller is
|
||||
under a subdirectory of that module).
|
||||
|
||||
* action-ID: optional, action ID. You may supply one or several
|
||||
action IDs. A default 'index' action will always be generated.
|
||||
|
||||
EXAMPLES
|
||||
* Generates the 'post' controller:
|
||||
controller post
|
||||
|
||||
* Generates the 'post' controller with additional actions 'contact'
|
||||
and 'about':
|
||||
controller post contact about
|
||||
|
||||
* Generates the 'post' controller which should be located under
|
||||
the 'admin' subdirectory of the base controller path:
|
||||
controller admin/post
|
||||
|
||||
* Generates the 'post' controller which should belong to
|
||||
the 'admin' module:
|
||||
controller admin/post
|
||||
|
||||
NOTE: in the last two examples, the commands are the same, but
|
||||
the generated controller file is located under different directories.
|
||||
Yii is able to detect whether 'admin' refers to a module or a subdirectory.
|
||||
|
||||
EOD;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the action.
|
||||
* @param array $args command line parameters specific for this command
|
||||
* @return integer|null non zero application exit code for help or null on success
|
||||
*/
|
||||
public function run($args)
|
||||
{
|
||||
if(!isset($args[0]))
|
||||
{
|
||||
echo "Error: controller name is required.\n";
|
||||
echo $this->getHelp();
|
||||
return 1;
|
||||
}
|
||||
|
||||
$module=Yii::app();
|
||||
$controllerID=$args[0];
|
||||
if(($pos=strrpos($controllerID,'/'))===false)
|
||||
{
|
||||
$controllerClass=ucfirst($controllerID).'Controller';
|
||||
$controllerFile=$module->controllerPath.DIRECTORY_SEPARATOR.$controllerClass.'.php';
|
||||
$controllerID[0]=strtolower($controllerID[0]);
|
||||
}
|
||||
else
|
||||
{
|
||||
$last=substr($controllerID,$pos+1);
|
||||
$last[0]=strtolower($last[0]);
|
||||
$pos2=strpos($controllerID,'/');
|
||||
$first=substr($controllerID,0,$pos2);
|
||||
$middle=$pos===$pos2?'':substr($controllerID,$pos2+1,$pos-$pos2);
|
||||
|
||||
$controllerClass=ucfirst($last).'Controller';
|
||||
$controllerFile=($middle===''?'':$middle.'/').$controllerClass.'.php';
|
||||
$controllerID=$middle===''?$last:$middle.'/'.$last;
|
||||
if(($m=Yii::app()->getModule($first))!==null)
|
||||
$module=$m;
|
||||
else
|
||||
{
|
||||
$controllerFile=$first.'/'.$controllerClass.'.php';
|
||||
$controllerID=$first.'/'.$controllerID;
|
||||
}
|
||||
|
||||
$controllerFile=$module->controllerPath.DIRECTORY_SEPARATOR.str_replace('/',DIRECTORY_SEPARATOR,$controllerFile);
|
||||
}
|
||||
|
||||
$args[]='index';
|
||||
$actions=array_unique(array_splice($args,1));
|
||||
|
||||
$templatePath=$this->templatePath===null?YII_PATH.'/cli/views/shell/controller':$this->templatePath;
|
||||
|
||||
$list=array(
|
||||
basename($controllerFile)=>array(
|
||||
'source'=>$templatePath.DIRECTORY_SEPARATOR.'controller.php',
|
||||
'target'=>$controllerFile,
|
||||
'callback'=>array($this,'generateController'),
|
||||
'params'=>array($controllerClass, $actions),
|
||||
),
|
||||
);
|
||||
|
||||
$viewPath=$module->viewPath.DIRECTORY_SEPARATOR.str_replace('/',DIRECTORY_SEPARATOR,$controllerID);
|
||||
foreach($actions as $name)
|
||||
{
|
||||
$list[$name.'.php']=array(
|
||||
'source'=>$templatePath.DIRECTORY_SEPARATOR.'view.php',
|
||||
'target'=>$viewPath.DIRECTORY_SEPARATOR.$name.'.php',
|
||||
'callback'=>array($this,'generateAction'),
|
||||
'params'=>array('controller'=>$controllerClass, 'action'=>$name),
|
||||
);
|
||||
}
|
||||
|
||||
$this->copyFiles($list);
|
||||
|
||||
if($module instanceof CWebModule)
|
||||
$moduleID=$module->id.'/';
|
||||
else
|
||||
$moduleID='';
|
||||
|
||||
echo <<<EOD
|
||||
|
||||
Controller '{$controllerID}' has been created in the following file:
|
||||
$controllerFile
|
||||
|
||||
You may access it in the browser using the following URL:
|
||||
http://hostname/path/to/index.php?r={$moduleID}{$controllerID}
|
||||
|
||||
EOD;
|
||||
}
|
||||
|
||||
public function generateController($source,$params)
|
||||
{
|
||||
if(!is_file($source)) // fall back to default ones
|
||||
$source=YII_PATH.'/cli/views/shell/controller/'.basename($source);
|
||||
return $this->renderFile($source,array('className'=>$params[0],'actions'=>$params[1]),true);
|
||||
}
|
||||
|
||||
public function generateAction($source,$params)
|
||||
{
|
||||
if(!is_file($source)) // fall back to default ones
|
||||
$source=YII_PATH.'/cli/views/shell/controller/'.basename($source);
|
||||
return $this->renderFile($source,$params,true);
|
||||
}
|
||||
}
|
||||
326
framework/cli/commands/shell/CrudCommand.php
Normal file
326
framework/cli/commands/shell/CrudCommand.php
Normal file
@@ -0,0 +1,326 @@
|
||||
<?php
|
||||
/**
|
||||
* CrudCommand class file.
|
||||
*
|
||||
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||
* @link http://www.yiiframework.com/
|
||||
* @copyright 2008-2013 Yii Software LLC
|
||||
* @license http://www.yiiframework.com/license/
|
||||
*/
|
||||
|
||||
/**
|
||||
* CrudCommand generates code implementing CRUD operations.
|
||||
*
|
||||
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||
* @package system.cli.commands.shell
|
||||
* @since 1.0
|
||||
*/
|
||||
class CrudCommand extends CConsoleCommand
|
||||
{
|
||||
/**
|
||||
* @var string the directory that contains templates for crud commands.
|
||||
* Defaults to null, meaning using 'framework/cli/views/shell/crud'.
|
||||
* If you set this path and some views are missing in the directory,
|
||||
* the default views will be used.
|
||||
*/
|
||||
public $templatePath;
|
||||
/**
|
||||
* @var string the directory that contains functional test classes.
|
||||
* Defaults to null, meaning using 'protected/tests/functional'.
|
||||
* If this is false, it means functional test file should NOT be generated.
|
||||
*/
|
||||
public $functionalTestPath;
|
||||
/**
|
||||
* @var array list of actions to be created. Each action must be associated with a template file with the same name.
|
||||
*/
|
||||
public $actions=array('create','update','index','view','admin','_form','_view','_search');
|
||||
|
||||
public function getHelp()
|
||||
{
|
||||
return <<<EOD
|
||||
USAGE
|
||||
crud <model-class> [controller-ID] ...
|
||||
|
||||
DESCRIPTION
|
||||
This command generates a controller and views that accomplish
|
||||
CRUD operations for the specified data model.
|
||||
|
||||
PARAMETERS
|
||||
* model-class: required, the name of the data model class. This can
|
||||
also be specified as a path alias (e.g. application.models.Post).
|
||||
If the model class belongs to a module, it should be specified
|
||||
as 'ModuleID.models.ClassName'.
|
||||
|
||||
* controller-ID: optional, the controller ID (e.g. 'post').
|
||||
If this is not specified, the model class name will be used
|
||||
as the controller ID. In this case, if the model belongs to
|
||||
a module, the controller will also be created under the same
|
||||
module.
|
||||
|
||||
If the controller should be located under a subdirectory,
|
||||
please specify the controller ID as 'path/to/ControllerID'
|
||||
(e.g. 'admin/user').
|
||||
|
||||
If the controller belongs to a module (different from the module
|
||||
that the model belongs to), please specify the controller ID
|
||||
as 'ModuleID/ControllerID' or 'ModuleID/path/to/Controller'.
|
||||
|
||||
EXAMPLES
|
||||
* Generates CRUD for the Post model:
|
||||
crud Post
|
||||
|
||||
* Generates CRUD for the Post model which belongs to module 'admin':
|
||||
crud admin.models.Post
|
||||
|
||||
* Generates CRUD for the Post model. The generated controller should
|
||||
belong to module 'admin', but not the model class:
|
||||
crud Post admin/post
|
||||
|
||||
EOD;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the action.
|
||||
* @param array $args command line parameters specific for this command
|
||||
* @return integer|null non zero application exit code for help or null on success
|
||||
*/
|
||||
public function run($args)
|
||||
{
|
||||
if(!isset($args[0]))
|
||||
{
|
||||
echo "Error: data model class is required.\n";
|
||||
echo $this->getHelp();
|
||||
return 1;
|
||||
}
|
||||
$module=Yii::app();
|
||||
$modelClass=$args[0];
|
||||
if(($pos=strpos($modelClass,'.'))===false)
|
||||
$modelClass='application.models.'.$modelClass;
|
||||
else
|
||||
{
|
||||
$id=substr($modelClass,0,$pos);
|
||||
if(($m=Yii::app()->getModule($id))!==null)
|
||||
$module=$m;
|
||||
}
|
||||
$modelClass=Yii::import($modelClass);
|
||||
|
||||
if(isset($args[1]))
|
||||
{
|
||||
$controllerID=$args[1];
|
||||
if(($pos=strrpos($controllerID,'/'))===false)
|
||||
{
|
||||
$controllerClass=ucfirst($controllerID).'Controller';
|
||||
$controllerFile=$module->controllerPath.DIRECTORY_SEPARATOR.$controllerClass.'.php';
|
||||
$controllerID[0]=strtolower($controllerID[0]);
|
||||
}
|
||||
else
|
||||
{
|
||||
$last=substr($controllerID,$pos+1);
|
||||
$last[0]=strtolower($last);
|
||||
$pos2=strpos($controllerID,'/');
|
||||
$first=substr($controllerID,0,$pos2);
|
||||
$middle=$pos===$pos2?'':substr($controllerID,$pos2+1,$pos-$pos2);
|
||||
|
||||
$controllerClass=ucfirst($last).'Controller';
|
||||
$controllerFile=($middle===''?'':$middle.'/').$controllerClass.'.php';
|
||||
$controllerID=$middle===''?$last:$middle.'/'.$last;
|
||||
if(($m=Yii::app()->getModule($first))!==null)
|
||||
$module=$m;
|
||||
else
|
||||
{
|
||||
$controllerFile=$first.'/'.$controllerFile;
|
||||
$controllerID=$first.'/'.$controllerID;
|
||||
}
|
||||
|
||||
$controllerFile=$module->controllerPath.DIRECTORY_SEPARATOR.str_replace('/',DIRECTORY_SEPARATOR,$controllerFile);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$controllerID=$modelClass;
|
||||
$controllerClass=ucfirst($controllerID).'Controller';
|
||||
$controllerFile=$module->controllerPath.DIRECTORY_SEPARATOR.$controllerClass.'.php';
|
||||
$controllerID[0]=strtolower($controllerID[0]);
|
||||
}
|
||||
|
||||
$templatePath=$this->templatePath===null?YII_PATH.'/cli/views/shell/crud':$this->templatePath;
|
||||
$functionalTestPath=$this->functionalTestPath===null?Yii::getPathOfAlias('application.tests.functional'):$this->functionalTestPath;
|
||||
|
||||
$viewPath=$module->viewPath.DIRECTORY_SEPARATOR.str_replace('.',DIRECTORY_SEPARATOR,$controllerID);
|
||||
$fixtureName=$this->pluralize($modelClass);
|
||||
$fixtureName[0]=strtolower($fixtureName);
|
||||
$list=array(
|
||||
basename($controllerFile)=>array(
|
||||
'source'=>$templatePath.'/controller.php',
|
||||
'target'=>$controllerFile,
|
||||
'callback'=>array($this,'generateController'),
|
||||
'params'=>array($controllerClass,$modelClass),
|
||||
),
|
||||
);
|
||||
|
||||
if($functionalTestPath!==false)
|
||||
{
|
||||
$list[$modelClass.'Test.php']=array(
|
||||
'source'=>$templatePath.'/test.php',
|
||||
'target'=>$functionalTestPath.DIRECTORY_SEPARATOR.$modelClass.'Test.php',
|
||||
'callback'=>array($this,'generateTest'),
|
||||
'params'=>array($controllerID,$fixtureName,$modelClass),
|
||||
);
|
||||
}
|
||||
|
||||
foreach($this->actions as $action)
|
||||
{
|
||||
$list[$action.'.php']=array(
|
||||
'source'=>$templatePath.'/'.$action.'.php',
|
||||
'target'=>$viewPath.'/'.$action.'.php',
|
||||
'callback'=>array($this,'generateView'),
|
||||
'params'=>$modelClass,
|
||||
);
|
||||
}
|
||||
|
||||
$this->copyFiles($list);
|
||||
|
||||
if($module instanceof CWebModule)
|
||||
$moduleID=$module->id.'/';
|
||||
else
|
||||
$moduleID='';
|
||||
|
||||
echo "\nCrud '{$controllerID}' has been successfully created. You may access it via:\n";
|
||||
echo "http://hostname/path/to/index.php?r={$moduleID}{$controllerID}\n";
|
||||
}
|
||||
|
||||
public function generateController($source,$params)
|
||||
{
|
||||
list($controllerClass,$modelClass)=$params;
|
||||
$model=CActiveRecord::model($modelClass);
|
||||
$id=$model->tableSchema->primaryKey;
|
||||
if($id===null)
|
||||
throw new ShellException(Yii::t('yii','Error: Table "{table}" does not have a primary key.',array('{table}'=>$model->tableName())));
|
||||
elseif(is_array($id))
|
||||
throw new ShellException(Yii::t('yii','Error: Table "{table}" has a composite primary key which is not supported by crud command.',array('{table}'=>$model->tableName())));
|
||||
|
||||
if(!is_file($source)) // fall back to default ones
|
||||
$source=YII_PATH.'/cli/views/shell/crud/'.basename($source);
|
||||
|
||||
return $this->renderFile($source,array(
|
||||
'ID'=>$id,
|
||||
'controllerClass'=>$controllerClass,
|
||||
'modelClass'=>$modelClass,
|
||||
),true);
|
||||
}
|
||||
|
||||
public function generateView($source,$modelClass)
|
||||
{
|
||||
$model=CActiveRecord::model($modelClass);
|
||||
$table=$model->getTableSchema();
|
||||
$columns=$table->columns;
|
||||
if(!is_file($source)) // fall back to default ones
|
||||
$source=YII_PATH.'/cli/views/shell/crud/'.basename($source);
|
||||
return $this->renderFile($source,array(
|
||||
'ID'=>$table->primaryKey,
|
||||
'modelClass'=>$modelClass,
|
||||
'columns'=>$columns),true);
|
||||
}
|
||||
|
||||
public function generateTest($source,$params)
|
||||
{
|
||||
list($controllerID,$fixtureName,$modelClass)=$params;
|
||||
if(!is_file($source)) // fall back to default ones
|
||||
$source=YII_PATH.'/cli/views/shell/crud/'.basename($source);
|
||||
return $this->renderFile($source, array(
|
||||
'controllerID'=>$controllerID,
|
||||
'fixtureName'=>$fixtureName,
|
||||
'modelClass'=>$modelClass,
|
||||
),true);
|
||||
}
|
||||
|
||||
public function generateInputLabel($modelClass,$column)
|
||||
{
|
||||
return "CHtml::activeLabelEx(\$model,'{$column->name}')";
|
||||
}
|
||||
|
||||
public function generateInputField($modelClass,$column)
|
||||
{
|
||||
if($column->type==='boolean')
|
||||
return "CHtml::activeCheckBox(\$model,'{$column->name}')";
|
||||
elseif(stripos($column->dbType,'text')!==false)
|
||||
return "CHtml::activeTextArea(\$model,'{$column->name}',array('rows'=>6, 'cols'=>50))";
|
||||
else
|
||||
{
|
||||
if(preg_match('/^(password|pass|passwd|passcode)$/i',$column->name))
|
||||
$inputField='activePasswordField';
|
||||
else
|
||||
$inputField='activeTextField';
|
||||
|
||||
if($column->type!=='string' || $column->size===null)
|
||||
return "CHtml::{$inputField}(\$model,'{$column->name}')";
|
||||
else
|
||||
{
|
||||
if(($size=$maxLength=$column->size)>60)
|
||||
$size=60;
|
||||
return "CHtml::{$inputField}(\$model,'{$column->name}',array('size'=>$size,'maxlength'=>$maxLength))";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function generateActiveLabel($modelClass,$column)
|
||||
{
|
||||
return "\$form->labelEx(\$model,'{$column->name}')";
|
||||
}
|
||||
|
||||
public function generateActiveField($modelClass,$column)
|
||||
{
|
||||
if($column->type==='boolean')
|
||||
return "\$form->checkBox(\$model,'{$column->name}')";
|
||||
elseif(stripos($column->dbType,'text')!==false)
|
||||
return "\$form->textArea(\$model,'{$column->name}',array('rows'=>6, 'cols'=>50))";
|
||||
else
|
||||
{
|
||||
if(preg_match('/^(password|pass|passwd|passcode)$/i',$column->name))
|
||||
$inputField='passwordField';
|
||||
else
|
||||
$inputField='textField';
|
||||
|
||||
if($column->type!=='string' || $column->size===null)
|
||||
return "\$form->{$inputField}(\$model,'{$column->name}')";
|
||||
else
|
||||
{
|
||||
if(($size=$maxLength=$column->size)>60)
|
||||
$size=60;
|
||||
return "\$form->{$inputField}(\$model,'{$column->name}',array('size'=>$size,'maxlength'=>$maxLength))";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function guessNameColumn($columns)
|
||||
{
|
||||
foreach($columns as $column)
|
||||
{
|
||||
if(!strcasecmp($column->name,'name'))
|
||||
return $column->name;
|
||||
}
|
||||
foreach($columns as $column)
|
||||
{
|
||||
if(!strcasecmp($column->name,'title'))
|
||||
return $column->name;
|
||||
}
|
||||
foreach($columns as $column)
|
||||
{
|
||||
if($column->isPrimaryKey)
|
||||
return $column->name;
|
||||
}
|
||||
return 'id';
|
||||
}
|
||||
|
||||
public function class2id($className)
|
||||
{
|
||||
return trim(strtolower(str_replace('_','-',preg_replace('/(?<![A-Z])[A-Z]/', '-\0', $className))),'-');
|
||||
}
|
||||
|
||||
public function class2name($className,$pluralize=false)
|
||||
{
|
||||
if($pluralize)
|
||||
$className=$this->pluralize($className);
|
||||
return ucwords(trim(strtolower(str_replace(array('-','_'),' ',preg_replace('/(?<![A-Z])[A-Z]/', ' \0', $className)))));
|
||||
}
|
||||
}
|
||||
122
framework/cli/commands/shell/FormCommand.php
Normal file
122
framework/cli/commands/shell/FormCommand.php
Normal file
@@ -0,0 +1,122 @@
|
||||
<?php
|
||||
/**
|
||||
* FormCommand class file.
|
||||
*
|
||||
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||
* @link http://www.yiiframework.com/
|
||||
* @copyright 2008-2013 Yii Software LLC
|
||||
* @license http://www.yiiframework.com/license/
|
||||
*/
|
||||
|
||||
/**
|
||||
* FormCommand generates a form view based on a specified model.
|
||||
*
|
||||
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||
* @package system.cli.commands.shell
|
||||
* @since 1.0
|
||||
*/
|
||||
class FormCommand extends CConsoleCommand
|
||||
{
|
||||
/**
|
||||
* @var string the directory that contains templates for the form command.
|
||||
* Defaults to null, meaning using 'framework/cli/views/shell/form'.
|
||||
* If you set this path and some views are missing in the directory,
|
||||
* the default views will be used.
|
||||
*/
|
||||
public $templatePath;
|
||||
|
||||
public function getHelp()
|
||||
{
|
||||
return <<<EOD
|
||||
USAGE
|
||||
form <model-class> <view-name> [scenario]
|
||||
|
||||
DESCRIPTION
|
||||
This command generates a form view that can be used to collect inputs
|
||||
for the specified model.
|
||||
|
||||
PARAMETERS
|
||||
* model-class: required, model class. This can be either the name of
|
||||
the model class (e.g. 'ContactForm') or the path alias of the model
|
||||
class file (e.g. 'application.models.ContactForm'). The former can
|
||||
be used only if the class can be autoloaded.
|
||||
|
||||
* view-name: required, the name of the view to be generated. This should
|
||||
be the path alias of the view script (e.g. 'application.views.site.contact').
|
||||
|
||||
* scenario: optional, the name of the scenario in which the model is used
|
||||
(e.g. 'update', 'login'). This determines which model attributes the
|
||||
generated form view will be used to collect user inputs for. If this
|
||||
is not provided, the scenario will be assumed to be '' (empty string).
|
||||
|
||||
EXAMPLES
|
||||
* Generates the view script for the 'ContactForm' model:
|
||||
form ContactForm application.views.site.contact
|
||||
|
||||
EOD;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the action.
|
||||
* @param array $args command line parameters specific for this command
|
||||
* @return integer|null non zero application exit code for help or null on success
|
||||
*/
|
||||
public function run($args)
|
||||
{
|
||||
if(!isset($args[0],$args[1]))
|
||||
{
|
||||
echo "Error: both model class and view name are required.\n";
|
||||
echo $this->getHelp();
|
||||
return 1;
|
||||
}
|
||||
$scenario=isset($args[2]) ? $args[2] : '';
|
||||
$modelClass=Yii::import($args[0],true);
|
||||
$model=new $modelClass($scenario);
|
||||
$attributes=$model->getSafeAttributeNames();
|
||||
|
||||
$templatePath=$this->templatePath===null?YII_PATH.'/cli/views/shell/form':$this->templatePath;
|
||||
$viewPath=Yii::getPathOfAlias($args[1]);
|
||||
$viewName=basename($viewPath);
|
||||
$viewPath.='.php';
|
||||
$params=array(
|
||||
'modelClass'=>$modelClass,
|
||||
'viewName'=>$viewName,
|
||||
'attributes'=>$attributes,
|
||||
);
|
||||
$list=array(
|
||||
basename($viewPath)=>array(
|
||||
'source'=>$templatePath.'/form.php',
|
||||
'target'=>$viewPath,
|
||||
'callback'=>array($this,'generateForm'),
|
||||
'params'=>$params,
|
||||
),
|
||||
);
|
||||
|
||||
$this->copyFiles($list);
|
||||
|
||||
$actionFile=$templatePath.'/action.php';
|
||||
if(!is_file($actionFile)) // fall back to default ones
|
||||
$actionFile=YII_PATH.'/cli/views/shell/form/action.php';
|
||||
|
||||
echo "The following form view has been successfully created:\n";
|
||||
echo "\t$viewPath\n\n";
|
||||
echo "You may use the following code in your controller action:\n\n";
|
||||
echo $this->renderFile($actionFile,$params,true);
|
||||
echo "\n";
|
||||
}
|
||||
|
||||
public function generateForm($source,$params)
|
||||
{
|
||||
if(!is_file($source)) // fall back to default ones
|
||||
$source=YII_PATH.'/cli/views/shell/form/'.basename($source);
|
||||
|
||||
return $this->renderFile($source,$params,true);
|
||||
}
|
||||
|
||||
public function class2id($className)
|
||||
{
|
||||
if(strrpos($className,'Form')===strlen($className)-4)
|
||||
$className=substr($className,0,strlen($className)-4);
|
||||
return trim(strtolower(str_replace('_','-',preg_replace('/(?<![A-Z])[A-Z]/', '-\0', $className))),'-');
|
||||
}
|
||||
}
|
||||
78
framework/cli/commands/shell/HelpCommand.php
Normal file
78
framework/cli/commands/shell/HelpCommand.php
Normal file
@@ -0,0 +1,78 @@
|
||||
<?php
|
||||
/**
|
||||
* HelpCommand class file.
|
||||
*
|
||||
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||
* @link http://www.yiiframework.com/
|
||||
* @copyright 2008-2013 Yii Software LLC
|
||||
* @license http://www.yiiframework.com/license/
|
||||
*/
|
||||
|
||||
/**
|
||||
* HelpCommand displays help information for commands under yiic shell.
|
||||
*
|
||||
* @property string $help The command description.
|
||||
*
|
||||
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||
* @package system.cli.commands.shell
|
||||
* @since 1.0
|
||||
*/
|
||||
class HelpCommand extends CConsoleCommand
|
||||
{
|
||||
/**
|
||||
* Execute the action.
|
||||
* @param array $args command line parameters specific for this command
|
||||
* @return integer non zero application exit code for help
|
||||
*/
|
||||
public function run($args)
|
||||
{
|
||||
$runner=$this->getCommandRunner();
|
||||
$commands=$runner->commands;
|
||||
if(isset($args[0]))
|
||||
$name=strtolower($args[0]);
|
||||
if(!isset($args[0]) || !isset($commands[$name]))
|
||||
{
|
||||
echo <<<EOD
|
||||
At the prompt, you may enter a PHP statement or one of the following commands:
|
||||
|
||||
EOD;
|
||||
$commandNames=array_keys($commands);
|
||||
sort($commandNames);
|
||||
echo ' - '.implode("\n - ",$commandNames);
|
||||
echo <<<EOD
|
||||
|
||||
|
||||
Type 'help <command-name>' for details about a command.
|
||||
|
||||
To expand the above command list, place your command class files
|
||||
under 'protected/commands/shell', or a directory specified
|
||||
by the 'YIIC_SHELL_COMMAND_PATH' environment variable. The command class
|
||||
must extend from CConsoleCommand.
|
||||
|
||||
EOD;
|
||||
}
|
||||
else
|
||||
echo $runner->createCommand($name)->getHelp();
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides the command description.
|
||||
* @return string the command description.
|
||||
*/
|
||||
public function getHelp()
|
||||
{
|
||||
return <<<EOD
|
||||
USAGE
|
||||
help [command-name]
|
||||
|
||||
DESCRIPTION
|
||||
Display the help information for the specified command.
|
||||
If the command name is not given, all commands will be listed.
|
||||
|
||||
PARAMETERS
|
||||
* command-name: optional, the name of the command to show help information.
|
||||
|
||||
EOD;
|
||||
}
|
||||
}
|
||||
488
framework/cli/commands/shell/ModelCommand.php
Normal file
488
framework/cli/commands/shell/ModelCommand.php
Normal file
@@ -0,0 +1,488 @@
|
||||
<?php
|
||||
/**
|
||||
* ModelCommand class file.
|
||||
*
|
||||
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||
* @link http://www.yiiframework.com/
|
||||
* @copyright 2008-2013 Yii Software LLC
|
||||
* @license http://www.yiiframework.com/license/
|
||||
*/
|
||||
|
||||
/**
|
||||
* ModelCommand generates a model class.
|
||||
*
|
||||
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||
* @package system.cli.commands.shell
|
||||
* @since 1.0
|
||||
*/
|
||||
class ModelCommand extends CConsoleCommand
|
||||
{
|
||||
/**
|
||||
* @var string the directory that contains templates for the model command.
|
||||
* Defaults to null, meaning using 'framework/cli/views/shell/model'.
|
||||
* If you set this path and some views are missing in the directory,
|
||||
* the default views will be used.
|
||||
*/
|
||||
public $templatePath;
|
||||
/**
|
||||
* @var string the directory that contains test fixtures.
|
||||
* Defaults to null, meaning using 'protected/tests/fixtures'.
|
||||
* If this is false, it means fixture file should NOT be generated.
|
||||
*/
|
||||
public $fixturePath;
|
||||
/**
|
||||
* @var string the directory that contains unit test classes.
|
||||
* Defaults to null, meaning using 'protected/tests/unit'.
|
||||
* If this is false, it means unit test file should NOT be generated.
|
||||
*/
|
||||
public $unitTestPath;
|
||||
|
||||
private $_schema;
|
||||
private $_relations; // where we keep table relations
|
||||
private $_tables;
|
||||
private $_classes;
|
||||
|
||||
public function getHelp()
|
||||
{
|
||||
return <<<EOD
|
||||
USAGE
|
||||
model <class-name> [table-name]
|
||||
|
||||
DESCRIPTION
|
||||
This command generates a model class with the specified class name.
|
||||
|
||||
PARAMETERS
|
||||
* class-name: required, model class name. By default, the generated
|
||||
model class file will be placed under the directory aliased as
|
||||
'application.models'. To override this default, specify the class
|
||||
name in terms of a path alias, e.g., 'application.somewhere.ClassName'.
|
||||
|
||||
If the model class belongs to a module, it should be specified
|
||||
as 'ModuleID.models.ClassName'.
|
||||
|
||||
If the class name ends with '*', then a model class will be generated
|
||||
for EVERY table in the database.
|
||||
|
||||
If the class name contains a regular expression deliminated by slashes,
|
||||
then a model class will be generated for those tables whose name
|
||||
matches the regular expression. If the regular expression contains
|
||||
sub-patterns, the first sub-pattern will be used to generate the model
|
||||
class name.
|
||||
|
||||
* table-name: optional, the associated database table name. If not given,
|
||||
it is assumed to be the model class name.
|
||||
|
||||
Note, when the class name ends with '*', this parameter will be
|
||||
ignored.
|
||||
|
||||
EXAMPLES
|
||||
* Generates the Post model:
|
||||
model Post
|
||||
|
||||
* Generates the Post model which is associated with table 'posts':
|
||||
model Post posts
|
||||
|
||||
* Generates the Post model which should belong to module 'admin':
|
||||
model admin.models.Post
|
||||
|
||||
* Generates a model class for every table in the current database:
|
||||
model *
|
||||
|
||||
* Same as above, but the model class files should be generated
|
||||
under 'protected/models2':
|
||||
model application.models2.*
|
||||
|
||||
* Generates a model class for every table whose name is prefixed
|
||||
with 'tbl_' in the current database. The model class will not
|
||||
contain the table prefix.
|
||||
model /^tbl_(.*)$/
|
||||
|
||||
* Same as above, but the model class files should be generated
|
||||
under 'protected/models2':
|
||||
model application.models2./^tbl_(.*)$/
|
||||
|
||||
EOD;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the given table is a "many to many" helper table.
|
||||
* Their PK has 2 fields, and both of those fields are also FK to other separate tables.
|
||||
* @param CDbTableSchema $table table to inspect
|
||||
* @return boolean true if table matches description of helper table.
|
||||
*/
|
||||
protected function isRelationTable($table)
|
||||
{
|
||||
$pk=$table->primaryKey;
|
||||
return (count($pk) === 2 // we want 2 columns
|
||||
&& isset($table->foreignKeys[$pk[0]]) // pk column 1 is also a foreign key
|
||||
&& isset($table->foreignKeys[$pk[1]]) // pk column 2 is also a foreign key
|
||||
&& $table->foreignKeys[$pk[0]][0] !== $table->foreignKeys[$pk[1]][0]); // and the foreign keys point different tables
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate code to put in ActiveRecord class's relations() function.
|
||||
* @return array indexed by table names, each entry contains array of php code to go in appropriate ActiveRecord class.
|
||||
* Empty array is returned if database couldn't be connected.
|
||||
*/
|
||||
protected function generateRelations()
|
||||
{
|
||||
$this->_relations=array();
|
||||
$this->_classes=array();
|
||||
foreach($this->_schema->getTables() as $table)
|
||||
{
|
||||
$tableName=$table->name;
|
||||
|
||||
if ($this->isRelationTable($table))
|
||||
{
|
||||
$pks=$table->primaryKey;
|
||||
$fks=$table->foreignKeys;
|
||||
|
||||
$table0=$fks[$pks[1]][0];
|
||||
$table1=$fks[$pks[0]][0];
|
||||
$className0=$this->getClassName($table0);
|
||||
$className1=$this->getClassName($table1);
|
||||
|
||||
$unprefixedTableName=$this->removePrefix($tableName,true);
|
||||
|
||||
$relationName=$this->generateRelationName($table0, $table1, true);
|
||||
$this->_relations[$className0][$relationName]="array(self::MANY_MANY, '$className1', '$unprefixedTableName($pks[0], $pks[1])')";
|
||||
|
||||
$relationName=$this->generateRelationName($table1, $table0, true);
|
||||
$this->_relations[$className1][$relationName]="array(self::MANY_MANY, '$className0', '$unprefixedTableName($pks[0], $pks[1])')";
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->_classes[$tableName]=$className=$this->getClassName($tableName);
|
||||
foreach ($table->foreignKeys as $fkName => $fkEntry)
|
||||
{
|
||||
// Put table and key name in variables for easier reading
|
||||
$refTable=$fkEntry[0]; // Table name that current fk references to
|
||||
$refKey=$fkEntry[1]; // Key in that table being referenced
|
||||
$refClassName=$this->getClassName($refTable);
|
||||
|
||||
// Add relation for this table
|
||||
$relationName=$this->generateRelationName($tableName, $fkName, false);
|
||||
$this->_relations[$className][$relationName]="array(self::BELONGS_TO, '$refClassName', '$fkName')";
|
||||
|
||||
// Add relation for the referenced table
|
||||
$relationType=$table->primaryKey === $fkName ? 'HAS_ONE' : 'HAS_MANY';
|
||||
$relationName=$this->generateRelationName($refTable, $this->removePrefix($tableName), $relationType==='HAS_MANY');
|
||||
$this->_relations[$refClassName][$relationName]="array(self::$relationType, '$className', '$fkName')";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected function getClassName($tableName)
|
||||
{
|
||||
return isset($this->_tables[$tableName]) ? $this->_tables[$tableName] : $this->generateClassName($tableName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates model class name based on a table name
|
||||
* @param string $tableName the table name
|
||||
* @return string the generated model class name
|
||||
*/
|
||||
protected function generateClassName($tableName)
|
||||
{
|
||||
return str_replace(' ','',
|
||||
ucwords(
|
||||
trim(
|
||||
strtolower(
|
||||
str_replace(array('-','_'),' ',
|
||||
preg_replace('/(?<![A-Z])[A-Z]/', ' \0', $tableName))))));
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates the mapping table between table names and class names.
|
||||
* @param CDbSchema $schema the database schema
|
||||
* @param string $pattern a regular expression that may be used to filter table names
|
||||
*/
|
||||
protected function generateClassNames($schema,$pattern=null)
|
||||
{
|
||||
$this->_tables=array();
|
||||
foreach($schema->getTableNames() as $name)
|
||||
{
|
||||
if($pattern===null)
|
||||
$this->_tables[$name]=$this->generateClassName($this->removePrefix($name));
|
||||
elseif(preg_match($pattern,$name,$matches))
|
||||
{
|
||||
if(count($matches)>1 && !empty($matches[1]))
|
||||
$className=$this->generateClassName($matches[1]);
|
||||
else
|
||||
$className=$this->generateClassName($matches[0]);
|
||||
$this->_tables[$name]=empty($className) ? $name : $className;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a name for use as a relation name (inside relations() function in a model).
|
||||
* @param string $tableName the name of the table to hold the relation
|
||||
* @param string $fkName the foreign key name
|
||||
* @param boolean $multiple whether the relation would contain multiple objects
|
||||
* @return string the generated relation name
|
||||
*/
|
||||
protected function generateRelationName($tableName, $fkName, $multiple)
|
||||
{
|
||||
if(strcasecmp(substr($fkName,-2),'id')===0 && strcasecmp($fkName,'id'))
|
||||
$relationName=rtrim(substr($fkName, 0, -2),'_');
|
||||
else
|
||||
$relationName=$fkName;
|
||||
$relationName[0]=strtolower($relationName);
|
||||
|
||||
$rawName=$relationName;
|
||||
if($multiple)
|
||||
$relationName=$this->pluralize($relationName);
|
||||
|
||||
$table=$this->_schema->getTable($tableName);
|
||||
$i=0;
|
||||
while(isset($table->columns[$relationName]))
|
||||
$relationName=$rawName.($i++);
|
||||
return $relationName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the action.
|
||||
* @param array $args command line parameters specific for this command
|
||||
* @return integer|null non zero application exit code for help or null on success
|
||||
*/
|
||||
public function run($args)
|
||||
{
|
||||
if(!isset($args[0]))
|
||||
{
|
||||
echo "Error: model class name is required.\n";
|
||||
echo $this->getHelp();
|
||||
return 1;
|
||||
}
|
||||
$className=$args[0];
|
||||
|
||||
if(($db=Yii::app()->getDb())===null)
|
||||
{
|
||||
echo "Error: an active 'db' connection is required.\n";
|
||||
echo "If you already added 'db' component in application configuration,\n";
|
||||
echo "please quit and re-enter the yiic shell.\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
$db->active=true;
|
||||
$this->_schema=$db->schema;
|
||||
|
||||
if(!preg_match('/^[\w\.\-\*]*(.*?)$/',$className,$matches))
|
||||
{
|
||||
echo "Error: model class name is invalid.\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
if(empty($matches[1])) // without regular expression
|
||||
{
|
||||
$this->generateClassNames($this->_schema);
|
||||
if(($pos=strrpos($className,'.'))===false)
|
||||
$basePath=Yii::getPathOfAlias('application.models');
|
||||
else
|
||||
{
|
||||
$basePath=Yii::getPathOfAlias(substr($className,0,$pos));
|
||||
$className=substr($className,$pos+1);
|
||||
}
|
||||
if($className==='*') // generate all models
|
||||
$this->generateRelations();
|
||||
else
|
||||
{
|
||||
$tableName=isset($args[1])?$args[1]:$className;
|
||||
$tableName=$this->addPrefix($tableName);
|
||||
$this->_tables[$tableName]=$className;
|
||||
$this->generateRelations();
|
||||
$this->_classes=array($tableName=>$className);
|
||||
}
|
||||
}
|
||||
else // with regular expression
|
||||
{
|
||||
$pattern=$matches[1];
|
||||
$pos=strrpos($className,$pattern);
|
||||
if($pos>0) // only regexp is given
|
||||
$basePath=Yii::getPathOfAlias(rtrim(substr($className,0,$pos),'.'));
|
||||
else
|
||||
$basePath=Yii::getPathOfAlias('application.models');
|
||||
$this->generateClassNames($this->_schema,$pattern);
|
||||
$classes=$this->_tables;
|
||||
$this->generateRelations();
|
||||
$this->_classes=$classes;
|
||||
}
|
||||
|
||||
if(count($this->_classes)>1)
|
||||
{
|
||||
$entries=array();
|
||||
$count=0;
|
||||
foreach($this->_classes as $tableName=>$className)
|
||||
$entries[]=++$count.". $className ($tableName)";
|
||||
echo "The following model classes (tables) match your criteria:\n";
|
||||
echo implode("\n",$entries)."\n\n";
|
||||
if(!$this->confirm("Do you want to generate the above classes?"))
|
||||
return;
|
||||
}
|
||||
|
||||
$templatePath=$this->templatePath===null?YII_PATH.'/cli/views/shell/model':$this->templatePath;
|
||||
$fixturePath=$this->fixturePath===null?Yii::getPathOfAlias('application.tests.fixtures'):$this->fixturePath;
|
||||
$unitTestPath=$this->unitTestPath===null?Yii::getPathOfAlias('application.tests.unit'):$this->unitTestPath;
|
||||
|
||||
$list=array();
|
||||
$files=array();
|
||||
foreach ($this->_classes as $tableName=>$className)
|
||||
{
|
||||
$files[$className]=$classFile=$basePath.DIRECTORY_SEPARATOR.$className.'.php';
|
||||
$list['models/'.$className.'.php']=array(
|
||||
'source'=>$templatePath.DIRECTORY_SEPARATOR.'model.php',
|
||||
'target'=>$classFile,
|
||||
'callback'=>array($this,'generateModel'),
|
||||
'params'=>array($className,$tableName),
|
||||
);
|
||||
if($fixturePath!==false)
|
||||
{
|
||||
$list['fixtures/'.$tableName.'.php']=array(
|
||||
'source'=>$templatePath.DIRECTORY_SEPARATOR.'fixture.php',
|
||||
'target'=>$fixturePath.DIRECTORY_SEPARATOR.$tableName.'.php',
|
||||
'callback'=>array($this,'generateFixture'),
|
||||
'params'=>$this->_schema->getTable($tableName),
|
||||
);
|
||||
}
|
||||
if($unitTestPath!==false)
|
||||
{
|
||||
$fixtureName=$this->pluralize($className);
|
||||
$fixtureName[0]=strtolower($fixtureName);
|
||||
$list['unit/'.$className.'Test.php']=array(
|
||||
'source'=>$templatePath.DIRECTORY_SEPARATOR.'test.php',
|
||||
'target'=>$unitTestPath.DIRECTORY_SEPARATOR.$className.'Test.php',
|
||||
'callback'=>array($this,'generateTest'),
|
||||
'params'=>array($className,$fixtureName),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
$this->copyFiles($list);
|
||||
|
||||
foreach($files as $className=>$file)
|
||||
{
|
||||
if(!class_exists($className,false))
|
||||
include_once($file);
|
||||
}
|
||||
|
||||
$classes=implode(", ", $this->_classes);
|
||||
|
||||
echo <<<EOD
|
||||
|
||||
The following model classes are successfully generated:
|
||||
$classes
|
||||
|
||||
If you have a 'db' database connection, you can test these models now with:
|
||||
\$model={$className}::model()->find();
|
||||
print_r(\$model);
|
||||
|
||||
EOD;
|
||||
}
|
||||
|
||||
public function generateModel($source,$params)
|
||||
{
|
||||
list($className,$tableName)=$params;
|
||||
$rules=array();
|
||||
$labels=array();
|
||||
$relations=array();
|
||||
if(($table=$this->_schema->getTable($tableName))!==null)
|
||||
{
|
||||
$required=array();
|
||||
$integers=array();
|
||||
$numerical=array();
|
||||
$length=array();
|
||||
$safe=array();
|
||||
foreach($table->columns as $column)
|
||||
{
|
||||
$label=ucwords(trim(strtolower(str_replace(array('-','_'),' ',preg_replace('/(?<![A-Z])[A-Z]/', ' \0', $column->name)))));
|
||||
$label=preg_replace('/\s+/',' ',$label);
|
||||
if(strcasecmp(substr($label,-3),' id')===0)
|
||||
$label=substr($label,0,-3);
|
||||
$labels[$column->name]=$label;
|
||||
if($column->isPrimaryKey && $table->sequenceName!==null)
|
||||
continue;
|
||||
$r=!$column->allowNull && $column->defaultValue===null;
|
||||
if($r)
|
||||
$required[]=$column->name;
|
||||
if($column->type==='integer')
|
||||
$integers[]=$column->name;
|
||||
elseif($column->type==='double')
|
||||
$numerical[]=$column->name;
|
||||
elseif($column->type==='string' && $column->size>0)
|
||||
$length[$column->size][]=$column->name;
|
||||
elseif(!$column->isPrimaryKey && !$r)
|
||||
$safe[]=$column->name;
|
||||
}
|
||||
if($required!==array())
|
||||
$rules[]="array('".implode(', ',$required)."', 'required')";
|
||||
if($integers!==array())
|
||||
$rules[]="array('".implode(', ',$integers)."', 'numerical', 'integerOnly'=>true)";
|
||||
if($numerical!==array())
|
||||
$rules[]="array('".implode(', ',$numerical)."', 'numerical')";
|
||||
if($length!==array())
|
||||
{
|
||||
foreach($length as $len=>$cols)
|
||||
$rules[]="array('".implode(', ',$cols)."', 'length', 'max'=>$len)";
|
||||
}
|
||||
if($safe!==array())
|
||||
$rules[]="array('".implode(', ',$safe)."', 'safe')";
|
||||
|
||||
if(isset($this->_relations[$className]) && is_array($this->_relations[$className]))
|
||||
$relations=$this->_relations[$className];
|
||||
}
|
||||
else
|
||||
echo "Warning: the table '$tableName' does not exist in the database.\n";
|
||||
|
||||
if(!is_file($source)) // fall back to default ones
|
||||
$source=YII_PATH.'/cli/views/shell/model/'.basename($source);
|
||||
return $this->renderFile($source,array(
|
||||
'className'=>$className,
|
||||
'tableName'=>$this->removePrefix($tableName,true),
|
||||
'columns'=>isset($table) ? $table->columns : array(),
|
||||
'rules'=>$rules,
|
||||
'labels'=>$labels,
|
||||
'relations'=>$relations,
|
||||
),true);
|
||||
}
|
||||
|
||||
public function generateFixture($source,$table)
|
||||
{
|
||||
if(!is_file($source)) // fall back to default ones
|
||||
$source=YII_PATH.'/cli/views/shell/model/'.basename($source);
|
||||
return $this->renderFile($source, array(
|
||||
'table'=>$table,
|
||||
),true);
|
||||
}
|
||||
|
||||
public function generateTest($source,$params)
|
||||
{
|
||||
list($className,$fixtureName)=$params;
|
||||
if(!is_file($source)) // fall back to default ones
|
||||
$source=YII_PATH.'/cli/views/shell/model/'.basename($source);
|
||||
return $this->renderFile($source, array(
|
||||
'className'=>$className,
|
||||
'fixtureName'=>$fixtureName,
|
||||
),true);
|
||||
}
|
||||
|
||||
protected function removePrefix($tableName,$addBrackets=false)
|
||||
{
|
||||
$tablePrefix=Yii::app()->getDb()->tablePrefix;
|
||||
if($tablePrefix!='' && !strncmp($tableName,$tablePrefix,strlen($tablePrefix)))
|
||||
{
|
||||
$tableName=substr($tableName,strlen($tablePrefix));
|
||||
if($addBrackets)
|
||||
$tableName='{{'.$tableName.'}}';
|
||||
}
|
||||
return $tableName;
|
||||
}
|
||||
|
||||
protected function addPrefix($tableName)
|
||||
{
|
||||
$tablePrefix=Yii::app()->getDb()->tablePrefix;
|
||||
if($tablePrefix!='' && strncmp($tableName,$tablePrefix,strlen($tablePrefix)))
|
||||
$tableName=$tablePrefix.$tableName;
|
||||
return $tableName;
|
||||
}
|
||||
}
|
||||
93
framework/cli/commands/shell/ModuleCommand.php
Normal file
93
framework/cli/commands/shell/ModuleCommand.php
Normal file
@@ -0,0 +1,93 @@
|
||||
<?php
|
||||
/**
|
||||
* ModuleCommand class file.
|
||||
*
|
||||
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||
* @link http://www.yiiframework.com/
|
||||
* @copyright 2008-2013 Yii Software LLC
|
||||
* @license http://www.yiiframework.com/license/
|
||||
* @version $Id: ModuleCommand.php 433 2008-12-30 22:59:17Z qiang.xue $
|
||||
*/
|
||||
|
||||
/**
|
||||
* ModuleCommand generates a controller class.
|
||||
*
|
||||
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||
* @version $Id: ModuleCommand.php 433 2008-12-30 22:59:17Z qiang.xue $
|
||||
* @package system.cli.commands.shell
|
||||
*/
|
||||
class ModuleCommand extends CConsoleCommand
|
||||
{
|
||||
/**
|
||||
* @var string the directory that contains templates for the module command.
|
||||
* Defaults to null, meaning using 'framework/cli/views/shell/module'.
|
||||
* If you set this path and some views are missing in the directory,
|
||||
* the default views will be used.
|
||||
*/
|
||||
public $templatePath;
|
||||
|
||||
public function getHelp()
|
||||
{
|
||||
return <<<EOD
|
||||
USAGE
|
||||
module <module-ID>
|
||||
|
||||
DESCRIPTION
|
||||
This command generates an application module.
|
||||
|
||||
PARAMETERS
|
||||
* module-ID: required, module ID. It is case-sensitive.
|
||||
|
||||
EOD;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the action.
|
||||
* @param array $args command line parameters specific for this command
|
||||
* @return integer|null non zero application exit code for help or null on success
|
||||
*/
|
||||
public function run($args)
|
||||
{
|
||||
if(!isset($args[0]))
|
||||
{
|
||||
echo "Error: module ID is required.\n";
|
||||
echo $this->getHelp();
|
||||
return 1;
|
||||
}
|
||||
|
||||
$moduleID=$args[0];
|
||||
$moduleClass=ucfirst($moduleID).'Module';
|
||||
$modulePath=Yii::app()->getModulePath().DIRECTORY_SEPARATOR.$moduleID;
|
||||
|
||||
$sourceDir=$this->templatePath===null?YII_PATH.'/cli/views/shell/module':$this->templatePath;
|
||||
$list=$this->buildFileList($sourceDir,$modulePath);
|
||||
$list['module.php']['target']=$modulePath.DIRECTORY_SEPARATOR.$moduleClass.'.php';
|
||||
$list['module.php']['callback']=array($this,'generateModuleClass');
|
||||
$list['module.php']['params']=array(
|
||||
'moduleClass'=>$moduleClass,
|
||||
'moduleID'=>$moduleID,
|
||||
);
|
||||
$list[$moduleClass.'.php']=$list['module.php'];
|
||||
unset($list['module.php']);
|
||||
|
||||
$this->copyFiles($list);
|
||||
|
||||
echo <<<EOD
|
||||
|
||||
Module '{$moduleID}' has been created under the following folder:
|
||||
$modulePath
|
||||
|
||||
You may access it in the browser using the following URL:
|
||||
http://hostname/path/to/index.php?r=$moduleID
|
||||
|
||||
Note, the module needs to be installed first by adding '{$moduleID}'
|
||||
to the 'modules' property in the application configuration.
|
||||
|
||||
EOD;
|
||||
}
|
||||
|
||||
public function generateModuleClass($source,$params)
|
||||
{
|
||||
return $this->renderFile($source,$params,true);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user