From 3342a9ee4b2244a463c1687b4825b0d28b008ea8 Mon Sep 17 00:00:00 2001 From: gmotov Date: Fri, 10 Oct 2025 01:18:27 +0300 Subject: [PATCH] release --- composer.json | 4 +- src/Generator.php | 170 ++++++++++++++++++++++++++++++++++--- src/assets/MainAsset.php | 13 +++ src/assets/main.js | 99 +++++++++++++++++++++ src/default/_search.php | 53 ++++++++++++ src/default/controller.php | 24 ++++++ src/default/migration.php | 31 +++++++ src/default/view.php | 85 +++++++++++++++++++ src/form.php | 35 ++++++++ 9 files changed, 499 insertions(+), 15 deletions(-) create mode 100644 src/assets/MainAsset.php create mode 100644 src/assets/main.js create mode 100644 src/default/_search.php create mode 100644 src/default/controller.php create mode 100644 src/default/migration.php create mode 100644 src/default/view.php create mode 100644 src/form.php diff --git a/composer.json b/composer.json index 48a9b23..12bd39c 100644 --- a/composer.json +++ b/composer.json @@ -1,5 +1,5 @@ { - "name": "drobnitsa/dictionary-yii2-generator", + "name": "drobnitsa/yii2-gii-dictionary-generator", "description": "Генератор справочников для проектов", "type": "yii2-extension", "authors": [ @@ -13,7 +13,7 @@ }, "autoload": { "psr-4": { - "drobnitsa\\DictionaryYii2Generator\\": "src/" + "drobnitsa\\dictionaryGenerator\\": "src/" } } } diff --git a/src/Generator.php b/src/Generator.php index cabdf3c..8317e83 100644 --- a/src/Generator.php +++ b/src/Generator.php @@ -1,26 +1,170 @@ '/^[\w\\\\]*$/', 'message' => 'Only word characters and backslashes are allowed.'], + ['modelName', 'validateModelClass'], + ['generateMigration', 'boolean'], + [['migrationName', 'controllerId'], 'string'], + ['attributes', 'safe'] +// ['attributes', 'string', 'length' => [0, 1024]], + ]; + } - // создаем файл - if (!file_exists(dirname($filePath))) { - mkdir(dirname($filePath), 0777, true); + public function validateModelClass($attribute, $params) + { + if (!class_exists($this->modelClass)) { + $this->addError($attribute, 'Модель не найдена.'); + } + } + + public function getModelClass() + { + return $this->modelNs . '\\' . $this->modelName; + } + + public function getControllerNs() + { + return $this->moduleId . '\controllers'; + } + + public function generate() + { + $controllerName = Inflector::id2camel($this->controllerId).'Controller'; + + + $params = [ + 'modelName' => $this->modelName, + 'modelClass' => $this->modelClass, + 'controllerNs' => $this->controllerNs, + 'controllerId' => $this->controllerId, + 'controllerName' => Inflector::id2camel($this->controllerId), + 'attributes' => $this->attributes, + 'migrationName' => $this->migrationName, + 'moduleId' => $this->moduleId + ]; + + $controllerCode = $this->render('controller.php', $params); + $viewCode = $this->render('view.php', $params); + $searchCode = $this->render('_search.php', $params); + + $nsPath = str_replace('\\', '/', $this->controllerNs); + + $controllerPath = \Yii::getAlias("@$nsPath/{$controllerName}.php"); + $viewDir = \Yii::getAlias("@backend/views/{$this->controllerId}"); + + $files = [ + new CodeFile($controllerPath, $controllerCode), + new CodeFile("$viewDir/index.php", $viewCode), + new CodeFile("$viewDir/_search.php", $searchCode), + ]; + + if($this->generateMigration && $this->migrationName) { + $nsPath = str_replace('\\', '/', $this->migrationNs); + $migrationPath = \Yii::getAlias("@$nsPath/{$this->migrationName}.php"); + $migrationCode = $this->render('migration.php', $params); + $files[] = new CodeFile($migrationPath, $migrationCode); } - file_put_contents($filePath, $content); + return $files; + } - return $filePath; + /** + * Возвращает список атрибутов модели для выбора в форме + */ + public function getModelAttributes($plain = true) + { + if (!class_exists($this->modelClass)) { + return []; + } + $model = new $this->modelClass(); + if(!$plain) { + $result = []; + foreach ($model->attributes() as $attribute) { + $result[$attribute] = $attribute; + } + return $result; + } + return $model->attributes(); +// return $model->attributeLabels(); + } + + /** + * @inheritdoc + */ + public function attributeLabels() + { + return array_merge(parent::attributeLabels(), [ + 'modelNs' => 'Model namespace', + 'moduleId' => 'Module', + ]); + } + + /** + * @inheritdoc + */ + public function hints() + { + return array_merge(parent::hints(), [ + 'modelNs' => 'This is the namespace for the source model, e.g., app\models', + 'moduleId' => 'This is the module name for the controller and view to be generated, e.g., app\controllers', + ]); + } + + /** + * @inheritdoc + */ + public function stickyAttributes() + { + return array_merge( + parent::stickyAttributes(), + [ + 'modelNs', + 'moduleId', + 'migrationNs' + ] + ); + } + + public function actionGetAttributes() + { + if(class_exists($this->modelClass)) { + return json_encode([ + 'result' => true, + 'controller_id' => Inflector::pluralize(Inflector::camel2id($this->modelName)), + 'attributes' => $this->getModelAttributes() + ]); + } + return json_encode(['result' => false]); } } \ No newline at end of file diff --git a/src/assets/MainAsset.php b/src/assets/MainAsset.php new file mode 100644 index 0000000..0f326d8 --- /dev/null +++ b/src/assets/MainAsset.php @@ -0,0 +1,13 @@ + { + autoUpdateController = false; + }); + + modelInput && modelInput.addEventListener('input', () => { + if (debounceTimer) clearTimeout(debounceTimer); + debounceTimer = setTimeout(() => { + const modelNs = modelNsInput.value.trim(); + if (modelNs.length === 0) return; + + const modelClassName = modelInput.value.trim(); + if (modelClassName.length === 0) return; + + const data = new FormData(); + data.append('Generator[modelNs]', modelNs) + data.append('Generator[modelName]', modelClassName) + data.append(yii.getCsrfParam(), yii.getCsrfToken()); + + // AJAX-запрос на получение атрибутов + fetch('default/action?id=dictionary&name=GetAttributes', { + method: 'POST', + body: data, + }) + .then(res => res.json()) + .then(data => { + if(data.result) { + if (attributesContainer) { + attributesContainer.innerHTML = ''; + data.attributes.forEach(attr => { + + let label = document.createElement('label'); + label.className = 'checkbox-label'; + + let checkbox = document.createElement('input'); + checkbox.type = 'checkbox'; + checkbox.name = 'Generator[attributes][]'; + checkbox.value = attr; + + label.appendChild(checkbox); + label.appendChild(document.createTextNode(' ' + attr)); + attributesContainer.appendChild(label); + attributesContainer.appendChild(document.createElement('br')); + }); + } + + controllerInput.value = data.controller_id; + generateMigrationName(data.controller_id); + } + + }); + }, 500); + }); + + function generateMigrationName(controllerId) { + let migrationNameInput = document.querySelector('#generator-migrationname'); + // Получаем текущие дата и время + const now = new Date(); + + // Форматируем дату и время в виде "yymmdd_hhmmss" + const year = now.getFullYear().toString().slice(-2); + const month = ('0' + (now.getMonth() + 1)).slice(-2); + const day = ('0' + now.getDate()).slice(-2); + const hours = ('0' + now.getHours()).slice(-2); + const minutes = ('0' + now.getMinutes()).slice(-2); + const seconds = ('0' + now.getSeconds()).slice(-2); + + const timestamp = `${year}${month}${day}_${hours}${minutes}${seconds}`; + + // Модифицируем имя таблицы: заменяем дефисы на подчеркивания, убираем расширения + // Предположим, что tableName — это строка с дефисами, например: "furniture-packs" + const formattedName = controllerId.replace(/-/g, '_'); + + migrationNameInput.value = `m${timestamp}_insert_${formattedName}_permissions`; + } + +})(); \ No newline at end of file diff --git a/src/default/_search.php b/src/default/_search.php new file mode 100644 index 0000000..f3299fb --- /dev/null +++ b/src/default/_search.php @@ -0,0 +1,53 @@ + +use yii\helpers\Html; +use common\widgets\ActiveForm; + +/* @var $this yii\web\View */ +/* @var $model common\models\Addition */ +/* @var $form yii\widgets\ActiveForm */ + +?> + + diff --git a/src/default/controller.php b/src/default/controller.php new file mode 100644 index 0000000..078dde2 --- /dev/null +++ b/src/default/controller.php @@ -0,0 +1,24 @@ + +declare(strict_types=1); +namespace ; + +use ; + +class extends AjaxController +{ + public string $modelClass = ::class; +} \ No newline at end of file diff --git a/src/default/migration.php b/src/default/migration.php new file mode 100644 index 0000000..1e4a74c --- /dev/null +++ b/src/default/migration.php @@ -0,0 +1,31 @@ + + +use backend\modules\rbac\migrations\BasePermissionsMigration; + +class extends BasePermissionsMigration +{ + function configure() + { + $this->moduleId = ''; + $this->controllerId = ''; + $this->actions = $this->defaultActions; + $this->baseAction = 'index'; + $this->childrenMap = [ + 'create' => [ + 'edit' + ] + ]; + } +} diff --git a/src/default/view.php b/src/default/view.php new file mode 100644 index 0000000..62bbfe5 --- /dev/null +++ b/src/default/view.php @@ -0,0 +1,85 @@ + + +use backend\assets\EditableFieldsAsset; +use ; +use common\grid\GridView; +use common\widgets\Card; +use yii\helpers\Html; +use yii\helpers\Url; +use yii\widgets\Pjax; + +/* @var $this yii\web\View */ +/* @var $searchModel */ +/* @var $dataProvider yii\data\ActiveDataProvider */ + +EditableFieldsAsset::register($this); +$page = Yii::$app->request->getQueryParams()['page'] ?? 1; + +$can_create = Yii::$app->user->can("backend_{$this->context->id}_create"); +$can_update = Yii::$app->user->can("backend_{$this->context->id}_edit"); +$can_delete = Yii::$app->user->can("backend_{$this->context->id}_delete"); + +$this->title = Yii::$app->urlManager->getLastTitle(); +?> + + $this->render('_search', ['model' => $searchModel]); ?> + + +Card::begin([]); ?> +Pjax::begin(['id' => 'pjax']) ?> + + GridView::widget([ + 'dataProvider' => $dataProvider, + 'createRowModel' => new (), + 'formatter' => ['class' => 'yii\i18n\Formatter','nullDisplay' => ''], + 'filterSelector' => '.search-block *[name]', + 'tableOptions' => ['class' => 'table align-middle table-check table-bordered mb-0 editable-fields', 'data-base_url' => $this->context->id, 'id' => 'table-'.$this->context->id], + 'actions' => common\widgets\ActionButtons::widget(['defaultShowTitle' => false, 'defaultAccess' => '$', 'items' => [ + ['name' => 'create', 'access' => $can_create, 'options' => ['class' => 'btn btn-success btn-sm btn-row-add', 'data-id' => 0], 'title' => 'Добавить', 'iconClass' => 'fa fa-plus'], + $page > 0 ? + ['name' => Url::current(['page' => -1]), 'options' => ['class' => 'btn btn-primary btn-sm'], 'title' => 'Показать все', 'iconClass' => 'fa fa-expand'] : + ['name' => Url::current(['page' => 1]), 'options' => ['class' => 'btn btn-primary btn-sm'], 'title' => 'Показать постранично', 'iconClass' => 'fa fa-compress'], + ]]), + 'columns' => [ + [ + 'class' => 'yii\grid\SerialColumn', + 'header' => '№', + ], + + [ + 'attribute' => '', + 'format' => 'raw', + 'value' => function($model) use ($can_update) { + return $can_update ? Html::input('text', '', $model->, ['class' => 'form-control', 'required' => 1]) : $model->; + }, + ], + + [ + 'class' => 'common\grid\ActionColumn', + 'defaultShowTitle' => false, + 'visible' => $can_delete, + 'buttons' => [ + 'delete' => ['icon' => 'fa fa-trash', 'class' => 'btn btn-danger btn-sm btn-row-remove', 'title' => 'Удаление'], + ], + ] + ], +]); +?> + + Pjax::end() ?> + Card::end(); ?> diff --git a/src/form.php b/src/form.php new file mode 100644 index 0000000..3ebb324 --- /dev/null +++ b/src/form.php @@ -0,0 +1,35 @@ +getModelAttributes(false); + +?> + +field($generator, 'modelNs') ?> + +field($generator, 'modelName')->textInput(['placeholder' => 'PostRecord']) ?> + +field($generator, 'moduleId') ?> + +field($generator, 'controllerId')->textInput(['placeholder' => 'post-records']) ?> + +field($generator, 'attributes')->checkboxList($modelAttributes, ['separator' => '
']) ?> + +field($generator, 'generateMigration')->checkbox() ?> + +field($generator, 'migrationNs') ?> + +field($generator, 'migrationName')->textInput(['placeholder' => 'm250907_112925_insert_furniture_packs_permissions']) ?> +