diff --git a/extensions/sphinx/gii/model/Generator.php b/extensions/sphinx/gii/model/Generator.php
new file mode 100644
index 0000000..07664db
--- /dev/null
+++ b/extensions/sphinx/gii/model/Generator.php
@@ -0,0 +1,389 @@
+<?php
+/**
+ * @link http://www.yiiframework.com/
+ * @copyright Copyright (c) 2008 Yii Software LLC
+ * @license http://www.yiiframework.com/license/
+ */
+
+namespace yii\sphinx\gii\model;
+
+use Yii;
+use yii\sphinx\ActiveRecord;
+use yii\sphinx\Connection;
+use yii\sphinx\Schema;
+use yii\gii\CodeFile;
+use yii\helpers\Inflector;
+
+/**
+ * This generator will generate one or multiple ActiveRecord classes for the specified Sphinx index.
+ *
+ * @author Paul Klimov <klimov.paul@gmail.com>
+ * @since 2.0
+ */
+class Generator extends \yii\gii\Generator
+{
+    public $db = 'sphinx';
+    public $ns = 'app\models';
+    public $indexName;
+    public $modelClass;
+    public $baseClass = 'yii\sphinx\ActiveRecord';
+    public $useIndexPrefix = false;
+
+    /**
+     * @inheritdoc
+     */
+    public function getName()
+    {
+        return 'Sphinx Model Generator';
+    }
+
+    /**
+     * @inheritdoc
+     */
+    public function getDescription()
+    {
+        return 'This generator generates an ActiveRecord class for the specified Sphinx index.';
+    }
+
+    /**
+     * @inheritdoc
+     */
+    public function rules()
+    {
+        return array_merge(parent::rules(), [
+            [['db', 'ns', 'indexName', 'modelClass', 'baseClass'], 'filter', 'filter' => 'trim'],
+            [['ns'], 'filter', 'filter' => function($value) { return trim($value, '\\'); }],
+
+            [['db', 'ns', 'indexName', '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.'],
+            [['indexName'], 'match', 'pattern' => '/^(\w+\.)?([\w\*]+)$/', 'message' => 'Only word characters, and optionally an asterisk and/or a dot are allowed.'],
+            [['db'], 'validateDb'],
+            [['ns'], 'validateNamespace'],
+            [['indexName'], 'validateIndexName'],
+            [['modelClass'], 'validateModelClass', 'skipOnEmpty' => false],
+            [['baseClass'], 'validateClass', 'params' => ['extends' => ActiveRecord::className()]],
+            [['enableI18N'], 'boolean'],
+            [['useIndexPrefix'], 'boolean'],
+            [['messageCategory'], 'validateMessageCategory', 'skipOnEmpty' => false],
+        ]);
+    }
+
+    /**
+     * @inheritdoc
+     */
+    public function attributeLabels()
+    {
+        return array_merge(parent::attributeLabels(), [
+            'ns' => 'Namespace',
+            'db' => 'Sphinx Connection ID',
+            'indexName' => 'Index 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., <code>app\models</code>',
+            'db' => 'This is the ID of the Sphinx application component.',
+            'indexName' => 'This is the name of the Sphinx index that the new ActiveRecord class is associated with, e.g. <code>post</code>.
+                The index name may end with asterisk to match multiple table names, e.g. <code>idx_*</code>
+                will match indexes, which name starts with <code>idx_</code>. In this case, multiple ActiveRecord classes
+                will be generated, one for each matching index name; and the class names will be generated from
+                the matching characters. For example, index <code>idx_post</code> will generate <code>Post</code>
+                class.',
+            '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 "Index Name" ends with asterisk, 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.',
+            'useIndexPrefix' => 'This indicates whether the index name returned by the generated ActiveRecord class
+                should consider the <code>tablePrefix</code> setting of the Sphinx connection. For example, if the
+                index name is <code>idx_post</code> and <code>tablePrefix=idx_</code>, the ActiveRecord class
+                will return the table name as <code>{{%post}}</code>.',
+        ]);
+    }
+
+    /**
+     * @inheritdoc
+     */
+    public function autoCompleteData()
+    {
+        $db = $this->getDbConnection();
+        if ($db !== null) {
+            return [
+                'indexName' => function () use ($db) {
+                    return $db->getSchema()->getIndexNames();
+                },
+            ];
+        } 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 = [];
+        $db = $this->getDbConnection();
+        foreach ($this->getIndexNames() as $indexName) {
+            $className = $this->generateClassName($indexName);
+            $indexSchema = $db->getIndexSchema($indexName);
+            $params = [
+                'indexName' => $indexName,
+                'className' => $className,
+                'indexSchema' => $indexSchema,
+                'labels' => $this->generateLabels($indexSchema),
+                'rules' => $this->generateRules($indexSchema),
+            ];
+            $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 = [];
+        foreach ($table->columns as $column) {
+            if (!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 index.
+     * @param \yii\sphinx\IndexSchema $index the index schema
+     * @return array the generated validation rules
+     */
+    public function generateRules($index)
+    {
+        $types = [];
+        foreach ($index->columns as $column) {
+            if ($column->isMva) {
+                $types['safe'][] = $column->name;
+                continue;
+            }
+            if ($column->isPrimaryKey) {
+                $types['required'][] = $column->name;
+                $types['unique'][] = $column->name;
+            }
+            switch ($column->type) {
+                case Schema::TYPE_PK:
+                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:
+                    $types['number'][] = $column->name;
+                    break;
+                case Schema::TYPE_TIMESTAMP:
+                    $types['safe'][] = $column->name;
+                    break;
+                default: // strings
+                    $types['string'][] = $column->name;
+            }
+        }
+        $rules = [];
+        foreach ($types as $type => $columns) {
+            $rules[] = "[['" . implode("', '", $columns) . "'], '$type']";
+        }
+
+        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 Sphinx 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 (substr($this->indexName, -1) !== '*' && $this->modelClass == '') {
+            $this->addError('modelClass', 'Model Class cannot be blank if table name does not end with asterisk.');
+        }
+    }
+
+    /**
+     * Validates the [[indexName]] attribute.
+     */
+    public function validateIndexName()
+    {
+        if (strpos($this->indexName, '*') !== false && substr($this->indexName, -1) !== '*') {
+            $this->addError('indexName', 'Asterisk is only allowed as the last character.');
+
+            return;
+        }
+        $tables = $this->getIndexNames();
+        if (empty($tables)) {
+            $this->addError('indexName', "Table '{$this->indexName}' does not exist.");
+        } else {
+            foreach ($tables as $table) {
+                $class = $this->generateClassName($table);
+                if ($this->isReservedKeyword($class)) {
+                    $this->addError('indexName', "Table '$table' will generate a class which is a reserved PHP keyword.");
+                    break;
+                }
+            }
+        }
+    }
+
+    private $_indexNames;
+    private $_classNames;
+
+    /**
+     * @return array the index names that match the pattern specified by [[indexName]].
+     */
+    protected function getIndexNames()
+    {
+        if ($this->_indexNames !== null) {
+            return $this->_indexNames;
+        }
+        $db = $this->getDbConnection();
+        if ($db === null) {
+            return [];
+        }
+        $indexNames = [];
+        if (strpos($this->indexName, '*') !== false) {
+            $indexNames = $db->getSchema()->getIndexNames();
+        } elseif (($index = $db->getIndexSchema($this->indexName, true)) !== null) {
+            $indexNames[] = $this->indexName;
+            $this->_classNames[$this->indexName] = $this->modelClass;
+        }
+
+        return $this->_indexNames = $indexNames;
+    }
+
+    /**
+     * Generates the table name by considering table prefix.
+     * If [[useIndexPrefix]] is false, the table name will be returned without change.
+     * @param string $indexName the table name (which may contain schema prefix)
+     * @return string the generated table name
+     */
+    public function generateIndexName($indexName)
+    {
+        if (!$this->useIndexPrefix) {
+            return $indexName;
+        }
+
+        $db = $this->getDbConnection();
+        if (preg_match("/^{$db->tablePrefix}(.*?)$/", $indexName, $matches)) {
+            $indexName = '{{%' . $matches[1] . '}}';
+        } elseif (preg_match("/^(.*?){$db->tablePrefix}$/", $indexName, $matches)) {
+            $indexName = '{{' . $matches[1] . '%}}';
+        }
+        return $indexName;
+    }
+
+    /**
+     * Generates a class name from the specified table name.
+     * @param string $indexName the table name (which may contain schema prefix)
+     * @return string the generated class name
+     */
+    protected function generateClassName($indexName)
+    {
+        if (isset($this->_classNames[$indexName])) {
+            return $this->_classNames[$indexName];
+        }
+
+        if (($pos = strrpos($indexName, '.')) !== false) {
+            $indexName = substr($indexName, $pos + 1);
+        }
+
+        $db = $this->getDbConnection();
+        $patterns = [];
+        $patterns[] = "/^{$db->tablePrefix}(.*?)$/";
+        $patterns[] = "/^(.*?){$db->tablePrefix}$/";
+        if (strpos($this->indexName, '*') !== false) {
+            $pattern = $this->indexName;
+            if (($pos = strrpos($pattern, '.')) !== false) {
+                $pattern = substr($pattern, $pos + 1);
+            }
+            $patterns[] = '/^' . str_replace('*', '(\w+)', $pattern) . '$/';
+        }
+        $className = $indexName;
+        foreach ($patterns as $pattern) {
+            if (preg_match($pattern, $indexName, $matches)) {
+                $className = $matches[1];
+                break;
+            }
+        }
+
+        return $this->_classNames[$indexName] = Inflector::id2camel($className, '_');
+    }
+
+    /**
+     * @return Connection the Sphinx connection as specified by [[db]].
+     */
+    protected function getDbConnection()
+    {
+        return Yii::$app->get($this->db, false);
+    }
+}
diff --git a/extensions/sphinx/gii/model/default/model.php b/extensions/sphinx/gii/model/default/model.php
new file mode 100644
index 0000000..ae39d39
--- /dev/null
+++ b/extensions/sphinx/gii/model/default/model.php
@@ -0,0 +1,67 @@
+<?php
+/**
+ * This is the template for generating the model class of a specified Sphinx index.
+ *
+ * @var yii\web\View $this
+ * @var yii\sphinx\gii\model\Generator $generator
+ * @var string $indexName full table name
+ * @var string $className class name
+ * @var yii\sphinx\IndexSchema $indexSchema
+ * @var string[] $labels list of attribute labels (name => label)
+ * @var string[] $rules list of validation rules
+ */
+
+echo "<?php\n";
+?>
+
+namespace <?= $generator->ns ?>;
+
+use Yii;
+
+/**
+ * This is the model class for index "<?= $indexName ?>".
+ *
+<?php foreach ($indexSchema->columns as $column): ?>
+ * @property <?= $column->isMva ? 'array' : $column->phpType ?> <?= "\${$column->name}\n" ?>
+<?php endforeach; ?>
+ */
+class <?= $className ?> extends <?= '\\' . ltrim($generator->baseClass, '\\') . "\n" ?>
+{
+    /**
+     * @inheritdoc
+     */
+    public static function indexName()
+    {
+        return '<?= $generator->generateIndexName($indexName) ?>';
+    }
+<?php if ($generator->db !== 'sphinx'): ?>
+
+    /**
+     * @return \yii\sphinx\Connection the database connection used by this AR class.
+     */
+    public static function getDb()
+    {
+        return Yii::$app->get('<?= $generator->db ?>');
+    }
+<?php endif; ?>
+
+    /**
+     * @inheritdoc
+     */
+    public function rules()
+    {
+        return [<?= "\n            " . implode(",\n            ", $rules) . "\n        " ?>];
+    }
+
+    /**
+     * @inheritdoc
+     */
+    public function attributeLabels()
+    {
+        return [
+<?php foreach ($labels as $name => $label): ?>
+            <?= "'$name' => " . $generator->generateString($label) . ",\n" ?>
+<?php endforeach; ?>
+        ];
+    }
+}
diff --git a/extensions/sphinx/gii/model/form.php b/extensions/sphinx/gii/model/form.php
new file mode 100644
index 0000000..1457664
--- /dev/null
+++ b/extensions/sphinx/gii/model/form.php
@@ -0,0 +1,15 @@
+<?php
+/**
+ * @var yii\web\View $this
+ * @var yii\widgets\ActiveForm $form
+ * @var yii\sphinx\gii\model\Generator $generator
+ */
+
+echo $form->field($generator, 'indexName');
+echo $form->field($generator, 'modelClass');
+echo $form->field($generator, 'ns');
+echo $form->field($generator, 'baseClass');
+echo $form->field($generator, 'db');
+echo $form->field($generator, 'useIndexPrefix')->checkbox();
+echo $form->field($generator, 'enableI18N')->checkbox();
+echo $form->field($generator, 'messageCategory');