' . $content . '';
+ } else {
+ return '
Preview is not available for this file type.
';
+ }
+ }
+ }
+ }
+ throw new HttpException(404, "Code file not found: $file");
+ }
+
+ public function actionDiff($id, $file)
+ {
+ $generator = $this->loadGenerator($id);
+ if ($generator->validate()) {
+ foreach ($generator->generate() as $f) {
+ if ($f->id === $file) {
+ return $this->renderPartial('diff', array(
+ 'diff' => $f->diff(),
+ ));
+ }
+ }
+ }
+ throw new HttpException(404, "Code file not found: $file");
+ }
+
+ public function createUrl($route, $params = array())
+ {
+ if (!isset($params['id']) && $this->generator !== null) {
+ foreach ($this->module->generators as $id => $generator) {
+ if ($generator === $this->generator) {
+ $params['id'] = $id;
+ break;
+ }
+ }
+ }
+ return parent::createUrl($route, $params);
+ }
+
+ /**
+ * Loads the generator with the specified ID.
+ * @param string $id the ID of the generator to be loaded.
+ * @return \yii\gii\Generator the loaded generator
+ * @throws \yii\web\HttpException
+ */
+ protected function loadGenerator($id)
+ {
+ if (isset($this->module->generators[$id])) {
+ $this->generator = $this->module->generators[$id];
+ $this->generator->loadStickyAttributes();
+ $this->generator->load($_POST);
+ return $this->generator;
+ } else {
+ throw new HttpException(404, "Code generator not found: $id");
+ }
+ }
+}
diff --git a/framework/yii/gii/generators/controller/Generator.php b/framework/yii/gii/generators/controller/Generator.php
new file mode 100644
index 0000000..c57b2b2
--- /dev/null
+++ b/framework/yii/gii/generators/controller/Generator.php
@@ -0,0 +1,227 @@
+
+ * @since 2.0
+ */
+class Generator extends \yii\gii\Generator
+{
+ /**
+ * @var string the controller ID
+ */
+ public $controller;
+ /**
+ * @var string the base class of the controller
+ */
+ public $baseClass = 'yii\web\Controller';
+ /**
+ * @var string the namespace of the controller class
+ */
+ public $ns = 'app\controllers';
+ /**
+ * @var string list of action IDs separated by commas or spaces
+ */
+ public $actions = 'index';
+
+ /**
+ * @inheritdoc
+ */
+ public function getName()
+ {
+ return 'Controller Generator';
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function getDescription()
+ {
+ return 'This generator helps you to quickly generate a new controller class,
+ one or several controller actions and their corresponding views.';
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function rules()
+ {
+ return array_merge(parent::rules(), array(
+ array('controller, actions, baseClass, ns', 'filter', 'filter' => 'trim'),
+ array('controller, baseClass', 'required'),
+ array('controller', 'match', 'pattern' => '/^[a-z\\-\\/]*$/', 'message' => 'Only a-z, dashes (-) and slashes (/) are allowed.'),
+ array('actions', 'match', 'pattern' => '/^[a-z\\-,\\s]*$/', 'message' => 'Only a-z, dashes (-), spaces and commas are allowed.'),
+ array('baseClass', 'match', 'pattern' => '/^[\w\\\\]*$/', 'message' => 'Only word characters and backslashes are allowed.'),
+ array('ns', 'match', 'pattern' => '/^[\w\\\\]*$/', 'message' => 'Only word characters and backslashes are allowed.'),
+ ));
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function attributeLabels()
+ {
+ return array(
+ 'baseClass' => 'Base Class',
+ 'controller' => 'Controller ID',
+ 'actions' => 'Action IDs',
+ 'ns' => 'Namespace',
+ );
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function requiredTemplates()
+ {
+ return array(
+ 'controller.php',
+ 'view.php',
+ );
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function stickyAttributes()
+ {
+ return array('ns', 'baseClass');
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function hints()
+ {
+ return array(
+ 'controller' => 'Controller ID should be in lower case and may contain module ID(s) separated by slashes. For example:
+
+ order
generates OrderController.php
+ order-item
generates OrderItemController.php
+ admin/user
generates UserController.php
within the admin
module.
+
',
+ 'actions' => 'Provide one or multiple action IDs to generate empty action method(s) in the controller. Separate multiple action IDs with commas or spaces.
+ Action IDs should be in lower case. For example:
+
+ index
generates actionIndex()
+ create-order
generates actionCreateOrder()
+
',
+ 'ns' => 'This is the namespace that the new controller class will use.',
+ 'baseClass' => 'This is the class that the new controller class will extend from. Please make sure the class exists and can be autoloaded.',
+ );
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function successMessage()
+ {
+ $actions = $this->getActionIDs();
+ if (in_array('index', $actions)) {
+ $route = $this->controller . '/index';
+ } else {
+ $route = $this->controller . '/' . reset($actions);
+ }
+ $link = Html::a('try it now', Yii::$app->getUrlManager()->createUrl($route), array('target' => '_blank'));
+ return "The controller has been generated successfully. You may $link.";
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function generate()
+ {
+ $files = array();
+
+ $files[] = new CodeFile(
+ $this->getControllerFile(),
+ $this->render('controller.php')
+ );
+
+ foreach ($this->getActionIDs() as $action) {
+ $files[] = new CodeFile(
+ $this->getViewFile($action),
+ $this->render('view.php', array('action' => $action))
+ );
+ }
+
+ return $files;
+ }
+
+ /**
+ * Normalizes [[actions]] into an array of action IDs.
+ * @return array an array of action IDs entered by the user
+ */
+ public function getActionIDs()
+ {
+ $actions = array_unique(preg_split('/[\s,]+/', $this->actions, -1, PREG_SPLIT_NO_EMPTY));
+ sort($actions);
+ return $actions;
+ }
+
+ /**
+ * @return string the controller class name without the namespace part.
+ */
+ public function getControllerClass()
+ {
+ return Inflector::id2camel($this->getControllerID()) . 'Controller';
+ }
+
+ /**
+ * @return string the controller ID (without the module ID prefix)
+ */
+ public function getControllerID()
+ {
+ if (($pos = strrpos($this->controller, '/')) !== false) {
+ return substr($this->controller, $pos + 1);
+ } else {
+ return $this->controller;
+ }
+ }
+
+ /**
+ * @return \yii\base\Module the module that the new controller belongs to
+ */
+ public function getModule()
+ {
+ if (($pos = strpos($this->controller, '/')) !== false) {
+ $id = substr($this->controller, 0, $pos);
+ if (($module = Yii::$app->getModule($id)) !== null) {
+ return $module;
+ }
+ }
+ return Yii::$app;
+ }
+
+ /**
+ * @return string the controller class file path
+ */
+ public function getControllerFile()
+ {
+ $module = $this->getModule();
+ return $module->getControllerPath() . '/' . $this->getControllerClass() . '.php';
+ }
+
+ /**
+ * @param string $action the action ID
+ * @return string the action view file path
+ */
+ public function getViewFile($action)
+ {
+ $module = $this->getModule();
+ return $module->getViewPath() . '/' . $this->getControllerID() . '/' . $action . '.php';
+ }
+}
diff --git a/framework/yii/gii/generators/controller/form.php b/framework/yii/gii/generators/controller/form.php
new file mode 100644
index 0000000..e4d2947
--- /dev/null
+++ b/framework/yii/gii/generators/controller/form.php
@@ -0,0 +1,10 @@
+field($generator, 'controller');
+echo $form->field($generator, 'actions');
+echo $form->field($generator, 'ns');
+echo $form->field($generator, 'baseClass');
diff --git a/framework/yii/gii/generators/controller/templates/controller.php b/framework/yii/gii/generators/controller/templates/controller.php
new file mode 100644
index 0000000..3829d54
--- /dev/null
+++ b/framework/yii/gii/generators/controller/templates/controller.php
@@ -0,0 +1,28 @@
+
+
+ns)): ?>
+namespace ns; ?>;
+
+
+class getControllerClass(); ?> extends baseClass, '\\') . "\n"; ?>
+{
+getActionIDs() as $action): ?>
+ public function action()
+ {
+ return $this->render('');
+ }
+
+
+}
diff --git a/framework/yii/gii/generators/controller/templates/view.php b/framework/yii/gii/generators/controller/templates/view.php
new file mode 100644
index 0000000..4b75d7a
--- /dev/null
+++ b/framework/yii/gii/generators/controller/templates/view.php
@@ -0,0 +1,22 @@
+
+/**
+ * @var yii\base\View $this
+ */
+"; ?>
+
+
getControllerID() . '/' . $action; ?>
+
+
+ You may change the content of this page by modifying
+ the file echo __FILE__; ?>
.
+
diff --git a/framework/yii/gii/generators/crud/Generator.php b/framework/yii/gii/generators/crud/Generator.php
new file mode 100644
index 0000000..15da2c2
--- /dev/null
+++ b/framework/yii/gii/generators/crud/Generator.php
@@ -0,0 +1,35 @@
+
+ * @since 2.0
+ */
+class Generator extends \yii\gii\Generator
+{
+ public function getName()
+ {
+ return 'CRUD Generator';
+ }
+
+ public function getDescription()
+ {
+ return 'This generator generates a controller and views that implement CRUD (Create, Read, Update, Delete)
+ operations for the specified data model.';
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function generate()
+ {
+ return array();
+ }
+}
diff --git a/framework/yii/gii/generators/form/Generator.php b/framework/yii/gii/generators/form/Generator.php
new file mode 100644
index 0000000..b0edb2a
--- /dev/null
+++ b/framework/yii/gii/generators/form/Generator.php
@@ -0,0 +1,155 @@
+
+ * @since 2.0
+ */
+class Generator extends \yii\gii\Generator
+{
+ public $modelClass;
+ public $viewPath = '@app/views';
+ public $viewName;
+ public $scenarioName;
+
+
+ /**
+ * @inheritdoc
+ */
+ public function getName()
+ {
+ return 'Form Generator';
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function getDescription()
+ {
+ return 'This generator generates a view script file that displays a form to collect input for the specified model class.';
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function generate()
+ {
+ $files = array();
+ $files[] = new CodeFile(
+ Yii::getAlias($this->viewPath) . '/' . $this->viewName . '.php',
+ $this->render('form.php')
+ );
+ return $files;
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function rules()
+ {
+ return array_merge(parent::rules(), array(
+ array('modelClass, viewName, scenarioName, viewPath', 'filter', 'filter' => 'trim'),
+ array('modelClass, viewName, viewPath', 'required'),
+ array('modelClass', 'match', 'pattern' => '/^[\w\\\\]*$/', 'message' => 'Only word characters and backslashes are allowed.'),
+ array('modelClass', 'validateClass', 'params' => array('extends' => Model::className())),
+ array('viewName', 'match', 'pattern' => '/^\w+[\\-\\/\w]*$/', 'message' => 'Only word characters, dashes and slashes are allowed.'),
+ array('viewPath', 'match', 'pattern' => '/^@?\w+[\\-\\/\w]*$/', 'message' => 'Only word characters, dashes, slashes and @ are allowed.'),
+ array('viewPath', 'validateViewPath'),
+ array('scenarioName', 'match', 'pattern' => '/^[\w\\-]+$/', 'message' => 'Only word characters and dashes are allowed.'),
+ ));
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function attributeLabels()
+ {
+ return array(
+ 'modelClass' => 'Model Class',
+ 'viewName' => 'View Name',
+ 'viewPath' => 'View Path',
+ 'scenarioName' => 'Scenario',
+ );
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function requiredTemplates()
+ {
+ return array(
+ 'form.php',
+ 'action.php',
+ );
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function stickyAttributes()
+ {
+ return array('viewPath', 'scenarioName');
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function hints()
+ {
+ return array(
+ 'modelClass' => 'This is the model class for collecting the form input. You should provide a fully qualified class name, e.g.,
app\models\Post
.',
+ 'viewName' => 'This is the view name with respect to the view path. For example,
site/index
would generate a
site/index.php
view file under the view path.',
+ 'viewPath' => 'This is the root view path to keep the generated view files. You may provide either a directory or a path alias, e.g.,
@app/views
.',
+ 'scenarioName' => 'This is the scenario to be used by the model when collecting the form input. If empty, the default scenario will be used.',
+ );
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function successMessage()
+ {
+ $code = highlight_string($this->render('action.php'), true);
+ return <<
The form has been generated successfully.
+You may add the following code in an appropriate controller class to invoke the view:
+$code
+EOD;
+ }
+
+ /**
+ * Validates [[viewPath]] to make sure it is a valid path or path alias and exists.
+ */
+ public function validateViewPath()
+ {
+ $path = Yii::getAlias($this->viewPath, false);
+ if ($path === false || !is_dir($path)) {
+ $this->addError('viewPath', 'View path does not exist.');
+ }
+ }
+
+ /**
+ * @return array list of safe attributes of [[modelClass]]
+ */
+ public function getModelAttributes()
+ {
+ /** @var Model $model */
+ $model = new $this->modelClass;
+ if (!empty($this->scenarioName)) {
+ $model->setScenario($this->scenarioName);
+ }
+ return $model->safeAttributes();
+ }
+}
diff --git a/framework/yii/gii/generators/form/form.php b/framework/yii/gii/generators/form/form.php
new file mode 100644
index 0000000..c04a26e
--- /dev/null
+++ b/framework/yii/gii/generators/form/form.php
@@ -0,0 +1,10 @@
+field($generator, 'viewName');
+echo $form->field($generator, 'modelClass');
+echo $form->field($generator, 'scenarioName');
+echo $form->field($generator, 'viewPath');
diff --git a/framework/yii/gii/generators/form/templates/action.php b/framework/yii/gii/generators/form/templates/action.php
new file mode 100644
index 0000000..fc5830e
--- /dev/null
+++ b/framework/yii/gii/generators/form/templates/action.php
@@ -0,0 +1,28 @@
+
+
+public function actionviewName), '_')); ?>()
+{
+ $model = new modelClass; ?>scenarioName) ? '' : "(array('scenario' => '{$generator->scenarioName}'))"; ?>;
+
+ if ($model->load($_POST)) {
+ if($model->validate()) {
+ // form inputs are valid, do something here
+ return;
+ }
+ }
+ return $this->render('viewName; ?>', array(
+ 'model' => $model,
+ ));
+}
diff --git a/framework/yii/gii/generators/form/templates/form.php b/framework/yii/gii/generators/form/templates/form.php
new file mode 100644
index 0000000..ad52fe9
--- /dev/null
+++ b/framework/yii/gii/generators/form/templates/form.php
@@ -0,0 +1,35 @@
+
+
+use yii\helpers\Html;
+use yii\widgets\ActiveForm;
+
+/**
+ * @var yii\base\View $this
+ * @var modelClass; ?> $model
+ * @var ActiveForm $form
+ */
+"; ?>
+
+
+
+ $form = ActiveForm::begin(); ?>
+
+ getModelAttributes() as $attribute): ?>
+ echo $form->field($model, ''); ?>
+
+
+
+ echo Html::submitButton('Submit', array('class' => 'btn btn-primary')); ?>
+
+ ActiveForm::end(); ?>
+
+
diff --git a/framework/yii/gii/generators/model/Generator.php b/framework/yii/gii/generators/model/Generator.php
new file mode 100644
index 0000000..7aa601a
--- /dev/null
+++ b/framework/yii/gii/generators/model/Generator.php
@@ -0,0 +1,532 @@
+
+ * @since 2.0
+ */
+class Generator extends \yii\gii\Generator
+{
+ public $db = 'db';
+ public $ns = 'app\models';
+ public $tableName;
+ public $modelClass;
+ public $baseClass = 'yii\db\ActiveRecord';
+ public $generateRelations = true;
+ public $generateLabelsFromComments = false;
+
+
+ /**
+ * @inheritdoc
+ */
+ public function getName()
+ {
+ return 'Model Generator';
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function getDescription()
+ {
+ return 'This generator generates an ActiveRecord class for the specified database table.';
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function rules()
+ {
+ return array_merge(parent::rules(), array(
+ array('db, ns, tableName, modelClass, baseClass', 'filter', 'filter' => 'trim'),
+ array('db, ns, tableName, baseClass', 'required'),
+ array('db, modelClass', 'match', 'pattern' => '/^\w+$/', 'message' => 'Only word characters are allowed.'),
+ array('ns, baseClass', 'match', 'pattern' => '/^[\w\\\\]+$/', 'message' => 'Only word characters and backslashes are allowed.'),
+ array('tableName', 'match', 'pattern' => '/^(\w+\.)?([\w\*]+)$/', 'message' => 'Only word characters, and optionally an asterisk and/or a dot are allowed.'),
+ array('db', 'validateDb'),
+ array('ns', 'validateNamespace'),
+ array('tableName', 'validateTableName'),
+ array('modelClass', 'validateModelClass'),
+ array('baseClass', 'validateClass', 'params' => array('extends' => ActiveRecord::className())),
+ array('generateRelations, generateLabelsFromComments', 'boolean'),
+ ));
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function attributeLabels()
+ {
+ return array(
+ 'ns' => 'Namespace',
+ 'db' => 'Database Connection ID',
+ 'tableName' => 'Table Name',
+ 'modelClass' => 'Model Class',
+ 'baseClass' => 'Base Class',
+ 'generateRelations' => 'Generate Relations',
+ 'generateLabelsFromComments' => 'Generate Labels from DB Comments',
+ );
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function hints()
+ {
+ return array(
+ 'ns' => 'This is the namespace of the ActiveRecord class to be generated, e.g., app\models
',
+ 'db' => 'This is the ID of the DB application component.',
+ 'tableName' => 'This is the name of the DB table that the new ActiveRecord class is associated with, e.g. tbl_post
.
+ The table name may consist of the DB schema part if needed, e.g. public.tbl_post
.
+ The table name may contain an asterisk at the end to match multiple table names, e.g. tbl_*
.
+ In this case, multiple ActiveRecord classes will be generated, one for each matching table name.',
+ 'modelClass' => 'This is the name of the ActiveRecord class to be generated. The class name should not contain
+ the namespace part as it is specified in "Namespace". You do not need to specify the class name
+ if "Table Name" contains an asterisk at the end, in which case multiple ActiveRecord classes will be generated.',
+ 'baseClass' => 'This is the base class of the new ActiveRecord class. It should be a fully qualified namespaced class name.',
+ 'generateRelations' => 'This indicates whether the generator should generate relations based on
+ foreign key constraints it detects in the database. Note that if your database contains too many tables,
+ you may want to uncheck this option to accelerate the code generation proc ess.',
+ 'generateLabelsFromComments' => 'This indicates whether the generator should generate attribute labels
+ by using the comments of the corresponding DB columns.',
+ );
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function requiredTemplates()
+ {
+ return array(
+ 'model.php',
+ );
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function stickyAttributes()
+ {
+ return array('ns', 'db', 'baseClass', 'generateRelations', 'generateLabelsFromComments');
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function generate()
+ {
+ $files = array();
+ $relations = $this->generateRelations();
+ $db = $this->getDbConnection();
+ foreach ($this->getTableNames() as $tableName) {
+ $className = $this->generateClassName($tableName);
+ $tableSchema = $db->getTableSchema($tableName);
+ $params = array(
+ 'tableName' => $tableName,
+ 'className' => $className,
+ 'tableSchema' => $tableSchema,
+ 'labels' => $this->generateLabels($tableSchema),
+ 'rules' => $this->generateRules($tableSchema),
+ 'relations' => isset($relations[$className]) ? $relations[$className] : array(),
+ );
+ $files[] = new CodeFile(
+ Yii::getAlias('@' . str_replace('\\', '/', $this->ns)) . '/' . $className . '.php',
+ $this->render('model.php', $params)
+ );
+ }
+
+ return $files;
+ }
+
+ /**
+ * Generates the attribute labels for the specified table.
+ * @param \yii\db\TableSchema $table the table schema
+ * @return array the generated attribute labels (name => label)
+ */
+ public function generateLabels($table)
+ {
+ $labels = array();
+ foreach ($table->columns as $column) {
+ if ($this->generateLabelsFromComments && !empty($column->comment)) {
+ $labels[$column->name] = $column->comment;
+ } elseif (!strcasecmp($column->name, 'id')) {
+ $labels[$column->name] = 'ID';
+ } else {
+ $label = Inflector::camel2words($column->name);
+ if (strcasecmp(substr($label, -3), ' id') === 0) {
+ $label = substr($label, 0, -3) . ' ID';
+ }
+ $labels[$column->name] = $label;
+ }
+ }
+ return $labels;
+ }
+
+ /**
+ * Generates validation rules for the specified table.
+ * @param \yii\db\TableSchema $table the table schema
+ * @return array the generated validation rules
+ */
+ public function generateRules($table)
+ {
+ $types = array();
+ $lengths = array();
+ foreach ($table->columns as $column) {
+ if ($column->autoIncrement) {
+ continue;
+ }
+ if (!$column->allowNull && $column->defaultValue === null) {
+ $types['required'][] = $column->name;
+ }
+ switch ($column->type) {
+ case Schema::TYPE_SMALLINT:
+ case Schema::TYPE_INTEGER:
+ case Schema::TYPE_BIGINT:
+ $types['integer'][] = $column->name;
+ break;
+ case Schema::TYPE_BOOLEAN:
+ $types['boolean'][] = $column->name;
+ break;
+ case Schema::TYPE_FLOAT:
+ case Schema::TYPE_DECIMAL:
+ case Schema::TYPE_MONEY:
+ $types['number'][] = $column->name;
+ break;
+ case Schema::TYPE_DATE:
+ case Schema::TYPE_TIME:
+ case Schema::TYPE_DATETIME:
+ case Schema::TYPE_TIMESTAMP:
+ $types['safe'][] = $column->name;
+ break;
+ default: // strings
+ if ($column->size > 0) {
+ $lengths[$column->size][] = $column->name;
+ } else {
+ $types['string'][] = $column->name;
+ }
+ }
+ }
+
+ $rules = array();
+ foreach ($types as $type => $columns) {
+ $rules[] = "array('" . implode(', ', $columns) . "', '$type')";
+ }
+ foreach ($lengths as $length => $columns) {
+ $rules[] = "array('" . implode(', ', $columns) . "', 'string', 'max' => $length)";
+ }
+
+ return $rules;
+ }
+
+ /**
+ * @return array the generated relation declarations
+ */
+ protected function generateRelations()
+ {
+ if (!$this->generateRelations) {
+ return array();
+ }
+
+ $db = $this->getDbConnection();
+
+ if (($pos = strpos($this->tableName, '.')) !== false) {
+ $schemaName = substr($this->tableName, 0, $pos);
+ } else {
+ $schemaName = '';
+ }
+
+ $relations = array();
+ foreach ($db->getSchema()->getTableSchemas($schemaName) as $table) {
+ $tableName = $table->name;
+ $className = $this->generateClassName($tableName);
+ foreach ($table->foreignKeys as $refs) {
+ $refTable = $refs[0];
+ unset($refs[0]);
+ $fks = array_keys($refs);
+ $refClassName = $this->generateClassName($refTable);
+
+ // Add relation for this table
+ $link = $this->generateRelationLink(array_flip($refs));
+ $relationName = $this->generateRelationName($relations, $className, $table, $fks[0], false);
+ $relations[$className][$relationName] = array(
+ "return \$this->hasOne('$refClassName', $link);",
+ $refClassName,
+ false,
+ );
+
+ // Add relation for the referenced table
+ $hasMany = false;
+ foreach ($fks as $key) {
+ if (!in_array($key, $table->primaryKey, true)) {
+ $hasMany = true;
+ break;
+ }
+ }
+ $link = $this->generateRelationLink($refs);
+ $relationName = $this->generateRelationName($relations, $refClassName, $refTable, $className, $hasMany);
+ $relations[$refClassName][$relationName] = array(
+ "return \$this->" . ($hasMany ? 'hasMany' : 'hasOne') . "('$className', $link);",
+ $className,
+ $hasMany,
+ );
+ }
+
+ if (($fks = $this->checkPivotTable($table)) === false) {
+ continue;
+ }
+ $table0 = $fks[$table->primaryKey[0]][0];
+ $table1 = $fks[$table->primaryKey[1]][0];
+ $className0 = $this->generateClassName($table0);
+ $className1 = $this->generateClassName($table1);
+
+ $link = $this->generateRelationLink(array($fks[$table->primaryKey[1]][1] => $table->primaryKey[1]));
+ $viaLink = $this->generateRelationLink(array($table->primaryKey[0] => $fks[$table->primaryKey[0]][1]));
+ $relationName = $this->generateRelationName($relations, $className0, $db->getTableSchema($table0), $table->primaryKey[1], true);
+ $relations[$className0][$relationName] = array(
+ "return \$this->hasMany('$className1', $link)->viaTable('{$table->name}', $viaLink);",
+ $className0,
+ true,
+ );
+
+ $link = $this->generateRelationLink(array($fks[$table->primaryKey[0]][1] => $table->primaryKey[0]));
+ $viaLink = $this->generateRelationLink(array($table->primaryKey[1] => $fks[$table->primaryKey[1]][1]));
+ $relationName = $this->generateRelationName($relations, $className1, $db->getTableSchema($table1), $table->primaryKey[0], true);
+ $relations[$className1][$relationName] = array(
+ "return \$this->hasMany('$className0', $link)->viaTable('{$table->name}', $viaLink);",
+ $className1,
+ true,
+ );
+ }
+ return $relations;
+ }
+
+ /**
+ * Generates the link parameter to be used in generating the relation declaration.
+ * @param array $refs reference constraint
+ * @return string the generated link parameter.
+ */
+ protected function generateRelationLink($refs)
+ {
+ $pairs = array();
+ foreach ($refs as $a => $b) {
+ $pairs[] = "'$a' => '$b'";
+ }
+ return 'array(' . implode(', ', $pairs) . ')';
+ }
+
+ /**
+ * Checks if the given table is a pivot table.
+ * For simplicity, this method only deals with the case where the pivot contains two PK columns,
+ * each referencing a column in a different table.
+ * @param \yii\db\TableSchema the table being checked
+ * @return array|boolean the relevant foreign key constraint information if the table is a pivot table,
+ * or false if the table is not a pivot table.
+ */
+ protected function checkPivotTable($table)
+ {
+ $pk = $table->primaryKey;
+ if (count($pk) !== 2) {
+ return false;
+ }
+ $fks = array();
+ foreach ($table->foreignKeys as $refs) {
+ if (count($refs) === 2) {
+ if (isset($refs[$pk[0]])) {
+ $fks[$pk[0]] = array($refs[0], $refs[$pk[0]]);
+ } elseif (isset($refs[$pk[1]])) {
+ $fks[$pk[1]] = array($refs[0], $refs[$pk[1]]);
+ }
+ }
+ }
+ if (count($fks) === 2 && $fks[$pk[0]][0] !== $fks[$pk[1]][0]) {
+ return $fks;
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Generate a relation name for the specified table and a base name.
+ * @param array $relations the relations being generated currently.
+ * @param string $className the class name that will contain the relation declarations
+ * @param \yii\db\TableSchema $table the table schema
+ * @param string $key a base name that the relation name may be generated from
+ * @param boolean $multiple whether this is a has-many relation
+ * @return string the relation name
+ */
+ protected function generateRelationName($relations, $className, $table, $key, $multiple)
+ {
+ if (strcasecmp(substr($key, -2), 'id') === 0 && strcasecmp($key, 'id')) {
+ $key = rtrim(substr($key, 0, -2), '_');
+ }
+ if ($multiple) {
+ $key = Inflector::pluralize($key);
+ }
+ $name = $rawName = Inflector::id2camel($key, '_');
+ $i = 0;
+ while (isset($table->columns[$name])) {
+ $name = $rawName . ($i++);
+ }
+ while (isset($relations[$className][$name])) {
+ $name = $rawName . ($i++);
+ }
+
+ return $name;
+ }
+
+ /**
+ * Validates the [[db]] attribute.
+ */
+ public function validateDb()
+ {
+ if (Yii::$app->hasComponent($this->db) === false) {
+ $this->addError('db', 'There is no application component named "db".');
+ } elseif (!Yii::$app->getComponent($this->db) instanceof Connection) {
+ $this->addError('db', 'The "db" application component must be a DB connection instance.');
+ }
+ }
+
+ /**
+ * Validates the [[ns]] attribute.
+ */
+ public function validateNamespace()
+ {
+ $this->ns = ltrim($this->ns, '\\');
+ $path = Yii::getAlias('@' . str_replace('\\', '/', $this->ns), false);
+ if ($path === false) {
+ $this->addError('ns', 'Namespace must be associated with an existing directory.');
+ }
+ }
+
+ /**
+ * Validates the [[modelClass]] attribute.
+ */
+ public function validateModelClass()
+ {
+ if ($this->isReservedKeyword($this->modelClass)) {
+ $this->addError('modelClass', 'Class name cannot be a reserved PHP keyword.');
+ }
+ if (strpos($this->tableName, '*') === false && $this->modelClass == '') {
+ $this->addError('modelClass', 'Model Class cannot be blank.');
+ }
+ }
+
+ /**
+ * Validates the [[tableName]] attribute.
+ */
+ public function validateTableName()
+ {
+ if (($pos = strpos($this->tableName, '*')) !== false && strpos($this->tableName, '*', $pos + 1) !== false) {
+ $this->addError('tableName', 'At most one asterisk is allowed.');
+ return;
+ }
+ $tables = $this->getTableNames();
+ if (empty($tables)) {
+ $this->addError('tableName', "Table '{$this->tableName}' does not exist.");
+ } else {
+ foreach ($tables as $table) {
+ $class = $this->generateClassName($table);
+ if ($this->isReservedKeyword($class)) {
+ $this->addError('tableName', "Table '$table' will generate a class which is a reserved PHP keyword.");
+ break;
+ }
+ }
+ }
+ }
+
+ private $_tableNames;
+ private $_classNames;
+
+ /**
+ * @return array the table names that match the pattern specified by [[tableName]].
+ */
+ protected function getTableNames()
+ {
+ if ($this->_tableNames !== null) {
+ return $this->_tableNames;
+ }
+ $db = $this->getDbConnection();
+ $tableNames = array();
+ if (strpos($this->tableName, '*') !== false) {
+ if (($pos = strrpos($this->tableName, '.')) !== false) {
+ $schema = substr($this->tableName, 0, $pos);
+ $pattern = '/^' . str_replace('*', '\w+', substr($this->tableName, $pos + 1)) . '$/';
+ } else {
+ $schema = '';
+ $pattern = '/^' . str_replace('*', '\w+', $this->tableName) . '$/';
+ }
+
+ foreach ($db->schema->getTableNames($schema) as $table) {
+ if (preg_match($pattern, $table)) {
+ $tableNames[] = $schema === '' ? $table : ($schema . '.' . $table);
+ }
+ }
+ } elseif (($table = $db->getTableSchema($this->tableName, true)) !== null) {
+ $tableNames[] = $this->tableName;
+ $this->_classNames[$this->tableName] = $this->modelClass;
+ }
+ return $this->_tableNames = $tableNames;
+ }
+
+ /**
+ * Generates a class name from the specified table name.
+ * @param string $tableName the table name (which may contain schema prefix)
+ * @return string the generated class name
+ */
+ protected function generateClassName($tableName)
+ {
+ if (isset($this->_classNames[$tableName])) {
+ return $this->_classNames[$tableName];
+ }
+
+ if (($pos = strrpos($tableName, '.')) !== false) {
+ $tableName = substr($tableName, $pos + 1);
+ }
+
+ $db = $this->getDbConnection();
+ $patterns = array();
+ if (strpos($this->tableName, '*') !== false) {
+ $pattern = $this->tableName;
+ if (($pos = strrpos($pattern, '.')) !== false) {
+ $pattern = substr($pattern, $pos + 1);
+ }
+ $patterns[] = '/^' . str_replace('*', '(\w+)', $pattern) . '$/';
+ }
+ if (!empty($db->tablePrefix)) {
+ $patterns[] = "/^{$db->tablePrefix}(.*?)|(.*?){$db->tablePrefix}$/";
+ } else {
+ $patterns[] = "/^tbl_(.*?)$/";
+ }
+
+ $className = $tableName;
+ foreach ($patterns as $pattern) {
+ if (preg_match($pattern, $tableName, $matches)) {
+ $className = $matches[1];
+ }
+ }
+ return $this->_classNames[$tableName] = Inflector::id2camel($className, '_');
+ }
+
+ /**
+ * @return Connection the DB connection as specified by [[db]].
+ */
+ protected function getDbConnection()
+ {
+ return Yii::$app->{$this->db};
+ }
+}
diff --git a/framework/yii/gii/generators/model/form.php b/framework/yii/gii/generators/model/form.php
new file mode 100644
index 0000000..9eca27c
--- /dev/null
+++ b/framework/yii/gii/generators/model/form.php
@@ -0,0 +1,14 @@
+field($generator, 'tableName');
+echo $form->field($generator, 'modelClass');
+echo $form->field($generator, 'ns');
+echo $form->field($generator, 'baseClass');
+echo $form->field($generator, 'db');
+echo $form->field($generator, 'generateRelations')->checkbox();
+echo $form->field($generator, 'generateLabelsFromComments')->checkbox();
diff --git a/framework/yii/gii/generators/model/templates/model.php b/framework/yii/gii/generators/model/templates/model.php
new file mode 100644
index 0000000..b194294
--- /dev/null
+++ b/framework/yii/gii/generators/model/templates/model.php
@@ -0,0 +1,72 @@
+label)
+ * @var string[] $rules list of validation rules
+ * @var array $relations list of relations (name=>relation declaration)
+ */
+
+echo "
+
+namespace ns; ?>;
+
+/**
+ * This is the model class for table "".
+ *
+columns as $column): ?>
+ * @property phpType} \${$column->name}\n"; ?>
+
+
+ *
+ $relation): ?>
+ * @property
+
+
+ */
+class extends baseClass, '\\') . "\n"; ?>
+{
+ /**
+ * @inheritdoc
+ */
+ public static function tableName()
+ {
+ return '';
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function rules()
+ {
+ return array();
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function attributeLabels()
+ {
+ return array(
+ $label): ?>
+ '" . addslashes($label) . "',\n"; ?>
+
+ );
+ }
+ $relation): ?>
+
+ /**
+ * @return \yii\db\ActiveRelation
+ */
+ public function get()
+ {
+
+ }
+
+}
diff --git a/framework/yii/gii/generators/module/Generator.php b/framework/yii/gii/generators/module/Generator.php
new file mode 100644
index 0000000..52fbfe4
--- /dev/null
+++ b/framework/yii/gii/generators/module/Generator.php
@@ -0,0 +1,167 @@
+
+ * @since 2.0
+ */
+class Generator extends \yii\gii\Generator
+{
+ public $moduleClass;
+ public $moduleID;
+
+ /**
+ * @inheritdoc
+ */
+ public function getName()
+ {
+ return 'Module Generator';
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function getDescription()
+ {
+ return 'This generator helps you to generate the skeleton code needed by a Yii module.';
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function rules()
+ {
+ return array_merge(parent::rules(), array(
+ array('moduleID, moduleClass', 'filter', 'filter' => 'trim'),
+ array('moduleID, moduleClass', 'required'),
+ array('moduleID', 'match', 'pattern' => '/^[\w\\-]+$/', 'message' => 'Only word characters and dashes are allowed.'),
+ array('moduleClass', 'match', 'pattern' => '/^[\w\\\\]*$/', 'message' => 'Only word characters and backslashes are allowed.'),
+ array('moduleClass', 'validateModuleClass'),
+ ));
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function attributeLabels()
+ {
+ return array(
+ 'moduleID' => 'Module ID',
+ 'moduleClass' => 'Module Class',
+ );
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function hints()
+ {
+ return array(
+ 'moduleID' => 'This refers to the ID of the module, e.g., admin
.',
+ 'moduleClass' => 'This is the fully qualified class name of the module, e.g., app\modules\admin\Module
.',
+ );
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function successMessage()
+ {
+ if (Yii::$app->hasModule($this->moduleID)) {
+ $link = Html::a('try it now', Yii::$app->getUrlManager()->createUrl($this->moduleID), array('target' => '_blank'));
+ return "The module has been generated successfully. You may $link.";
+ }
+
+ $output = <<The module has been generated successfully.
+To access the module, you need to modify the application configuration as follows:
+EOD;
+ $code = <<array(
+ '{$this->moduleID}' => array(
+ 'class' => '{$this->moduleClass}',
+ ),
+ ),
+ ......
+);
+EOD;
+
+ return $output . '' . highlight_string($code, true) . '
';
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function requiredTemplates()
+ {
+ return array(
+ 'module.php',
+ 'controller.php',
+ 'view.php',
+ );
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function generate()
+ {
+ $files = array();
+ $modulePath = $this->getModulePath();
+ $files[] = new CodeFile(
+ $modulePath . '/' . StringHelper::basename($this->moduleClass) . '.php',
+ $this->render("module.php")
+ );
+ $files[] = new CodeFile(
+ $modulePath . '/controllers/DefaultController.php',
+ $this->render("controller.php")
+ );
+ $files[] = new CodeFile(
+ $modulePath . '/views/default/index.php',
+ $this->render("view.php")
+ );
+
+ return $files;
+ }
+
+ /**
+ * Validates [[moduleClass]] to make sure it is a fully qualified class name.
+ */
+ public function validateModuleClass()
+ {
+ if (strpos($this->moduleClass, '\\') === false || Yii::getAlias('@' . str_replace('\\', '/', $this->moduleClass)) === false) {
+ $this->addError('moduleClass', 'Module class must be properly namespaced.');
+ }
+ }
+
+ /**
+ * @return boolean the directory that contains the module class
+ */
+ public function getModulePath()
+ {
+ return Yii::getAlias('@' . str_replace('\\', '/', substr($this->moduleClass, 0, strrpos($this->moduleClass, '\\'))));
+ }
+
+ /**
+ * @return string the controller namespace of the module.
+ */
+ public function getControllerNamespace()
+ {
+ return substr($this->moduleClass, 0, strrpos($this->moduleClass, '\\')) . '\controllers';
+ }
+}
diff --git a/framework/yii/gii/generators/module/form.php b/framework/yii/gii/generators/module/form.php
new file mode 100644
index 0000000..8a0cc88
--- /dev/null
+++ b/framework/yii/gii/generators/module/form.php
@@ -0,0 +1,13 @@
+
+
+field($generator, 'moduleClass');
+ echo $form->field($generator, 'moduleID');
+?>
+
diff --git a/framework/yii/gii/generators/module/templates/controller.php b/framework/yii/gii/generators/module/templates/controller.php
new file mode 100644
index 0000000..34dd961
--- /dev/null
+++ b/framework/yii/gii/generators/module/templates/controller.php
@@ -0,0 +1,21 @@
+
+
+namespace getControllerNamespace(); ?>;
+
+use yii\web\Controller;
+
+class DefaultController extends Controller
+{
+ public function actionIndex()
+ {
+ $this->render('index');
+ }
+}
diff --git a/framework/yii/gii/generators/module/templates/module.php b/framework/yii/gii/generators/module/templates/module.php
new file mode 100644
index 0000000..911cb29
--- /dev/null
+++ b/framework/yii/gii/generators/module/templates/module.php
@@ -0,0 +1,29 @@
+moduleClass;
+$pos = strrpos($className, '\\');
+$ns = ltrim(substr($className, 0, $pos), '\\');
+$className = substr($className, $pos + 1);
+
+echo "
+
+namespace ;
+
+
+class extends \yii\web\Module
+{
+ public $controllerNamespace = 'getControllerNamespace(); ?>';
+
+ public function init()
+ {
+ parent::init();
+
+ // custom initialization code goes here
+ }
+}
diff --git a/framework/yii/gii/generators/module/templates/view.php b/framework/yii/gii/generators/module/templates/view.php
new file mode 100644
index 0000000..d0e1ce6
--- /dev/null
+++ b/framework/yii/gii/generators/module/templates/view.php
@@ -0,0 +1,18 @@
+
+
+
echo $this->context->action->uniqueId; ?>
+
+ This is the view content for action " echo $this->context->action->id; ?>".
+ The action belongs to the controller " echo get_class($this->context); ?>"
+ in the " echo $this->context->module->id; ?>" module.
+
+
+ You may customize this page by editing the following file:
+ echo __FILE__; ?>
+
+
diff --git a/framework/yii/gii/views/default/diff.php b/framework/yii/gii/views/default/diff.php
new file mode 100644
index 0000000..bb4e455
--- /dev/null
+++ b/framework/yii/gii/views/default/diff.php
@@ -0,0 +1,15 @@
+
+
+
+
Diff is not supported for this file type.
+
+
Identical.
+
+
+
+
diff --git a/framework/yii/gii/views/default/index.php b/framework/yii/gii/views/default/index.php
new file mode 100644
index 0000000..7dd1fdd
--- /dev/null
+++ b/framework/yii/gii/views/default/index.php
@@ -0,0 +1,33 @@
+controller->module->generators;
+$activeGenerator = Yii::$app->controller->generator;
+$this->title = 'Welcome to Gii';
+?>
+
+
+
+
Start the fun with the following code generators:
+
+
+ $generator): ?>
+
+
getName()); ?>
+
getDescription(); ?>
+
$id), array('class' => 'btn btn-default')); ?>
+
+
+
+
+
Get More Generators
+
+
diff --git a/framework/yii/gii/views/default/view.php b/framework/yii/gii/views/default/view.php
new file mode 100644
index 0000000..821f6fc
--- /dev/null
+++ b/framework/yii/gii/views/default/view.php
@@ -0,0 +1,69 @@
+title = $generator->getName();
+$templates = array();
+foreach ($generator->templates as $name => $path) {
+ $templates[$name] = "$name ($path)";
+}
+?>
+
+
title); ?>
+
+
getDescription(); ?>
+
+ array('class' => ActiveField::className()))); ?>
+
+
+ renderFile($generator->formView(), array(
+ 'generator' => $generator,
+ 'form' => $form,
+ )); ?>
+ field($generator, 'template')->sticky()
+ ->label(array('label' => 'Code Template'))
+ ->dropDownList($templates)->hint('
+ Please select which set of the templates should be used to generated the code.
+ '); ?>
+
+ 'preview', 'class' => 'btn btn-success')); ?>
+
+
+ 'generate', 'class' => 'btn btn-danger')); ?>
+
+
+
+
+
+ render('view/results', array(
+ 'generator' => $generator,
+ 'results' => $results,
+ 'hasError' => $hasError,
+ ));
+ } elseif (isset($files)) {
+ echo $this->render('view/files', array(
+ 'generator' => $generator,
+ 'files' => $files,
+ 'answers' => $answers,
+ ));
+ }
+ ?>
+
+
diff --git a/framework/yii/gii/views/default/view/files.php b/framework/yii/gii/views/default/view/files.php
new file mode 100644
index 0000000..5ba08e8
--- /dev/null
+++ b/framework/yii/gii/views/default/view/files.php
@@ -0,0 +1,79 @@
+
+
+
Click on the above Generate
button to generate the files selected below:
+
+
+
+
+
diff --git a/framework/yii/gii/views/default/view/results.php b/framework/yii/gii/views/default/view/results.php
new file mode 100644
index 0000000..caca404
--- /dev/null
+++ b/framework/yii/gii/views/default/view/results.php
@@ -0,0 +1,22 @@
+
+
+ There was something wrong when generating the code. Please check the following messages.
';
+ } else {
+ echo '' . $generator->successMessage() . '
';
+ }
+ ?>
+
+
diff --git a/framework/yii/gii/views/layouts/generator.php b/framework/yii/gii/views/layouts/generator.php
new file mode 100644
index 0000000..fcf9fbc
--- /dev/null
+++ b/framework/yii/gii/views/layouts/generator.php
@@ -0,0 +1,31 @@
+controller->module->generators;
+$activeGenerator = Yii::$app->controller->generator;
+?>
+beginContent('@yii/gii/views/layouts/main.php'); ?>
+'
- . Html::activeCheckboxList($this->model, $this->attribute, $items, $options)
- . '
'
- );
+ $this->parts['{input}'] = Html::activeCheckboxList($this->model, $this->attribute, $items, $options);
+ return $this;
}
/**
- * Renders a field containing a list of radio buttons.
+ * Renders a list of radio buttons.
* A radio button list is like a checkbox list, except that it only allows single selection.
* The selection of the radio buttons is taken from the value of the model attribute.
* @param array $items the data item used to generate the radio buttons.
@@ -532,19 +556,16 @@ class ActiveField extends Component
* where $index is the zero-based index of the radio button in the whole list; $label
* is the label for the radio button; and $name, $value and $checked represent the name,
* value and the checked status of the radio button input.
- * @return string the rendering result
+ * @return ActiveField the field object itself
*/
public function radioList($items, $options = array())
{
- return $this->render(
- ''
- . Html::activeRadioList($this->model, $this->attribute, $items, $options)
- . '
'
- );
+ $this->parts['{input}'] = Html::activeRadioList($this->model, $this->attribute, $items, $options);
+ return $this;
}
/**
- * Renders a field containing an input widget.
+ * Renders a widget as the input of the field.
*
* Note that the widget must have both `model` and `attribute` properties. They will
* be initialized with [[model]] and [[attribute]] of this field, respectively.
@@ -554,7 +575,7 @@ class ActiveField extends Component
*
* @param string $class the widget class name
* @param array $config name-value pairs that will be used to initialize the widget
- * @return string the rendering result
+ * @return ActiveField the field object itself
*/
public function widget($class, $config = array())
{
@@ -562,6 +583,53 @@ class ActiveField extends Component
$config['model'] = $this->model;
$config['attribute'] = $this->attribute;
$config['view'] = $this->form->getView();
- return $this->render($class::widget($config));
+ $this->parts['{input}'] = $class::widget($config);
+ return $this;
+ }
+
+ /**
+ * Returns the JS options for the field.
+ * @return array the JS options
+ */
+ protected function getClientOptions()
+ {
+ $enableClientValidation = $this->enableClientValidation || $this->enableClientValidation === null && $this->form->enableClientValidation;
+ if ($enableClientValidation) {
+ $attribute = Html::getAttributeName($this->attribute);
+ $validators = array();
+ foreach ($this->model->getActiveValidators($attribute) as $validator) {
+ /** @var \yii\validators\Validator $validator */
+ $js = $validator->clientValidateAttribute($this->model, $attribute, $this->form->getView());
+ if ($validator->enableClientValidation && $js != '') {
+ $validators[] = $js;
+ }
+ }
+ if (!empty($validators)) {
+ $options['validate'] = new JsExpression("function(attribute, value, messages) {" . implode('', $validators) . '}');
+ }
+ }
+
+ $enableAjaxValidation = $this->enableAjaxValidation || $this->enableAjaxValidation === null && $this->form->enableAjaxValidation;
+ if ($enableAjaxValidation) {
+ $options['enableAjaxValidation'] = 1;
+ }
+
+ if ($enableClientValidation && !empty($options['validate']) || $enableAjaxValidation) {
+ $inputID = Html::getInputId($this->model, $this->attribute);
+ $options['name'] = $inputID;
+ foreach (array('validateOnChange', 'validateOnType', 'validationDelay') as $name) {
+ $options[$name] = $this->$name === null ? $this->form->$name : $this->$name;
+ }
+ $options['container'] = isset($this->selectors['container']) ? $this->selectors['container'] : ".field-$inputID";
+ $options['input'] = isset($this->selectors['input']) ? $this->selectors['input'] : "#$inputID";
+ if (isset($this->errorOptions['class'])) {
+ $options['error'] = '.' . implode('.', preg_split('/\s+/', $this->errorOptions['class'], -1, PREG_SPLIT_NO_EMPTY));
+ } else {
+ $options['error'] = isset($this->errorOptions['tag']) ? $this->errorOptions['tag'] : 'div';
+ }
+ return $options;
+ } else {
+ return array();
+ }
}
}
diff --git a/framework/yii/widgets/Menu.php b/framework/yii/widgets/Menu.php
index 40fd479..8bf845e 100644
--- a/framework/yii/widgets/Menu.php
+++ b/framework/yii/widgets/Menu.php
@@ -250,7 +250,7 @@ class Menu extends Widget
}
$hasActiveChild = false;
if (isset($item['items'])) {
- $items[$i]['items'] = $this->normalizeItems($item['items'], $route, $hasActiveChild);
+ $items[$i]['items'] = $this->normalizeItems($item['items'], $hasActiveChild);
if (empty($items[$i]['items']) && $this->hideEmptyItems) {
unset($items[$i]['items']);
if (!isset($item['url'])) {
@@ -284,7 +284,14 @@ class Menu extends Widget
*/
protected function isItemActive($item)
{
- if (isset($item['url']) && is_array($item['url']) && trim($item['url'][0], '/') === $this->route) {
+ if (isset($item['url']) && is_array($item['url']) && isset($item['url'][0])) {
+ $route = $item['url'][0];
+ if ($route[0] !== '/' && Yii::$app->controller) {
+ $route = Yii::$app->controller->module->getUniqueId() . '/' . $route;
+ }
+ if (ltrim($route, '/') !== $this->route) {
+ return false;
+ }
unset($item['url']['#']);
if (count($item['url']) > 1) {
foreach (array_splice($item['url'], 1) as $name => $value) {
diff --git a/tests/unit/TestCase.php b/tests/unit/TestCase.php
index efcedf0..bd95465 100644
--- a/tests/unit/TestCase.php
+++ b/tests/unit/TestCase.php
@@ -37,6 +37,7 @@ abstract class TestCase extends \yii\test\TestCase
* Populates Yii::$app with a new application
* The application will be destroyed on tearDown() automatically.
* @param array $config The application configuration, if needed
+ * @param string $appClass name of the application class to create
*/
protected function mockApplication($config = array(), $appClass = '\yii\console\Application')
{
@@ -45,7 +46,7 @@ abstract class TestCase extends \yii\test\TestCase
'basePath' => __DIR__,
);
- new $appClass(array_merge($defaultConfig,$config));
+ new $appClass(array_merge($defaultConfig, $config));
}
/**
diff --git a/tests/unit/data/ar/Customer.php b/tests/unit/data/ar/Customer.php
index b26b51b..561d1ae 100644
--- a/tests/unit/data/ar/Customer.php
+++ b/tests/unit/data/ar/Customer.php
@@ -1,6 +1,6 @@
hasMany('Item', array('id' => 'item_id'))
- ->via('orderItems', function($q) {
+ ->via('orderItems', function ($q) {
// additional query configuration
})->orderBy('id');
}
diff --git a/tests/unit/data/base/InvalidRulesModel.php b/tests/unit/data/base/InvalidRulesModel.php
index f5a8438..13f428d 100644
--- a/tests/unit/data/base/InvalidRulesModel.php
+++ b/tests/unit/data/base/InvalidRulesModel.php
@@ -1,5 +1,6 @@
array(
'mysql' => array(
@@ -27,7 +26,6 @@ return array(
'redis' => array(
'dsn' => 'redis://localhost:6379/0',
'password' => null,
- // 'fixture' => __DIR__ . '/mysql.sql',
),
- )
+ ),
);
diff --git a/tests/unit/framework/base/BehaviorTest.php b/tests/unit/framework/base/BehaviorTest.php
index 95b7220..edb9a1e 100644
--- a/tests/unit/framework/base/BehaviorTest.php
+++ b/tests/unit/framework/base/BehaviorTest.php
@@ -8,7 +8,6 @@ use yiiunit\TestCase;
class BarClass extends Component
{
-
}
class FooClass extends Component
diff --git a/tests/unit/framework/base/ComponentTest.php b/tests/unit/framework/base/ComponentTest.php
index 712a515..79fb7db 100644
--- a/tests/unit/framework/base/ComponentTest.php
+++ b/tests/unit/framework/base/ComponentTest.php
@@ -194,7 +194,7 @@ class ComponentTest extends TestCase
$this->assertFalse($this->component->event->handled);
$eventRaised = false;
- $this->component->on('click', function($event) use (&$eventRaised) {
+ $this->component->on('click', function ($event) use (&$eventRaised) {
$eventRaised = true;
});
$this->component->raiseEvent();
@@ -202,7 +202,7 @@ class ComponentTest extends TestCase
// raise event w/o parameters
$eventRaised = false;
- $this->component->on('test', function($event) use (&$eventRaised) {
+ $this->component->on('test', function ($event) use (&$eventRaised) {
$eventRaised = true;
});
$this->component->trigger('test');
@@ -331,7 +331,7 @@ class NewComponent extends Component
public function getExecute()
{
- return function($param) {
+ return function ($param) {
return $param * 2;
};
}
diff --git a/tests/unit/framework/base/ModelTest.php b/tests/unit/framework/base/ModelTest.php
index ff20d42..e4d8976 100644
--- a/tests/unit/framework/base/ModelTest.php
+++ b/tests/unit/framework/base/ModelTest.php
@@ -1,6 +1,7 @@
mockApplication(
array(
'components' => array(
@@ -112,4 +113,4 @@ class ActiveRecordAutoTimestamp extends ActiveRecord
{
return 'test_auto_timestamp';
}
-}
\ No newline at end of file
+}
diff --git a/tests/unit/framework/caching/ApcCacheTest.php b/tests/unit/framework/caching/ApcCacheTest.php
index 17bcb6e..1d07498 100644
--- a/tests/unit/framework/caching/ApcCacheTest.php
+++ b/tests/unit/framework/caching/ApcCacheTest.php
@@ -1,5 +1,6 @@
markTestSkipped("APC cli is not enabled. Skipping.");
}
- if(!ini_get("apc.enabled") || !ini_get("apc.enable_cli")) {
+ if (!ini_get("apc.enabled") || !ini_get("apc.enable_cli")) {
$this->markTestSkipped("APC is installed but not enabled. Skipping.");
}
diff --git a/tests/unit/framework/caching/CacheTestCase.php b/tests/unit/framework/caching/CacheTestCase.php
index 81ac5cc..491459b 100644
--- a/tests/unit/framework/caching/CacheTestCase.php
+++ b/tests/unit/framework/caching/CacheTestCase.php
@@ -6,7 +6,8 @@ namespace yii\caching;
* Mock for the time() function for caching classes
* @return int
*/
-function time() {
+function time()
+{
return \yiiunit\framework\caching\CacheTestCase::$time ?: \time();
}
diff --git a/tests/unit/framework/caching/DbCacheTest.php b/tests/unit/framework/caching/DbCacheTest.php
index 3a26595..969b034 100644
--- a/tests/unit/framework/caching/DbCacheTest.php
+++ b/tests/unit/framework/caching/DbCacheTest.php
@@ -35,11 +35,11 @@ class DbCacheTest extends CacheTestCase
* @param bool $reset whether to clean up the test database
* @return \yii\db\Connection
*/
- function getConnection($reset = true)
+ public function getConnection($reset = true)
{
if ($this->_connection === null) {
$databases = $this->getParam('databases');
- $params = $databases['mysql'];
+ $params = $databases['mysql'];
$db = new \yii\db\Connection;
$db->dsn = $params['dsn'];
$db->username = $params['username'];
diff --git a/tests/unit/framework/caching/FileCacheTest.php b/tests/unit/framework/caching/FileCacheTest.php
index 62f8637..0bdbc86 100644
--- a/tests/unit/framework/caching/FileCacheTest.php
+++ b/tests/unit/framework/caching/FileCacheTest.php
@@ -1,5 +1,6 @@
testFilePath . DIRECTORY_SEPARATOR . 'bundle.php';
$configFile = $this->testFilePath . DIRECTORY_SEPARATOR . 'config.php';
diff --git a/tests/unit/framework/data/SortTest.php b/tests/unit/framework/data/SortTest.php
index 8e623ba..9891ad1 100644
--- a/tests/unit/framework/data/SortTest.php
+++ b/tests/unit/framework/data/SortTest.php
@@ -6,11 +6,11 @@
*/
namespace yiiunit\framework\data;
+
use yii\web\UrlManager;
use yiiunit\TestCase;
use yii\data\Sort;
-
/**
* @author Qiang Xue