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 ns ?>; + +use Yii; + +/** + * This is the model class for collection "". + * + + * @property + + */ +class extends baseClass, '\\') . "\n" ?> +{ + /** + * @inheritdoc + */ + public static function collectionName() + { +databaseName)): ?> + return ''; + + return ['databaseName ?>', '']; + + } +db !== 'mongodb'): ?> + + /** + * @return \yii\mongodb\Connection the MongoDB connection used by this AR class. + */ + public static function getDb() + { + return Yii::$app->get('db ?>'); + } + + + /** + * @inheritdoc + */ + public function attributes() + { + return [ + + + + ]; + } + + /** + * @inheritdoc + */ + public function rules() + { + return []; + } + + /** + * @inheritdoc + */ + public function attributeLabels() + { + return [ + $label): ?> + " . $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');