diff --git a/extensions/mongodb/CHANGELOG.md b/extensions/mongodb/CHANGELOG.md
index f018c23d3f..05558632f2 100644
--- a/extensions/mongodb/CHANGELOG.md
+++ b/extensions/mongodb/CHANGELOG.md
@@ -6,6 +6,7 @@ Yii Framework 2 mongodb extension Change Log
- Bug #3385: Fixed "The 'connected' property is deprecated" (samdark)
- Enh #3520: Added `unlinkAll()`-method to active record to remove all records of a model relation (NmDimas, samdark, cebe)
+- Enh: Gii generator for Active Record model added (klimov-paul)
2.0.0-beta April 13, 2014
diff --git a/extensions/mongodb/README.md b/extensions/mongodb/README.md
index 4ce2832f0e..28f456ed3a 100644
--- a/extensions/mongodb/README.md
+++ b/extensions/mongodb/README.md
@@ -258,4 +258,31 @@ return [
],
]
];
-```
\ No newline at end of file
+```
+
+
+Using Gii generator
+-------------------
+
+This extension provides a code generator, which can be integrated with yii 'gii' module. It allows generation of the
+Active Record code. In order to enable it, you should adjust your application configuration in following way:
+
+```php
+return [
+ //....
+ 'modules' => [
+ // ...
+ 'gii' => [
+ 'class' => 'yii\gii\Module',
+ 'generators' => [
+ 'mongoDbModel' => [
+ 'class' => 'yii\mongodb\gii\model\Generator'
+ ]
+ ],
+ ],
+ ]
+];
+```
+
+> Note: since MongoDB is schemaless, there is not much information, which generated code may base on. So generated code
+ is very basic and definitely requires adjustments.
\ No newline at end of file
diff --git a/extensions/mongodb/gii/model/Generator.php b/extensions/mongodb/gii/model/Generator.php
new file mode 100644
index 0000000000..cdfde9ffbe
--- /dev/null
+++ b/extensions/mongodb/gii/model/Generator.php
@@ -0,0 +1,281 @@
+
+ * @since 2.0
+ */
+class Generator extends \yii\gii\Generator
+{
+ public $db = 'mongodb';
+ public $ns = 'app\models';
+ public $collectionName;
+ public $databaseName;
+ public $attributeList;
+ public $modelClass;
+ public $baseClass = 'yii\mongodb\ActiveRecord';
+
+ /**
+ * @inheritdoc
+ */
+ public function getName()
+ {
+ return 'MongoDB Model Generator';
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function getDescription()
+ {
+ return 'This generator generates an ActiveRecord class for the specified MongoDB collection.';
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function rules()
+ {
+ return array_merge(parent::rules(), [
+ [['db', 'ns', 'collectionName', 'databaseName', 'attributeList', 'modelClass', 'baseClass'], 'filter', 'filter' => 'trim'],
+ [['ns'], 'filter', 'filter' => function($value) { return trim($value, '\\'); }],
+
+ [['db', 'ns', 'collectionName', 'baseClass'], 'required'],
+ [['db', 'modelClass'], 'match', 'pattern' => '/^\w+$/', 'message' => 'Only word characters are allowed.'],
+ [['ns', 'baseClass'], 'match', 'pattern' => '/^[\w\\\\]+$/', 'message' => 'Only word characters and backslashes are allowed.'],
+ [['collectionName'], 'match', 'pattern' => '/^[^$ ]+$/', 'message' => 'Collection name can not contain spaces or "$" symbols.'],
+ [['databaseName'], 'match', 'pattern' => '/^[^\\/\\\\\\. "*:?\\|<>]+$/', 'message' => 'Database name can not contain spaces or any of "/\."*<>:|?" symbols.'],
+ [['db'], 'validateDb'],
+ [['ns'], 'validateNamespace'],
+ [['collectionName'], 'validateCollectionName'],
+ [['attributeList'], 'match', 'pattern' => '/^(\w+\,[ ]*)*([\w]+)$/', 'message' => 'Attributes should contain only word characters, and should be separated by coma.'],
+ [['modelClass'], 'validateModelClass', 'skipOnEmpty' => false],
+ [['baseClass'], 'validateClass', 'params' => ['extends' => ActiveRecord::className()]],
+ [['enableI18N'], 'boolean'],
+ [['messageCategory'], 'validateMessageCategory', 'skipOnEmpty' => false],
+ ]);
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function attributeLabels()
+ {
+ return array_merge(parent::attributeLabels(), [
+ 'ns' => 'Namespace',
+ 'db' => 'MongoDB Connection ID',
+ 'collectionName' => 'Collection Name',
+ 'databaseName' => 'Database Name',
+ 'modelClass' => 'Model Class',
+ 'baseClass' => 'Base Class',
+ ]);
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function hints()
+ {
+ return array_merge(parent::hints(), [
+ 'ns' => 'This is the namespace of the ActiveRecord class to be generated, e.g., app\models',
+ 'db' => 'This is the ID of the MongoDB application component.',
+ 'collectionName' => 'This is the name of the MongoDB collection that the new ActiveRecord class is associated with, e.g. post.',
+ 'databaseName' => 'This is the name of the MongoDB database, which contains the collection that the new ActiveRecord class is associated with.
+ You may leave this field blank, if your application uses single MongoDB database.',
+ 'attributeList' => 'List of the collection attribute names separated by coma.
+ You do not need to specify "_id" attribute here - it will be added automatically.',
+ '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 may leave this field blank - in this case class name
+ will be generated automatically.',
+ 'baseClass' => 'This is the base class of the new ActiveRecord class. It should be a fully qualified namespaced class name.',
+ ]);
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function autoCompleteData()
+ {
+ $db = $this->getDbConnection();
+ if ($db !== null) {
+ return [
+ 'collectionName' => function () use ($db) {
+ return $db->getDatabase()->mongoDb->getCollectionNames();
+ },
+ ];
+ } else {
+ return [];
+ }
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function requiredTemplates()
+ {
+ return ['model.php'];
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function stickyAttributes()
+ {
+ return array_merge(parent::stickyAttributes(), ['ns', 'db', 'baseClass']);
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function generate()
+ {
+ $files = [];
+ $collectionName = $this->collectionName;
+
+ $attributes = ['_id'];
+ if (!empty($this->attributeList)) {
+ $customAttributes = explode(',', $this->attributeList);
+ $customAttributes = array_map('trim', $customAttributes);
+ $attributes = array_merge(['_id'], $customAttributes);
+ }
+
+ $className = $this->generateClassName($collectionName);
+ $params = [
+ 'collectionName' => $collectionName,
+ 'className' => $className,
+ 'attributes' => $attributes,
+ 'labels' => $this->generateLabels($attributes),
+ 'rules' => $this->generateRules($attributes),
+ ];
+ $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 attributes list.
+ * @param array $attributes the list of attributes
+ * @return array the generated attribute labels (name => label)
+ */
+ public function generateLabels($attributes)
+ {
+ $labels = [];
+ foreach ($attributes as $attribute) {
+ if (!strcasecmp($attribute, '_id')) {
+ $label = 'ID';
+ } else {
+ $label = Inflector::camel2words($attribute);
+ if (strcasecmp(substr($label, -3), ' id') === 0) {
+ $label = substr($label, 0, -3) . ' ID';
+ }
+ }
+ $labels[$attribute] = $label;
+ }
+
+ return $labels;
+ }
+
+ /**
+ * Generates validation rules for the specified table.
+ * @param array $attributes the list of attributes
+ * @return array the generated validation rules
+ */
+ public function generateRules($attributes)
+ {
+ $rules = [];
+ $safeAttributes = [];
+ foreach ($attributes as $attribute) {
+ if ($attribute == '_id') {
+ continue;
+ }
+ $safeAttributes[] = $attribute;
+ }
+ if (!empty($safeAttributes)) {
+ $rules[] = "[['" . implode("', '", $safeAttributes) . "'], 'safe']";
+ }
+ return $rules;
+ }
+
+ /**
+ * Validates the [[db]] attribute.
+ */
+ public function validateDb()
+ {
+ if (!Yii::$app->has($this->db)) {
+ $this->addError('db', 'There is no application component named "' . $this->db . '".');
+ } elseif (!Yii::$app->get($this->db) instanceof Connection) {
+ $this->addError('db', 'The "' . $this->db . '" application component must be a MongoDB 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.');
+ }
+ }
+
+ /**
+ * Validates the [[collectionName]] attribute.
+ */
+ public function validateCollectionName()
+ {
+ if (empty($this->modelClass)) {
+ $class = $this->generateClassName($this->collectionName);
+ if ($this->isReservedKeyword($class)) {
+ $this->addError('collectionName', "Collection '{$this->collectionName}' will generate a class which is a reserved PHP keyword.");
+ }
+ }
+ }
+
+ /**
+ * Generates a class name from the specified table name.
+ * @param string $collectionName the table name (which may contain schema prefix)
+ * @return string the generated class name
+ */
+ protected function generateClassName($collectionName)
+ {
+ $className = preg_replace('/[^\\w]+/is', '_', $collectionName);
+ return Inflector::id2camel($className, '_');
+ }
+
+ /**
+ * @return Connection the DB connection as specified by [[db]].
+ */
+ protected function getDbConnection()
+ {
+ return Yii::$app->get($this->db, false);
+ }
+}
diff --git a/extensions/mongodb/gii/model/default/model.php b/extensions/mongodb/gii/model/default/model.php
new file mode 100644
index 0000000000..6c23df6350
--- /dev/null
+++ b/extensions/mongodb/gii/model/default/model.php
@@ -0,0 +1,83 @@
+ label)
+ * @var string[] $rules list of validation rules
+ */
+
+echo "
+
+namespace = $generator->ns ?>;
+
+use Yii;
+
+/**
+ * This is the model class for collection "= $collectionName ?>".
+ *
+
+ * @property = $attribute == '_id' ? '\MongoId|string' : 'mixed' ?> = "\${$attribute}\n" ?>
+
+ */
+class = $className ?> extends = '\\' . ltrim($generator->baseClass, '\\') . "\n" ?>
+{
+ /**
+ * @inheritdoc
+ */
+ public static function collectionName()
+ {
+databaseName)): ?>
+ return '= $collectionName ?>';
+
+ return ['= $generator->databaseName ?>', '= $collectionName ?>'];
+
+ }
+db !== 'mongodb'): ?>
+
+ /**
+ * @return \yii\mongodb\Connection the MongoDB connection used by this AR class.
+ */
+ public static function getDb()
+ {
+ return Yii::$app->get('= $generator->db ?>');
+ }
+
+
+ /**
+ * @inheritdoc
+ */
+ public function attributes()
+ {
+ return [
+
+ = "'$attribute',\n" ?>
+
+ ];
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function rules()
+ {
+ return [= "\n " . implode(",\n ", $rules) . "\n " ?>];
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function attributeLabels()
+ {
+ return [
+ $label): ?>
+ = "'$name' => " . $generator->generateString($label) . ",\n" ?>
+
+ ];
+ }
+}
diff --git a/extensions/mongodb/gii/model/form.php b/extensions/mongodb/gii/model/form.php
new file mode 100644
index 0000000000..881ddb4573
--- /dev/null
+++ b/extensions/mongodb/gii/model/form.php
@@ -0,0 +1,16 @@
+field($generator, 'collectionName');
+echo $form->field($generator, 'databaseName');
+echo $form->field($generator, 'attributeList');
+echo $form->field($generator, 'modelClass');
+echo $form->field($generator, 'ns');
+echo $form->field($generator, 'baseClass');
+echo $form->field($generator, 'db');
+echo $form->field($generator, 'enableI18N')->checkbox();
+echo $form->field($generator, 'messageCategory');