diff options
Diffstat (limited to 'framework/cli/commands/shell/CrudCommand.php')
| -rw-r--r-- | framework/cli/commands/shell/CrudCommand.php | 327 |
1 files changed, 327 insertions, 0 deletions
diff --git a/framework/cli/commands/shell/CrudCommand.php b/framework/cli/commands/shell/CrudCommand.php new file mode 100644 index 0000000..5932dea --- /dev/null +++ b/framework/cli/commands/shell/CrudCommand.php @@ -0,0 +1,327 @@ +<?php +/** + * CrudCommand class file. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2011 Yii Software LLC + * @license http://www.yiiframework.com/license/ + * @version $Id: CrudCommand.php 2799 2011-01-01 19:31:13Z qiang.xue $ + */ + +/** + * CrudCommand generates code implementing CRUD operations. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @version $Id: CrudCommand.php 2799 2011-01-01 19:31:13Z qiang.xue $ + * @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 command line parameters specific for this command + */ + public function run($args) + { + if(!isset($args[0])) + { + echo "Error: data model class is required.\n"; + echo $this->getHelp(); + return; + } + $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()))); + else if(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}')"; + else if(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}')"; + else if(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))))); + } +} |
