├── .gitignore ├── base └── Provider.php ├── model ├── form.php ├── default │ ├── model-extended.php │ └── model.php └── Generator.php ├── composer.json ├── crud ├── providers │ ├── DateTimeProvider.php │ ├── EditorProvider.php │ ├── CallbackProvider.php │ └── RelationProvider.php ├── default │ ├── views │ │ ├── create.php │ │ ├── _search.php │ │ ├── update.php │ │ ├── _form.php │ │ ├── index.php │ │ └── view.php │ ├── search.php │ └── controller.php ├── form.php └── Generator.php ├── Bootstrap.php ├── commands └── BatchController.php └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | # phpstorm project files 2 | .idea 3 | -------------------------------------------------------------------------------- /base/Provider.php: -------------------------------------------------------------------------------- 1 | field($generator, 'tableName'); 9 | echo $form->field($generator, 'tablePrefix'); 10 | echo $form->field($generator, 'modelClass'); 11 | echo $form->field($generator, 'ns'); 12 | echo $form->field($generator, 'baseClass'); 13 | echo $form->field($generator, 'db'); 14 | echo $form->field($generator, 'generateRelations')->checkbox(); 15 | echo $form->field($generator, 'generateLabelsFromComments')->checkbox(); 16 | echo $form->field($generator, 'generateModelClass')->checkbox(); 17 | echo $form->field($generator, 'enableI18N')->checkbox(); 18 | echo $form->field($generator, 'messageCategory'); 19 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "veyselsahin/yii2-giix", 3 | "description": "Extended Gii for Yii 2 Framework", 4 | "keywords": ["yii2", "gii", "crud"], 5 | "homepage": "https://github.com/veyselsahin/yii2-giix", 6 | "type": "yii2-extension", 7 | "license": "BSD-3-Clause", 8 | "authors": [ 9 | { 10 | "name": "Veysel Şahin", 11 | "email": "hello@veyselsahin.com.tr", 12 | "homepage": "https://veyselsahin.com.tr" 13 | } 14 | ], 15 | "autoload": { 16 | "psr-4": { 17 | "veyselsahin\\giix\\": "" 18 | } 19 | }, 20 | "extra": { 21 | "bootstrap": "veyselsahin\\giix\\Bootstrap" 22 | }, 23 | "require": { 24 | "yiisoft/yii2-gii": "~2.0.1" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /model/default/model-extended.php: -------------------------------------------------------------------------------- 1 | label) 11 | * @var string[] $rules list of validation rules 12 | * @var array $relations list of relations (name => relation declaration) 13 | */ 14 | 15 | echo " 17 | 18 | namespace ns ?>; 19 | 20 | use Yii; 21 | 22 | /** 23 | * This is the model class for table "". 24 | */ 25 | class extends \ns ?>\base\ 26 | { 27 | } 28 | -------------------------------------------------------------------------------- /crud/providers/DateTimeProvider.php: -------------------------------------------------------------------------------- 1 | generator->getTableSchema()->columns[$attribute->name]; 10 | 11 | switch (true) { 12 | case (in_array($column->name, $this->columnNames)): 13 | $this->generator->requires[] = 'zhuravljov\yii2-datetime-widgets'; 14 | return <<field(\$model, '{$column->name}')->widget(\zhuravljov\widgets\DateTimePicker::className(), [ 16 | 'options' => ['class' => 'form-control'], 17 | 'clientOptions' => [ 18 | 'autoclose' => true, 19 | 'todayHighlight' => true, 20 | ], 21 | ]) 22 | EOS; 23 | break; 24 | default: 25 | return null; 26 | } 27 | } 28 | } -------------------------------------------------------------------------------- /crud/default/views/create.php: -------------------------------------------------------------------------------- 1 | 13 | 14 | use yii\helpers\Html; 15 | 16 | /** 17 | * @var yii\web\View $this 18 | * @var modelClass, '\\') ?> $model 19 | */ 20 | 21 | $this->title = 'Oluştur'; 22 | $this->params['breadcrumbs'][] = ['label' => 'modelClass)) 24 | ) ?>', 'url' => ['index']]; 25 | $this->params['breadcrumbs'][] = $this->title; 26 | ?> 27 |
28 | 29 |

30 | Html::a('İptal', \yii\helpers\Url::previous(), ['class' => 'btn btn-default']) ?> 31 |

32 |
33 | 34 | echo $this->render('_form', [ 35 | 'model' => $model, 36 | ]); ?> 37 | 38 |
39 | -------------------------------------------------------------------------------- /crud/providers/EditorProvider.php: -------------------------------------------------------------------------------- 1 | generator->getTableSchema()->columns[$attribute->name])) { 18 | return null; 19 | } 20 | $column = $this->generator->getTableSchema()->columns[$attribute->name]; 21 | switch (true) { 22 | case (in_array($column->name, $this->columnNames)): 23 | $this->generator->requires[] = '2amigos/yii2-ckeditor-widget'; 24 | return <<field(\$model, '{$attribute->name}')->widget( 26 | \dosamigos\ckeditor\CKEditor::className(), 27 | [ 28 | 'options' => ['rows' => 6], 29 | 'preset' => 'basic' 30 | ] 31 | ) 32 | EOS; 33 | break; 34 | default: 35 | return null; 36 | } 37 | } 38 | } -------------------------------------------------------------------------------- /crud/form.php: -------------------------------------------------------------------------------- 1 | field($generator, 'modelClass'); 9 | echo $form->field($generator, 'searchModelClass'); 10 | echo $form->field($generator, 'controllerClass'); 11 | echo $form->field($generator, 'baseControllerClass'); 12 | echo $form->field($generator, 'viewPath'); 13 | echo $form->field($generator, 'pathPrefix'); 14 | echo $form->field($generator, 'indexWidgetType')->dropDownList( 15 | [ 16 | 'grid' => 'GridView', 17 | 'list' => 'ListView', 18 | ] 19 | ); 20 | echo $form->field($generator, 'formLayout')->dropDownList( 21 | [ 22 | /* Form Types */ 23 | 'vertical' => 'vertical', 24 | 'horizontal' => 'horizontal', 25 | 'inline' => 'inline' 26 | ] 27 | ); 28 | echo $form->field($generator, 'actionButtonClass')->dropDownList( 29 | [ 30 | 'yii\\grid\\ActionColumn' => 'Default', 31 | 'common\\helpers\\ActionColumn' => 'App Class', 32 | ] 33 | ); 34 | echo $form->field($generator, 'providerList')->textarea(); 35 | -------------------------------------------------------------------------------- /crud/default/views/_search.php: -------------------------------------------------------------------------------- 1 | 13 | 14 | use yii\helpers\Html; 15 | use yii\widgets\ActiveForm; 16 | 17 | /** 18 | * @var yii\web\View $this 19 | * @var searchModelClass, '\\') ?> $model 20 | * @var yii\widgets\ActiveForm $form 21 | */ 22 | ?> 23 | 24 | 49 | -------------------------------------------------------------------------------- /crud/default/views/update.php: -------------------------------------------------------------------------------- 1 | generateUrlParams(); 12 | 13 | echo " 15 | 16 | use yii\helpers\Html; 17 | 18 | /** 19 | * @var yii\web\View $this 20 | * @var modelClass, '\\') ?> $model 21 | */ 22 | 23 | $this->title = 'modelClass)) ?> Güncelle ' . $model->getNameAttribute() ?> . ''; 24 | $this->params['breadcrumbs'][] = ['label' => 'modelClass))) ?>', 'url' => ['index']]; 25 | $this->params['breadcrumbs'][] = ['label' => (string)$model->getNameAttribute() ?>, 'url' => ['view', ]]; 26 | $this->params['breadcrumbs'][] = 'Edit'; 27 | ?> 28 |
29 | 30 |

31 | Html::a(' Görüntüle', ['view', ], ['class' => 'btn btn-info']) ?> 32 |

33 | 34 | echo $this->render('_form', [ 35 | 'model' => $model, 36 | ]); ?> 37 | 38 |
39 | -------------------------------------------------------------------------------- /Bootstrap.php: -------------------------------------------------------------------------------- 1 | 20 | */ 21 | class Bootstrap implements BootstrapInterface 22 | { 23 | 24 | /** 25 | * Bootstrap method to be called during application bootstrap stage. 26 | * 27 | * @param Application $app the application currently running 28 | */ 29 | public function bootstrap($app) 30 | { 31 | if ($app->hasModule('gii')) { 32 | 33 | if (!isset($app->getModule('gii')->generators['giix-model'])) { 34 | $app->getModule('gii')->generators['giix-model'] = 'veyselsahin\giix\model\Generator'; 35 | } 36 | if (!isset($app->getModule('gii')->generators['giix-crud'])) { 37 | $app->getModule('gii')->generators['giix-crud'] = 'veyselsahin\giix\crud\Generator'; 38 | } 39 | if ($app instanceof \yii\console\Application) { 40 | $app->controllerMap['giix-batch'] = 'veyselsahin\giix\commands\BatchController'; 41 | } 42 | } 43 | } 44 | } -------------------------------------------------------------------------------- /model/default/model.php: -------------------------------------------------------------------------------- 1 | label) 12 | * @var string[] $rules list of validation rules 13 | * @var array $relations list of relations (name => relation declaration) 14 | */ 15 | 16 | echo " 18 | 19 | namespace ns ?>\base; 20 | 21 | use Yii; 22 | 23 | /** 24 | * This is the base-model class for table "". 25 | * 26 | columns as $column): ?> 27 | * @property phpType} \${$column->name}\n" ?> 28 | 29 | 30 | * 31 | $relation): ?> 32 | * @property 33 | 34 | 35 | */ 36 | class extends baseClass, '\\') . "\n" ?> 37 | { 38 | /** 39 | * @inheritdoc 40 | */ 41 | public static function tableName() 42 | { 43 | return ''; 44 | } 45 | 46 | /** 47 | * @inheritdoc 48 | */ 49 | public function rules() 50 | { 51 | return []; 52 | } 53 | 54 | /** 55 | * @inheritdoc 56 | */ 57 | public function attributeLabels() 58 | { 59 | return [ 60 | $label): ?> 61 | " . $generator->generateString($label) . ",\n" ?> 62 | 63 | ]; 64 | } 65 | $relation) 66 | { 67 | $name = str_replace("0", "", $name);?> 68 | 69 | 70 | 71 | 74 | /** 75 | * @return \yii\db\ActiveQuery 76 | */ 77 | public function get() 78 | { 79 | 80 | } 81 | 83 | } 84 | -------------------------------------------------------------------------------- /crud/default/search.php: -------------------------------------------------------------------------------- 1 | modelClass); 13 | $searchModelClass = StringHelper::basename($generator->searchModelClass); 14 | $rules = $generator->generateSearchRules(); 15 | $labels = $generator->generateSearchLabels(); 16 | $searchAttributes = $generator->getSearchAttributes(); 17 | $searchConditions = $generator->generateSearchConditions(); 18 | 19 | echo " 21 | 22 | namespace searchModelClass, '\\')) ?>; 23 | 24 | use yii\base\Model; 25 | use yii\data\ActiveDataProvider; 26 | use modelClass, '\\') ?>; 27 | 28 | /** 29 | * represents the model behind the search form about . 30 | */ 31 | class extends Model 32 | { 33 | public $; 34 | 35 | public function rules() 36 | { 37 | return [ 38 | , 39 | ]; 40 | } 41 | 42 | /** 43 | * @inheritdoc 44 | */ 45 | public function attributeLabels() 46 | { 47 | return [ 48 | $label): ?> 49 | '" . addslashes($label) . "',\n" ?> 50 | 51 | ]; 52 | } 53 | 54 | public function search($params) 55 | { 56 | $query = ::find(); 57 | $dataProvider = new ActiveDataProvider([ 58 | 'query' => $query, 59 | ]); 60 | 61 | if (!($this->load($params) && $this->validate())) { 62 | return $dataProvider; 63 | } 64 | 65 | 66 | 67 | return $dataProvider; 68 | } 69 | 70 | protected function addCondition($query, $attribute, $partialMatch = false) 71 | { 72 | $value = $this->$attribute; 73 | if (trim($value) === '') { 74 | return; 75 | } 76 | if ($partialMatch) { 77 | $value = '%' . strtr($value, ['%'=>'\%', '_'=>'\_', '\\'=>'\\\\']) . '%'; 78 | $query->andWhere(['like', $attribute, $value]); 79 | } else { 80 | $query->andWhere([$attribute => $value]); 81 | } 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /crud/providers/CallbackProvider.php: -------------------------------------------------------------------------------- 1 | findValue($this->getModelKey($column->name, $model), $this->activeFields); 22 | if ($key) 23 | { 24 | return $this->activeFields[$key]($column, $model); 25 | } 26 | } 27 | 28 | public function prependActiveField($column, $model) 29 | { 30 | $key = $this->findValue($this->getModelKey($column->name, $model), $this->prependActiveFields); 31 | if ($key) 32 | { 33 | return $this->prependActiveFields[$key]($column, $model); 34 | } 35 | } 36 | 37 | public function appendActiveField($column, $model) 38 | { 39 | $key = $this->findValue($this->getModelKey($column->name, $model), $this->appendActiveFields); 40 | if ($key) 41 | { 42 | return $this->appendActiveFields[$key]($column, $model); 43 | } 44 | } 45 | 46 | 47 | public function attributeFormat($column, $model) 48 | { 49 | $key = $this->findValue($this->getModelKey($column->name, $model), $this->attributeFormats); 50 | if ($key) 51 | { 52 | return $this->attributeFormats[$key]($column, $model); 53 | } 54 | } 55 | 56 | public function columnFormat($column, $model) 57 | { 58 | $key = $this->findValue($this->getModelKey($column->name, $model), $this->columnFormats); 59 | if ($key) 60 | { 61 | return $this->columnFormats[$key]($column, $model); 62 | } 63 | } 64 | 65 | private function getModelKey($attribute, $model) 66 | { 67 | return $model::className() . '.' . $attribute; 68 | } 69 | 70 | private function findValue($subject, $array) 71 | { 72 | foreach ($array AS $key => $value) 73 | { 74 | if (preg_match('/' . $key . '/', $subject)) 75 | { 76 | return $key; 77 | } 78 | } 79 | } 80 | 81 | } -------------------------------------------------------------------------------- /crud/default/views/_form.php: -------------------------------------------------------------------------------- 1 | modelClass; 12 | $safeAttributes = $model->safeAttributes(); 13 | if (empty($safeAttributes)) { 14 | $safeAttributes = $model->getTableSchema()->columnNames; 15 | } 16 | 17 | echo " 19 | 20 | use yii\helpers\Html; 21 | use yii\bootstrap\ActiveForm; 22 | 23 | /** 24 | * @var yii\web\View $this 25 | * @var modelClass, '\\') ?> $model 26 | * @var yii\widgets\ActiveForm $form 27 | */ 28 | ?> 29 | 30 |
31 | 32 | $form = ActiveForm::begin(['layout' => 'formLayout ?>', 'enableClientValidation' => false]); ?> 33 | 34 |
35 | echo $form->errorSummary($model); ?> 36 | beginBlock('main'); ?>\n"; ?> 37 | 38 |

39 | getTableSchema()->columns[$attribute])){ 41 | $column = $generator->getTableSchema()->columns[$attribute]; 42 | 43 | if(!in_array($column->name,Yii::$app->params['globally_hidden_on_create'])) 44 | { 45 | $prepend = $generator->prependActiveField($column, $model); 46 | $field = $generator->activeField($column, $model); 47 | $append = $generator->appendActiveField($column, $model); 48 | 49 | if ($prepend) 50 | { 51 | echo "\n\t\t\t"; 52 | } 53 | if ($field) 54 | { 55 | echo "\n\t\t\t"; 56 | } 57 | if ($append) 58 | { 59 | echo "\n\t\t\t"; 60 | } 61 | } 62 | } 63 | 64 | } ?> 65 | 66 |

67 | endBlock(); ?>"; ?> 68 | 69 | '$label', 75 | 'content' => \$this->blocks['main'], 76 | 'active' => true, 77 | ], 78 | EOS; 79 | ?> 80 | 81 | false, 86 | 'items' => [ $items ] 87 | ] 88 | ); 89 | ?>"; 90 | ?> 91 | 92 |
93 | 94 | Html::submitButton(' '.($model->isNewRecord ? 'Oluştur' : 'Kaydet'), ['class' => $model->isNewRecord ? 95 | 'btn btn-primary' : 'btn btn-primary']) ?> 96 | 97 | ActiveForm::end(); ?> 98 | 99 |
100 | 101 |
102 | -------------------------------------------------------------------------------- /crud/default/controller.php: -------------------------------------------------------------------------------- 1 | controllerClass); 13 | $modelClass = StringHelper::basename($generator->modelClass); 14 | $searchModelClass = StringHelper::basename($generator->searchModelClass); 15 | if ($modelClass === $searchModelClass) { 16 | $searchModelAlias = $searchModelClass.'Search'; 17 | } 18 | 19 | $pks = $generator->getTableSchema()->primaryKey; 20 | $urlParams = $generator->generateUrlParams(); 21 | $actionParams = $generator->generateActionParams(); 22 | $actionParamComments = $generator->generateActionParamComments(); 23 | 24 | echo " 26 | 27 | namespace controllerClass, '\\')) ?>; 28 | 29 | use modelClass, '\\') ?>; 30 | use searchModelClass, '\\') ?> as ; 31 | use baseControllerClass, '\\') ?>; 32 | use yii\web\HttpException; 33 | use yii\filters\VerbFilter; 34 | use yii\filters\AccessControl; 35 | use yii\helpers\Url; 36 | 37 | /** 38 | * implements the CRUD actions for model. 39 | */ 40 | class extends baseControllerClass) . "\n" ?> 41 | { 42 | /** 43 | * Lists all models. 44 | * @return mixed 45 | */ 46 | public function actionIndex() 47 | { 48 | $searchModel = new ; 49 | $dataProvider = $searchModel->search($_GET); 50 | 51 | Url::remember(); 52 | return $this->render('index', [ 53 | 'dataProvider' => $dataProvider, 54 | 'searchModel' => $searchModel, 55 | ]); 56 | } 57 | 58 | /** 59 | * Displays a single model. 60 | * 61 | * @return mixed 62 | */ 63 | public function actionView() 64 | { 65 | Url::remember(); 66 | return $this->render('view', [ 67 | 'model' => $this->findModel(), 68 | ]); 69 | } 70 | 71 | /** 72 | * Creates a new model. 73 | * If creation is successful, the browser will be redirected to the 'view' page. 74 | * @return mixed 75 | */ 76 | public function actionCreate() 77 | { 78 | $model = new ; 79 | 80 | try { 81 | if ($model->load($_POST) && $model->save()) { 82 | return $this->redirect(Url::previous()); 83 | } elseif (!\Yii::$app->request->isPost) { 84 | $model->load($_GET); 85 | } 86 | } catch (\Exception $e) { 87 | $msg = (isset($e->errorInfo[2]))?$e->errorInfo[2]:$e->getMessage(); 88 | $model->addError('_exception', $msg); 89 | } 90 | return $this->render('create', ['model' => $model,]); 91 | } 92 | 93 | /** 94 | * Updates an existing model. 95 | * If update is successful, the browser will be redirected to the 'view' page. 96 | * 97 | * @return mixed 98 | */ 99 | public function actionUpdate() 100 | { 101 | $model = $this->findModel(); 102 | 103 | if ($model->load($_POST) && $model->save()) { 104 | return $this->redirect(Url::previous()); 105 | } else { 106 | return $this->render('update', [ 107 | 'model' => $model, 108 | ]); 109 | } 110 | } 111 | 112 | /** 113 | * Deletes an existing model. 114 | * If deletion is successful, the browser will be redirected to the 'index' page. 115 | * 116 | * @return mixed 117 | */ 118 | public function actionDelete() 119 | { 120 | $this->findModel()->delete(); 121 | return $this->redirect(Url::previous()); 122 | } 123 | 124 | /** 125 | * Finds the model based on its primary key value. 126 | * If the model is not found, a 404 HTTP exception will be thrown. 127 | * 128 | * @return the loaded model 129 | * @throws HttpException if the model cannot be found 130 | */ 131 | protected function findModel() 132 | { 133 | \$$pk"; 140 | } 141 | $condition = '[' . implode(', ', $condition) . ']'; 142 | } 143 | ?> 144 | if (($model = ::findOne()) !== null) { 145 | return $model; 146 | } else { 147 | throw new HttpException(404, 'The requested page does not exist.'); 148 | } 149 | } 150 | } 151 | -------------------------------------------------------------------------------- /commands/BatchController.php: -------------------------------------------------------------------------------- 1 | 9 | */ 10 | class BatchController extends Controller 11 | { 12 | /** 13 | * @var bool whether to generate and overwrite all files 14 | */ 15 | public $generate = true; 16 | /** 17 | * @var bool whether to generate and overwrite all files 18 | */ 19 | public $overwrite = true; 20 | /** 21 | * @var bool whether to overwrite extended models (from ModelBase) 22 | */ 23 | public $extendedModels = false; 24 | /** 25 | * @var array table names for generating models and CRUDs 26 | */ 27 | public $tables = ['urun','urun_kategori','restoran','menu','menu_urun']; 28 | /** 29 | * @var string eg. `app_` 30 | */ 31 | public $tablePrefix = ''; 32 | public $tableNameMap = []; 33 | public $modelNamespace = 'backend\\models '; 34 | public $modelDb = 'db'; 35 | public $modelBaseClass = 'common\models\VysModel'; 36 | public $crudControllerNamespace = 'backend\\controllers'; 37 | public $crudViewPath = '@backend/views'; 38 | public $crudPathPrefix = ''; 39 | public $crudProviders = []; 40 | public $crudBaseControllerClass = 'common\controllers\VysController'; 41 | /** 42 | * @inheritdoc 43 | */ 44 | public function options($id) 45 | { 46 | return array_merge( 47 | parent::options($id), 48 | [ 49 | 'generate', 50 | 'overwrite', 51 | 'extendedModels', 52 | 'tables', 53 | 'tablePrefix', 54 | 'modelDb', 55 | 'modelNamespace', 56 | 'modelBaseClass', 57 | 'crudControllerNamespace', 58 | 'crudViewPath', 59 | 'crudPathPrefix', 60 | 'crudProviders', 61 | 'crudBaseControllerClass' 62 | ] 63 | ); 64 | } 65 | /** 66 | * This command echoes what you have entered as the message. 67 | * 68 | * @param string $message the message to be echoed. 69 | */ 70 | public function actionIndex() 71 | { 72 | echo "Running batch...\n"; 73 | $config = $this->getYiiConfiguration(); 74 | $config['id'] = 'temp'; 75 | // create models 76 | foreach ($this->tables AS $table) { 77 | #var_dump($this->tableNameMap, $table);exit; 78 | $params = [ 79 | 'interactive' => $this->interactive, 80 | 'template' => 'default', 81 | 'ns' => $this->modelNamespace, 82 | 'db' => $this->modelDb, 83 | 'tableName' => $table, 84 | 'tablePrefix' => $this->tablePrefix, 85 | 'generateModelClass' => $this->extendedModels, 86 | 'modelClass' => isset($this->tableNameMap[$table]) ? $this->tableNameMap[$table] : 87 | Inflector::camelize($table), // TODO: setting is not recognized in giix 88 | 'baseClass' => $this->modelBaseClass, 89 | 'tableNameMap' => $this->tableNameMap 90 | ]; 91 | $route = 'gii/giix-model'; 92 | $app = \Yii::$app; 93 | $temp = new \yii\console\Application($config); 94 | $temp->runAction(ltrim($route, '/'), $params); 95 | unset($temp); 96 | \Yii::$app = $app; 97 | } 98 | // create CRUDs 99 | $providers = ArrayHelper::merge($this->crudProviders, Generator::getCoreProviders()); 100 | foreach ($this->tables AS $table) { 101 | $table = str_replace($this->tablePrefix,'',$table); 102 | $name = isset($this->tableNameMap[$table]) ? $this->tableNameMap[$table] : Inflector::camelize($table); 103 | $params = [ 104 | 'interactive' => $this->interactive, 105 | 'overwrite' => $this->overwrite, 106 | 'template' => 'default', 107 | 'modelClass' => $this->modelNamespace . '\\' . $name, 108 | 'searchModelClass' => $this->modelNamespace . '\\search\\' . $name . 'Search', 109 | 'controllerClass' => $this->crudControllerNamespace . '\\' . $name . 'Controller', 110 | 'viewPath' => $this->crudViewPath, 111 | 'pathPrefix' => $this->crudPathPrefix, 112 | 'actionButtonClass' => 'yii\\grid\\ActionColumn', 113 | 'baseControllerClass' => $this->crudBaseControllerClass, 114 | 'providerList' => implode(',', $providers), 115 | ]; 116 | $route = 'gii/giix-crud'; 117 | $app = \Yii::$app; 118 | $temp = new \yii\console\Application($config); 119 | $temp->runAction(ltrim($route, '/'), $params); 120 | unset($temp); 121 | \Yii::$app = $app; 122 | } 123 | } 124 | /** 125 | * TODO should be removed, if this issue is closed -> https://github.com/yiisoft/yii2/pull/5687 126 | * @return array 127 | */ 128 | protected function getYiiConfiguration() 129 | { 130 | $config = \yii\helpers\ArrayHelper::merge( 131 | require(\Yii::getAlias('@app') . '/../common/config/main.php'), 132 | (is_file(\Yii::getAlias('@app') . '/../common/config/main-local.php')) ? 133 | require(\Yii::getAlias('@app') . '/../common/config/main-local.php') 134 | : [], 135 | require(\Yii::getAlias('@app') . '/../console/config/main.php'), 136 | (is_file(\Yii::getAlias('@app') . '/../console/config/main-local.php')) ? 137 | require(\Yii::getAlias('@app') . '/../console/config/main-local.php') 138 | : [] 139 | ); 140 | return $config; 141 | } 142 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | yii2-giix 2 | =========== 3 | 4 | Extended models and CRUDs for Gii, the code generator of Yii2 Framework 5 | 6 | **PROJECT IS IN DEVELOPMENT STAGE!** 7 | 8 | 9 | What is it? 10 | ----------- 11 | 12 | Yii2-Giix provides templates for model and CRUD generation with relation support and a sophisticated UI. 13 | A main project goal is porting many features and learnings from yii2-giiant, gtc, giix, awecrud and others into one solution. 14 | 15 | 16 | Installation 17 | ------------ 18 | 19 | The preferred way to install this extension is through [composer](http://getcomposer.org/download/). 20 | 21 | composer.phar require veyselsahin/yii2-giix:"*" 22 | 23 | The generators are registered automatically in the application bootstrap process, if the Gii module is enabled 24 | 25 | Usage 26 | ----- 27 | 28 | Visit your application's Gii (eg. `index.php?r=gii` and choose one of the generators from the main menu screen. 29 | 30 | For basic usage instructions see the [Yii2 Guide section for Gii](http://www.yiiframework.com/doc-2.0/guide-tool-gii.html). 31 | 32 | ### Command Line Batches 33 | 34 | You can run batches of base-model and CRUD generation with the build in batch command: 35 | 36 | ./yii giix-batch --tables=profile,social_account,user,token 37 | 38 | It will process the given tables, for more details see `./yii help giix-batch`. 39 | 40 | 41 | Features 42 | -------- 43 | 44 | ### Model generator 45 | 46 | - generates separate model classes to customize and base models classes to regenerate 47 | - table prefixes can be stipped off model class names (not bound to db connection setting) 48 | 49 | ### CRUD generator 50 | 51 | - model, view and controller locations can be customized to use subfolders 52 | - horizontal and vertical form layout 53 | - action button class customization 54 | - input, attribute, column and relation customization with provider queue 55 | - callback provider to inject any kind of code for inputs, attributes and columns via dependency injection 56 | 57 | #### Providers 58 | 59 | - *CallbackProvider* universal provider to modify any input, attribute or column with highly flexible callback functions 60 | - *RelationProvider* renders code for relations (eg. links, dropdowns) 61 | - *EditorProvider* renders RTE, like `Ckeditor` as input widget 62 | - *DateTimeProvider* renders date inputs 63 | 64 | Use custom generators, model and crud templates 65 | ---------------------------- 66 | ``` 67 | 68 | $config['modules']['gii'] = [ 69 | 'class' => 'yii\gii\Module', 70 | 'allowedIPs' => ['127.0.0.1'], 71 | 'generators' => [ 72 | // generator name 73 | 'rb-model' => [ 74 | //generator class 75 | 'class' => 'veyselsahin\giix\model\Generator', 76 | //setting for out templates 77 | 'templates' => [ 78 | // template name => path to template 79 | 'rbModel' => 80 | '@app/giiTemplates/model/default', 81 | 82 | ] 83 | ] 84 | ], 85 | ]; 86 | 87 | ``` 88 | 89 | Customization with providers 90 | ---------------------------- 91 | 92 | In many cases you want to exchange i.e. some inputs with a customized version for your project. 93 | Examples for this use-case are editors, file-uploads or choosers, complex input widget with a modal screen, getting 94 | data via AJAX and so on. 95 | 96 | With Yii2-Giix Providers you can create a queue of instances which may provide custom code depending on more complex 97 | rules. Take a look at some existing [giix providers](https://github.com/veyselsahin/yii2-giix/tree/develop/crud/providers). 98 | 99 | Configure providers, add this to your provider list in the form: 100 | 101 | \veyselsahin\giix\crud\providers\EditorProvider, 102 | \veyselsahin\giix\crud\providers\SelectProvider, 103 | 104 | And configure the settings of the provider, add setting via dependecy injection this to your application config, eg. in `console/config/bootstrap.php`: 105 | 106 | \Yii::$container->set( 107 | 'veyselsahin\giix\crud\providers\EditorProvider', 108 | [ 109 | 'columnNames' => ['description'] 110 | ] 111 | ); 112 | 113 | This will render a Ckeditor widget for every column named `description`. 114 | 115 | field($model, 'description')->widget( 116 | \dosamigos\ckeditor\CKEditor::className(), 117 | [ 118 | 'options' => ['rows' => 6], 119 | 'preset' => 'basic' 120 | ]) ?> 121 | 122 | 123 | ### Universal `CallbackProvider` 124 | 125 | Configuration via DI container: 126 | 127 | ``` 128 | \Yii::$container->set( 129 | 'veyselsahin\giix\crud\providers\CallbackProvider', 130 | [ 131 | 132 | 'activeFields' => [ 133 | 134 | /** 135 | * Generate a checkbox for specific column (model attribute) 136 | */ 137 | 'common\models\Foo.isAvailable' => function ($attribute, $generator) { 138 | $data = \yii\helpers\VarDumper::export([0 => 'Nein', 1 => 'Ja']); 139 | return <<field(\$model, '{$attribute}')->checkbox({$data}); 141 | INPUT; 142 | }, 143 | ], 144 | 145 | 146 | 'columnFormats' => [ 147 | 148 | /** 149 | * generate custom HTML in column 150 | */ 151 | 'common\models\Foo.html' => function ($attribute, $generator) { 152 | 153 | return << 'html', 156 | 'label'=>'FOOFOO', 157 | 'attribute' => 'item_id', 158 | 'value'=> function(\$model){ 159 | return \yii\helpers\Html::a(\$model->bar,['/crud/item/view', 'id' => \$model->link_id]); 160 | } 161 | ] 162 | FORMAT; 163 | }, 164 | 165 | 166 | /** 167 | * hide all text fields in grid 168 | */ 169 | '.+' => function ($column, $model) { 170 | if ($column->dbType == 'text') { 171 | return false; 172 | } 173 | }, 174 | 175 | /** 176 | * hide system fields in grid 177 | */ 178 | 'created_at$|updated_at$' => function () { 179 | return false; 180 | }, 181 | 182 | ] 183 | ] 184 | ); 185 | ``` 186 | 187 | Extras 188 | ------ 189 | 190 | A detailed description how to use MySQL workbench for model updates and migration see [here](docs/using-mysql-workbench.md). 191 | 192 | 193 | Links 194 | ----- 195 | 196 | - [GitHub](https://github.com/veyselsahin/yii2-giix) 197 | - [Packagist](https://packagist.org/packages/veyselsahin/yii2-giix) 198 | 199 | 200 | php yii.php giix-batch --modelDb=cafees --modelBaseClass=common\\models\\VysModel --modelNamespace=backend\\models --tables=gruplar,sinav,sinav_sonuc 201 | 202 | php yii.php giix-batch --modelDb=cafees --modelBaseClass=common\\models\\VysModel --modelNamespace=backend\\models 203 | -------------------------------------------------------------------------------- /crud/default/views/index.php: -------------------------------------------------------------------------------- 1 | yii\grid\DataColumn::className(), 16 | "attribute" => "{$attr}", 17 | "value" => function(\$model){ 18 | \$user=\$model->get{$uptOrCrt}User(); 19 | if(isset(\$user->username)){ 20 | return yii\helpers\Html::a(\$user->username,["user/profile", 'id' => \$user->id],["data-pjax"=>0]); 21 | } 22 | else{ 23 | return ''; 24 | } 25 | }, 26 | "format" => "raw" 27 | ] 28 | EOS; 29 | return $c; 30 | } 31 | } 32 | /** 33 | * @var yii\web\View $this 34 | * @var fproject\giix\crud\Generator $generator 35 | */ 36 | 37 | $urlParams = $generator->generateUrlParams(); 38 | $nameAttribute = $generator->getNameAttribute(); 39 | 40 | echo " 42 | 43 | use yii\helpers\Html; 44 | use indexWidgetType === 'grid' ? "yii\\grid\\GridView" : "yii\\widgets\\ListView" ?>; 45 | 46 | /** 47 | * @var yii\web\View $this 48 | * @var yii\data\ActiveDataProvider $dataProvider 49 | * @var searchModelClass, '\\') ?> $searchModel 50 | */ 51 | 52 | $this->title = 'modelClass)) ?>'; 53 | $this->params['breadcrumbs'][] = $this->title; 54 | ?> 55 | 56 |
57 | 58 | indexWidgetType === 'grid' ? "// " : "") ?> 60 | echo $this->render('_search', ['model' =>$searchModel]); 61 | ?> 62 | 63 |
64 |

65 | Html::a(' 66 | Yeni modelClass)) ?>', ['create'], 67 | ['class' => 'btn btn-success']) ?> 68 |

69 | 70 |

71 | 72 | Çöp Kutusu', 'cop', 73 | ['class' => 'btn btn-success']) ?> 74 |

75 | 76 |
77 | 78 | 79 | modelClass; 82 | ?> 83 | getModelRelations($model) AS $relation): ?> 84 | multiple) ? 'arrow-right' : 'arrow-left'; 87 | if ($generator->isPivotRelation($relation)) 88 | { 89 | $iconType = 'random'; 90 | } 91 | $controller = $generator->pathPrefix . Inflector::camel2id( 92 | StringHelper::basename($relation->modelClass), 93 | '-', 94 | true 95 | ); 96 | $route = $generator->createRelationRoute($relation, 'index'); 97 | $label = Inflector::titleize(StringHelper::basename($relation->modelClass), '-', true); 98 | $items[] = [ 99 | 'label' => ' ' . $label . '', 100 | 'url' => [$route] 101 | ] 102 | ?> 103 | 104 | 105 | 106 | echo \yii\bootstrap\ButtonDropdown::widget( 107 | [ 108 | 'id' => 'giix-relations', 109 | 'encodeLabel' => false, 110 | 'label' => ' İlişkili', 111 | 'dropdown' => [ 112 | 'options' => [ 113 | 'class' => 'dropdown-menu-right' 114 | ], 115 | 'encodeLabels' => false, 116 | 'items' => 117 | ], 118 | ] 119 | ); 120 | " ?> 121 |
122 |
123 | 124 | indexWidgetType === 'grid'): ?> 125 | echo GridView::widget([ 126 | 'dataProvider' => $dataProvider, 127 | 'filterModel' => $searchModel, 128 | 'columns' => [ 129 | getTableSchema()->columns as $column) 133 | { 134 | if ($column->name == "olusturan") 135 | { 136 | $format = getColumn("Create"); 137 | } else if ($column->name == "guncelleyen") 138 | { 139 | $format = getColumn("Update"); 140 | } else 141 | { 142 | $format = trim($generator->columnFormat($column, $model)); 143 | } 144 | 145 | if ($column->name != "silindi") 146 | { 147 | if ($format == false) continue; 148 | if (++$count < 8) 149 | { 150 | echo "\t\t\t{$format},\n"; 151 | } else 152 | { 153 | echo "\t\t\t{$format},\n"; 154 | } 155 | } 156 | } 157 | ?> 158 | [ 159 | 'class' => 'actionButtonClass ?>', 160 | 'urlCreator' => function($action, $model, $key, $index) { 161 | // using the column name as key, not mapping to 'id' like the standard generator 162 | $params = is_array($key) ? $key : [$model->primaryKey()[0] => (string) $key]; 163 | $params[0] = \Yii::$app->controller->id ? \Yii::$app->controller->id . '/' . $action : $action; 164 | return \yii\helpers\Url::toRoute($params); 165 | }, 166 | 'contentOptions' => ['nowrap'=>'nowrap'] 167 | ], 168 | ], 169 | ]); ?> 170 | 171 | echo ListView::widget([ 172 | 'dataProvider' => $dataProvider, 173 | 'itemOptions' => ['class' => 'item'], 174 | 'itemView' => function ($model, $key, $index, $widget) { 175 | return Html::a(Html::encode($model->), ['view', ]); 176 | }, 177 | ]); ?> 178 | 179 | 180 |
181 | 182 | 183 | -------------------------------------------------------------------------------- /crud/default/views/view.php: -------------------------------------------------------------------------------- 1 | generateUrlParams(); 12 | 13 | echo " 15 | 16 | use yii\helpers\Html; 17 | use yii\grid\GridView; 18 | use yii\widgets\DetailView; 19 | use yii\widgets\Pjax; 20 | 21 | /** 22 | * @var yii\web\View $this 23 | * @var modelClass, '\\') ?> $model 24 | */ 25 | 26 | $this->title = 'modelClass) 29 | ) ?> View ' . $model->getNameAttribute() ?> . ''; 30 | $this->params['breadcrumbs'][] = ['label' => 'modelClass)) 33 | ) ?>', 'url' => ['index']]; 34 | $this->params['breadcrumbs'][] = ['label' => (string)$model->getNameAttribute() ?>, 'url' => ['view', ]]; 35 | $this->params['breadcrumbs'][] = 'View'; 36 | ?> 37 |
38 | 39 |

40 | Html::a(' Düzenle', ['update', ], 41 | ['class' => 'btn btn-info']) ?> 42 | Html::a(' Yeni modelClass) 45 | ) ?>', ['create'], ['class' => 'btn 46 | btn-success']) ?> 47 |

48 | 49 | \n"; 51 | echo " Listele', ['index'], ['class'=>'btn btn-default']) ?>\n"; 52 | echo "

\n"; 53 | ?> 54 | 55 | modelClass); ?> 56 | 57 |

58 | " . $generator->getModelNameAttribute($generator->modelClass) . " ?>" ?> 59 |

60 | 61 | 62 | beginBlock('{$generator->modelClass}'); ?>\n"; 64 | ?> 65 | 66 | echo DetailView::widget([ 67 | 'model' => $model, 68 | 'attributes' => [ 69 | getTableSchema()->columns as $column) { 71 | $format = $generator->attributeFormat($column); 72 | if ($format === false) { 73 | continue; 74 | } else { 75 | echo $format . ",\n"; 76 | } 77 | } 78 | ?> 79 | ], 80 | ]); ?> 81 | 82 |
83 | 84 | echo Html::a(' Sil', ['delete', ], 85 | [ 86 | 'class' => 'btn btn-danger', 87 | 'data-confirm' => Yii::t('app', 'Bu kaydı silmek istediğinize emin misiniz?'), 88 | 'data-method' => 'post', 89 | ]); ?> 90 | 91 | endBlock(); ?>\n\n"; ?> 92 | 93 | ' $label', 97 | 'content' => \$this->blocks['{$generator->modelClass}'], 98 | 'active' => true, 99 | ], 100 | EOS; 101 | 102 | foreach ($generator->getModelRelations($generator->modelClass, ['has_many']) as $name => $relation) { 103 | 104 | echo "\nbeginBlock('$name'); ?>\n"; 105 | 106 | // get relation info $ prepare add button 107 | $model = new $generator->modelClass; 108 | $showAllRecords = false; 109 | 110 | if ($relation->via !== null) { 111 | $pivotName = Inflector::pluralize($generator->getModelByTableName($relation->via->from[0])); 112 | $pivotRelation = $model->{'get' . $pivotName}(); 113 | $pivotPk = key($pivotRelation->link); 114 | 115 | $addButton = " Attach " . 117 | Inflector::singularize(Inflector::camel2words($name)) . 118 | "', ['" . $generator->createRelationRoute($pivotRelation, 'create') . "', '" . 119 | Inflector::singularize($pivotName) . "'=>['" . key( 120 | $pivotRelation->link 121 | ) . "'=>\$model->{$model->primaryKey()[0]}]], 122 | ['class'=>'btn btn-info btn-xs'] 123 | ) ?>\n"; 124 | } else { 125 | $addButton = ''; 126 | } 127 | 128 | // relation list, add, create buttons 129 | echo "

\n"; 130 | 131 | echo " List All " . 133 | Inflector::camel2words($name) . "', 134 | ['" . $generator->createRelationRoute($relation, 'index') . "'], 135 | ['class'=>'btn text-muted btn-xs'] 136 | ) ?>\n"; 137 | // TODO: support multiple PKs, VarDumper? 138 | echo " New " . 140 | Inflector::singularize(Inflector::camel2words($name)) . "', 141 | ['" . $generator->createRelationRoute($relation, 'create') . "', '" . 142 | Inflector::singularize($name) . "'=>['" . key($relation->link) . "'=>\$model->" . $model->primaryKey()[0] . "]], 143 | ['class'=>'btn btn-success btn-xs'] 144 | ) ?>\n"; 145 | echo $addButton; 146 | 147 | echo "

\n"; 148 | 149 | // render pivot grid 150 | if ($relation->via !== null) { 151 | $pjaxId = "pjax-{$pivotName}"; 152 | $gridRelation = $pivotRelation; 153 | $gridName = $pivotName; 154 | } else { 155 | $pjaxId = "pjax-{$name}"; 156 | $gridRelation = $relation; 157 | $gridName = $name; 158 | } 159 | 160 | $output = $generator->relationGrid([$gridRelation, $gridName, $showAllRecords]); 161 | 162 | // render relation grid 163 | if (!empty($output)): 164 | echo "'pjax-{$name}','linkSelector'=>'#pjax-{$name} ul.pagination a']) ?>\n"; 165 | echo "\n"; 166 | echo "\n"; 167 | endif; 168 | 169 | echo "endBlock() ?>\n\n"; 170 | 171 | // build tab items 172 | $label = Inflector::camel2words($name); 173 | $items .= << ' $label', 176 | 'content' => \$this->blocks['$name'], 177 | 'active' => false, 178 | ], 179 | EOS; 180 | } 181 | ?> 182 | 183 | 'relation-tabs', 189 | 'encodeLabels' => false, 190 | 'items' => [ $items ] 191 | ] 192 | ); 193 | ?>"; 194 | ?> 195 |
196 | -------------------------------------------------------------------------------- /model/Generator.php: -------------------------------------------------------------------------------- 1 | 18 | * @since 0.0.1 19 | */ 20 | class Generator extends \yii\gii\generators\model\Generator 21 | { 22 | /** 23 | * @var bool whether to overwrite (extended) model classes, will be always created, if file does not exist 24 | */ 25 | public $generateModelClass = false; 26 | 27 | /** 28 | * @var null string for the table prefix, which is ignored in generated class name 29 | */ 30 | public $tablePrefix = null; 31 | 32 | /** 33 | * @var array key-value pairs for mapping a table-name to class-name, eg. 'prefix_FOObar' => 'FooBar' 34 | */ 35 | public $tableNameMap = []; 36 | protected $classNames2; 37 | 38 | /** 39 | * @inheritdoc 40 | */ 41 | public function getName() 42 | { 43 | return 'Yii2 Giix Model'; 44 | } 45 | 46 | /** 47 | * @inheritdoc 48 | */ 49 | public function getDescription() 50 | { 51 | return 'This generator generates an ActiveRecord class and base class for the specified database table.'; 52 | } 53 | 54 | /** 55 | * @inheritdoc 56 | */ 57 | public function rules() 58 | { 59 | return array_merge( 60 | parent::rules(), 61 | [ 62 | [['generateModelClass'], 'boolean'], 63 | [['tablePrefix'], 'safe'], 64 | ] 65 | ); 66 | } 67 | 68 | /** 69 | * @inheritdoc 70 | */ 71 | public function attributeLabels() 72 | { 73 | return array_merge( 74 | parent::attributeLabels(), 75 | [ 76 | 'generateModelClass' => 'Generate Model Class', 77 | ] 78 | ); 79 | } 80 | 81 | /** 82 | * @inheritdoc 83 | */ 84 | public function hints() 85 | { 86 | return array_merge( 87 | parent::hints(), 88 | [ 89 | 'generateModelClass' => 'This indicates whether the generator should generate the model class, this should usually be done only once. The model-base class is always generated.', 90 | 'tablePrefix' => 'Custom table prefix, eg app_.
Note! overrides yii\db\Connection prefix!', 91 | 92 | ] 93 | ); 94 | } 95 | 96 | /** 97 | * @inheritdoc 98 | */ 99 | public function requiredTemplates() 100 | { 101 | return ['model.php', 'model-extended.php']; 102 | } 103 | 104 | /** 105 | * @inheritdoc 106 | */ 107 | public function generate() 108 | { 109 | $files = []; 110 | $relations = $this->generateRelations(); 111 | $db = $this->getDbConnection(); 112 | foreach ($this->getTableNames() as $tableName) { 113 | 114 | $className = $this->generateClassName($tableName); 115 | 116 | $tableSchema = $db->getTableSchema($tableName); 117 | $params = [ 118 | 'tableName' => $tableName, 119 | 'className' => $className, 120 | 'tableSchema' => $tableSchema, 121 | 'labels' => $this->generateLabels($tableSchema), 122 | 'rules' => $this->generateRules($tableSchema), 123 | 'relations' => isset($relations[$className]) ? $relations[$className] : [], 124 | ]; 125 | 126 | $files[] = new CodeFile( 127 | Yii::getAlias('@' . str_replace('\\', '/', $this->ns)) . '/base/' . $className . '.php', 128 | $this->render('model.php', $params) 129 | ); 130 | 131 | $modelClassFile = Yii::getAlias('@' . str_replace('\\', '/', $this->ns)) . '/' . $className . '.php'; 132 | if ($this->generateModelClass || !is_file($modelClassFile)) { 133 | $files[] = new CodeFile( 134 | $modelClassFile, 135 | $this->render('model-extended.php', $params) 136 | ); 137 | } 138 | } 139 | return $files; 140 | } 141 | 142 | /** 143 | * Generates a class name from the specified table name. 144 | * 145 | * @param string $tableName the table name (which may contain schema prefix) 146 | * 147 | * @return string the generated class name 148 | */ 149 | protected function generateClassName($tableName) 150 | { 151 | 152 | #Yii::trace("Generating class name for '{$tableName}'...", __METHOD__); 153 | if (isset($this->classNames2[$tableName])) { 154 | #Yii::trace("Using '{$this->classNames2[$tableName]}' for '{$tableName}' from classNames2.", __METHOD__); 155 | return $this->classNames2[$tableName]; 156 | } 157 | 158 | if (isset($this->tableNameMap[$tableName])) { 159 | Yii::trace("Converted '{$tableName}' from tableNameMap.", __METHOD__); 160 | return $this->classNames2[$tableName] = $this->tableNameMap[$tableName]; 161 | } 162 | 163 | if (($pos = strrpos($tableName, '.')) !== false) { 164 | $tableName = substr($tableName, $pos + 1); 165 | } 166 | 167 | $db = $this->getDbConnection(); 168 | $patterns = []; 169 | $patterns[] = "/^{$this->tablePrefix}(.*?)$/"; 170 | $patterns[] = "/^(.*?){$this->tablePrefix}$/"; 171 | $patterns[] = "/^{$db->tablePrefix}(.*?)$/"; 172 | $patterns[] = "/^(.*?){$db->tablePrefix}$/"; 173 | 174 | if (strpos($this->tableName, '*') !== false) { 175 | $pattern = $this->tableName; 176 | if (($pos = strrpos($pattern, '.')) !== false) { 177 | $pattern = substr($pattern, $pos + 1); 178 | } 179 | $patterns[] = '/^' . str_replace('*', '(\w+)', $pattern) . '$/'; 180 | } 181 | 182 | $className = $tableName; 183 | foreach ($patterns as $pattern) { 184 | if (preg_match($pattern, $tableName, $matches)) { 185 | $className = $matches[1]; 186 | Yii::trace("Mapping '{$tableName}' to '{$className}' from pattern '{$pattern}'.", __METHOD__); 187 | break; 188 | } 189 | } 190 | 191 | $returnName = Inflector::id2camel($className, '_'); 192 | Yii::trace("Converted '{$tableName}' to '{$returnName}'.", __METHOD__); 193 | return $this->classNames2[$tableName] = $returnName; 194 | } 195 | 196 | protected function generateRelations() 197 | { 198 | $relations = parent::generateRelations(); 199 | 200 | // inject namespace 201 | $ns = "\\{$this->ns}\\"; 202 | foreach ($relations AS $model => $relInfo) { 203 | foreach ($relInfo AS $relName => $relData) { 204 | 205 | $relations[$model][$relName][0] = preg_replace( 206 | '/(has[A-Za-z0-9]+\()([a-zA-Z0-9]+::)/', 207 | '$1__NS__$2', 208 | $relations[$model][$relName][0] 209 | ); 210 | $relations[$model][$relName][0] = str_replace('__NS__', $ns, $relations[$model][$relName][0]); 211 | } 212 | } 213 | return $relations; 214 | } 215 | 216 | } 217 | -------------------------------------------------------------------------------- /crud/providers/RelationProvider.php: -------------------------------------------------------------------------------- 1 | generator->getRelationByColumn($this->generator->modelClass, $column); 19 | if ($relation) 20 | { 21 | switch (true) 22 | { 23 | case (!$relation->multiple): 24 | $pk = key($relation->link); 25 | 26 | $q = new $relation->modelClass; 27 | $attrib = "ad"; 28 | if (!$q->hasAttribute("ad")) 29 | { 30 | $attrib = "username"; 31 | } 32 | 33 | $name = $this->generator->getModelNameAttribute($relation->modelClass); 34 | 35 | $method = __METHOD__; 36 | $code = <<field(\$model, '{$column->name}')->dropDownList( 39 | \yii\helpers\ArrayHelper::map({$relation->modelClass}::find()->all(),'{$pk}','{$attrib}'), 40 | ['prompt'=>'Seçin'] 41 | ); 42 | EOS; 43 | return $code; 44 | default: 45 | return null; 46 | 47 | } 48 | } 49 | } 50 | 51 | public function attributeFormat($column) 52 | { 53 | // do not handle columns with a primary key, TOOD: review(!) should not be omitted in every case 54 | if ($column->isPrimaryKey) 55 | { 56 | return null; 57 | } 58 | if ($column->name == "olusturan") 59 | { 60 | return $this->olusturanGuncelleyen($column); 61 | } else if ($column->name == "guncelleyen") 62 | { 63 | return $this->olusturanGuncelleyen($column); 64 | } 65 | $relation = $this->generator->getRelationByColumn($this->generator->modelClass, $column); 66 | 67 | 68 | if ($relation) 69 | { 70 | if ($relation->multiple) 71 | { 72 | return null; 73 | } 74 | $title = $this->generator->getModelNameAttribute($relation->modelClass); 75 | $route = $this->generator->createRelationRoute($relation, 'view'); 76 | $relationGetter = 'get' . Inflector::id2camel( 77 | str_replace('_id', '', $column->name), 78 | '_' 79 | ) . '()'; // TODO: improve detection 80 | $params = "'id' => \$model->{$column->name}"; 81 | 82 | $relationModel = new $relation->modelClass; 83 | $pks = $relationModel->primaryKey(); 84 | $paramArrayItems = ""; 85 | foreach ($pks as $attr) 86 | { 87 | $paramArrayItems .= "'{$attr}' => \$model->{$relationGetter}->one()->{$attr},"; 88 | } 89 | $q = new $relation->modelClass; 90 | $title = "ad"; 91 | if (!$q->hasAttribute("ad")) 92 | { 93 | $title = "username"; 94 | } 95 | $method = __METHOD__; 96 | $code = <<'html', 100 | 'attribute'=>'$column->name', 101 | 'value' => (\$model->{$relationGetter}->one() ? Html::a(\$model->{$relationGetter}->one()->{$title}, ['{$route}', {$paramArrayItems}]) : '?'), 102 | ] 103 | EOS; 104 | return $code; 105 | } 106 | } 107 | 108 | public function olusturanGuncelleyen($column) 109 | { 110 | $method = __METHOD__; 111 | if ($column->name == "olusturan") 112 | { 113 | $code = <<'html', 117 | 'attribute'=>'$column->name', 118 | 'value' => (\$model->getCreateBy() ? Html::a(\$model->getCreateBy()->username, ['user/profile', 'id'=>\$model->getCreatedBy()->id]) : '?'), 119 | ] 120 | EOS; 121 | return $code; 122 | } else if($column->name == "guncelleyen") 123 | { 124 | $code = <<'html', 128 | 'attribute'=>'$column->name', 129 | 'value' => (\$model->getUpdateBy() ? Html::a(\$model->getUpdateBy()->username, ['user/profile', 'id'=>\$model->getUpdateBy()->id]) : '?'), 130 | ] 131 | EOS; 132 | return $code; 133 | 134 | } 135 | } 136 | 137 | public function columnFormat($column, $model) 138 | { 139 | // do not handle columns with a primary key, TOOD: review(!) should not be omitted in every case 140 | if ($column->isPrimaryKey) 141 | { 142 | return null; 143 | } 144 | 145 | $relation = $this->generator->getRelationByColumn($model, $column); 146 | if ($relation) 147 | { 148 | if ($relation->multiple) 149 | { 150 | return null; 151 | } 152 | $title = $this->generator->getModelNameAttribute($relation->modelClass); 153 | $route = $this->generator->createRelationRoute($relation, 'view'); 154 | $method = __METHOD__; 155 | $relationGetter = 'get' . Inflector::id2camel( 156 | str_replace('_id', '', $column->name), 157 | '_' 158 | ) . '()'; // TODO: improve detection 159 | 160 | $pk = key($relation->link); 161 | $relationModel = new $relation->modelClass; 162 | $pks = $relationModel->primaryKey(); 163 | $paramArrayItems = ""; 164 | foreach ($pks as $attr) 165 | { 166 | $paramArrayItems .= "'{$attr}' => \$rel->{$attr},"; 167 | } 168 | 169 | if ($column->name == "olusturan") 170 | { 171 | 172 | $code = << yii\grid\DataColumn::className(), 175 | "attribute" => "olusturan", 176 | "value" => function(\$model){ 177 | \$user=\$model->getUpdateUser(); 178 | return yii\helpers\Html::a(\$user->username,["user/profile", 'id' => \$user->id],["data-pjax"=>0]); 179 | }, 180 | "format" => "raw" 181 | ] 182 | EOS; 183 | return $code; 184 | } 185 | 186 | 187 | $code = << yii\\grid\\DataColumn::className(), 190 | "attribute" => "{$column->name}", 191 | "value" => function(\$model){ 192 | if (\$rel = \$model->{$relationGetter}->one()) { 193 | if (isset(\$rel->ad)) 194 | { 195 | return yii\helpers\Html::a(\$rel->ad,["{$route}", {$paramArrayItems}],["data-pjax"=>0]); 196 | } 197 | if (isset(\$rel->username)) 198 | { 199 | return yii\helpers\Html::a(\$rel->username,["{$route}", {$paramArrayItems}],["data-pjax"=>0]); 200 | } 201 | return yii\helpers\Html::a(\$rel->{$title},["{$route}", {$paramArrayItems}],["data-pjax"=>0]); 202 | } else { 203 | return ''; 204 | } 205 | }, 206 | "format" => "raw", 207 | ] 208 | EOS; 209 | 210 | return $code; 211 | } 212 | } 213 | 214 | 215 | // TODO: params is an array, because we need the name, improve params 216 | public function relationGrid($data) 217 | { 218 | $name = $data[1]; 219 | $relation = $data[0]; 220 | $showAllRecords = isset($data[2]) ? $data[2] : false; 221 | $model = new $relation->modelClass; 222 | $counter = 0; 223 | $columns = ''; 224 | 225 | foreach ($model->attributes AS $attr => $value) 226 | { 227 | // max seven columns 228 | if ($counter > 8) 229 | { 230 | continue; 231 | } 232 | // skip virtual attributes 233 | if (!isset($model->tableSchema->columns[$attr])) 234 | { 235 | continue; 236 | } 237 | // don't show current model 238 | if (key($relation->link) == $attr) 239 | { 240 | continue; 241 | } 242 | 243 | $code = $this->generator->columnFormat($model->tableSchema->columns[$attr], $model); 244 | if ($code == false) 245 | { 246 | continue; 247 | } 248 | $columns .= $code . ",\n"; 249 | $counter++; 250 | } 251 | 252 | $reflection = new \ReflectionClass($relation->modelClass); 253 | if (!$this->generator->isPivotRelation($relation)) 254 | { 255 | $template = '{view} {update}'; 256 | $deleteButtonPivot = ''; 257 | } else 258 | { 259 | $template = '{view} {delete}'; 260 | $deleteButtonPivot = << function (\$url, \$model) { 262 | return Html::a('', \$url, [ 263 | 'class' => 'text-danger', 264 | 'title' => Yii::t('yii', 'Remove'), 265 | 'data-confirm' => Yii::t('yii', 'Are you sure you want to delete the related item?'), 266 | 'data-method' => 'get', 267 | 'data-pjax' => '0', 268 | ]); 269 | }, 270 | 'view' => function (\$url, \$model) { 271 | return Html::a( 272 | '', 273 | \$url, 274 | [ 275 | 'data-title' => Yii::t('yii', 'View Pivot Record'), 276 | 'data-toggle' => 'tooltip', 277 | 'data-pjax' => '0', 278 | 'class' => 'text-muted' 279 | ] 280 | ); 281 | }, 282 | EOS; 283 | } 284 | 285 | $controller = $this->generator->pathPrefix . Inflector::camel2id($reflection->getShortName(), '-', true); 286 | $actionColumn = << 'yii\grid\ActionColumn', 289 | 'template' => '$template', 290 | 'contentOptions' => ['nowrap'=>'nowrap'], 291 | 'urlCreator' => function(\$action, \$model, \$key, \$index) { 292 | // using the column name as key, not mapping to 'id' like the standard generator 293 | \$params = is_array(\$key) ? \$key : [\$model->primaryKey()[0] => (string) \$key]; 294 | \$params[0] = '$controller' . '/' . \$action; 295 | return \yii\helpers\Url::toRoute(\$params); 296 | }, 297 | 'buttons' => [ 298 | $deleteButtonPivot 299 | ], 300 | 'controller' => '$controller' 301 | ] 302 | EOS; 303 | $columns .= $actionColumn . ","; 304 | 305 | $query = $showAllRecords ? 306 | "'query' => \\{$relation->modelClass}::find()" : 307 | "'query' => \$model->get{$name}()"; 308 | $code = ''; 309 | $code .= << new \\yii\\data\\ActiveDataProvider([{$query}, 'pagination' => ['pageSize' => 10]]), 312 | 'columns' => [$columns] 313 | ]); 314 | EOS; 315 | return $code; 316 | } 317 | 318 | 319 | } -------------------------------------------------------------------------------- /crud/Generator.php: -------------------------------------------------------------------------------- 1 | 23 | * @since 1.0 24 | */ 25 | class Generator extends \yii\gii\generators\crud\Generator 26 | { 27 | #public $codeModel; 28 | public $actionButtonClass = 'yii\grid\ActionColumn'; 29 | public $providerList = ""; 30 | public $viewPath = '@backend/views'; 31 | public $tablePrefix = null; 32 | public $pathPrefix = null; 33 | public $formLayout = 'horizontal'; 34 | public $requires = []; 35 | private $_p = []; 36 | 37 | static public function getCoreProviders() 38 | { 39 | return [ 40 | CallbackProvider::className(), 41 | EditorProvider::className(), 42 | DateTimeProvider::className(), 43 | RelationProvider::className() 44 | ]; 45 | } 46 | 47 | public function getName() 48 | { 49 | return 'Yii2 Giix CRUD'; 50 | } 51 | 52 | public function getDescription() 53 | { 54 | return 'This generator generates an extended version of CRUDs.'; 55 | } 56 | 57 | /** 58 | * @inheritdoc 59 | */ 60 | public function successMessage() 61 | { 62 | $return = 'The code has been generated successfully. Please require the following packages with composer:'; 63 | $return .= '
' . implode('
', $this->requires) . '
'; 64 | return $return; 65 | } 66 | 67 | /** 68 | * Prepare providers 69 | * 70 | * @param array $data 71 | * @param null $formName 72 | * 73 | * @return bool|void 74 | */ 75 | public function init() 76 | { 77 | \Yii::trace("Initializing giix CRUD generator for model '{$this->modelClass}'", __METHOD__); 78 | parent::init(); 79 | } 80 | 81 | private function initializeProviders(){ 82 | // TODO: this is a hotfix for an already initialized provider queue on action re-entry 83 | if ($this->_p !== []) { 84 | return; 85 | } 86 | $this->providerList=RelationProvider::className(); 87 | if ($this->providerList) { 88 | foreach (explode(',', $this->providerList) AS $class) { 89 | $class = trim($class); 90 | if (!$class) { 91 | continue; 92 | } 93 | $obj = \Yii::createObject(['class' => $class]); 94 | $obj->generator = $this; 95 | $this->_p[] = $obj; 96 | #\Yii::trace("Initialized provider '{$class}'", __METHOD__); 97 | } 98 | } 99 | } 100 | 101 | /** 102 | * @inheritdoc 103 | */ 104 | public function hints() 105 | { 106 | return array_merge( 107 | parent::hints(), 108 | [ 109 | 'providerList' => 'Comma separated list of provider class names, make sure you are using the full namespaced path app\providers\CustomProvider1,
app\providers\CustomProvider2
.', 110 | 'viewPath' => 'Output path for view files, eg. @backend/views/crud.', 111 | 'pathPrefix' => 'Customized route/subfolder for controllers and views eg. crud/. Note! Should correspond to viewPath.', 112 | ] 113 | ); 114 | } 115 | 116 | /** 117 | * @inheritdoc 118 | */ 119 | public function rules() 120 | { 121 | return array_merge( 122 | parent::rules(), 123 | [ 124 | [['providerList'], 'filter', 'filter' => 'trim'], 125 | [['actionButtonClass', 'viewPath', 'pathPrefix'], 'safe'], 126 | [['viewPath'], 'required'], 127 | ] 128 | ); 129 | } 130 | 131 | /** 132 | * @inheritdoc 133 | */ 134 | public function stickyAttributes() 135 | { 136 | return array_merge(parent::stickyAttributes(), ['providerList', 'actionButtonClass', 'viewPath', 'pathPrefix']); 137 | } 138 | 139 | 140 | public function getModelNameAttribute($modelClass) 141 | { 142 | $model = new $modelClass; 143 | if ($model->hasMethod('getLabel')) { 144 | return 'label'; 145 | } 146 | foreach ($modelClass::getTableSchema()->getColumnNames() as $name) { 147 | switch (strtolower($name)) { 148 | case 'name': 149 | case 'title': 150 | case 'name_id': 151 | case 'default_title': 152 | case 'default_name': 153 | return $name; 154 | break; 155 | default: 156 | continue; 157 | break; 158 | } 159 | 160 | } 161 | 162 | return $modelClass::primaryKey()[0]; 163 | } 164 | 165 | public function getModelByTableName($name) 166 | { 167 | return Inflector::id2camel(str_replace($this->tablePrefix, '', $name), '_'); 168 | } 169 | 170 | /** 171 | * @return string the action view file path 172 | */ 173 | public function getViewPath() 174 | { 175 | if ($this->viewPath !== null) { 176 | return \Yii::getAlias($this->viewPath) . '/' . $this->getControllerID(); 177 | } else { 178 | return parent::getViewPath(); 179 | } 180 | 181 | } 182 | 183 | /** 184 | * @return string the controller ID (without the module ID prefix) 185 | */ 186 | public function getControllerID() 187 | { 188 | $pos = strrpos($this->controllerClass, '\\'); 189 | $class = substr(substr($this->controllerClass, $pos + 1), 0, -10); 190 | 191 | return Inflector::camel2id($class, '-', true); 192 | } 193 | 194 | 195 | /** 196 | * @param $column 197 | * 198 | * @return null|\yii\db\ActiveQuery 199 | */ 200 | public function getRelationByColumn($model, $column) 201 | { 202 | $relations = $this->getModelRelations($model); 203 | foreach ($relations AS $relation) { 204 | // TODO: check multiple link(s) 205 | if (reset($relation->link) == $column->name) { 206 | return $relation; 207 | } 208 | } 209 | return null; 210 | } 211 | 212 | /** 213 | * @todo docs 214 | * @return array 215 | */ 216 | public function getModelRelations($modelClass, $types = ['belongs_to', 'many_many', 'has_many', 'has_one', 'pivot']) 217 | { 218 | $reflector = new \ReflectionClass($modelClass); 219 | $model = new $modelClass; 220 | $stack = []; 221 | foreach ($reflector->getMethods() AS $method) { 222 | // look for getters 223 | if (substr($method->name, 0, 3) !== 'get') { 224 | continue; 225 | } 226 | // skip class specific getters 227 | $skipMethods = [ 228 | 'getRelation', 229 | 'getBehavior', 230 | 'getFirstError', 231 | 'getAttribute', 232 | 'getAttributeLabel', 233 | 'getOldAttribute' 234 | ]; 235 | if (in_array($method->name, $skipMethods)) { 236 | continue; 237 | 238 | } 239 | // check for relation 240 | $relation = call_user_func(array($model, $method->name)); 241 | if ($relation instanceof Yii\db\ActiveQuery) { 242 | #var_dump($relation->primaryModel->primaryKey); 243 | if ($relation->multiple === false) { 244 | $relationType = 'belongs_to'; 245 | } elseif ($this->isPivotRelation($relation)) { # TODO: detecttion 246 | $relationType = 'pivot'; 247 | } else { 248 | $relationType = 'has_many'; 249 | } 250 | 251 | if (in_array($relationType, $types)) { 252 | $stack[substr($method->name, 3)] = $relation; 253 | } 254 | } 255 | } 256 | return $stack; 257 | } 258 | 259 | 260 | public function createRelationRoute($relation, $action) 261 | { 262 | $route = $this->pathPrefix . Inflector::camel2id( 263 | $this->generateRelationTo($relation), 264 | '-', 265 | true 266 | ) . "/" . $action; 267 | return $route; 268 | } 269 | 270 | public function generateRelationTo($relation) 271 | { 272 | $class = new \ReflectionClass($relation->modelClass); 273 | $route = Inflector::variablize($class->getShortName()); 274 | return $route; 275 | } 276 | 277 | /** 278 | * Generates code for active field by using the provider queue 279 | * 280 | * @param string $attribute 281 | * 282 | * @return string 283 | */ 284 | public function activeField(ColumnSchema $column, $model = null) 285 | { 286 | Yii::trace("Rendering activeField for '{$column->name}'", __METHOD__); 287 | if ($model === null) { 288 | $model = $this->modelClass; 289 | } 290 | $code = $this->callProviderQueue(__FUNCTION__, $column, $model); 291 | if ($code !== null) { 292 | return $code; 293 | } else { 294 | return parent::generateActiveField($column->name); 295 | }; 296 | } 297 | 298 | public function prependActiveField(ColumnSchema $column, $model = null) 299 | { 300 | Yii::trace("Rendering activeField for '{$column->name}'", __METHOD__); 301 | if ($model === null) { 302 | $model = $this->modelClass; 303 | } 304 | return $this->callProviderQueue(__FUNCTION__, $column, $model); 305 | } 306 | 307 | public function appendActiveField(ColumnSchema $column, $model = null) 308 | { 309 | Yii::trace("Rendering activeField for '{$column->name}'", __METHOD__); 310 | if ($model === null) { 311 | $model = $this->modelClass; 312 | } 313 | return $this->callProviderQueue(__FUNCTION__, $column, $model); 314 | } 315 | 316 | public function columnFormat(ColumnSchema $column, $model = null) 317 | { 318 | Yii::trace("Rendering columnFormat for '{$column->name}'", __METHOD__); 319 | if ($model === null) { 320 | $model = $this->modelClass; 321 | } 322 | $code = $this->callProviderQueue(__FUNCTION__, $column, $model); 323 | if ($code !== null) { 324 | return $code; 325 | } else { 326 | return $this->shorthandAttributeFormat($column); 327 | }; 328 | } 329 | 330 | 331 | public function attributeFormat(ColumnSchema $column, $model = null) 332 | { 333 | Yii::trace("Rendering attributeFormat for '{$column->name}'", __METHOD__); 334 | if ($model === null) { 335 | $model = $this->modelClass; 336 | } 337 | $code = $this->callProviderQueue(__FUNCTION__, $column, $model); 338 | if ($code !== null) { 339 | return $code; 340 | } 341 | return $this->shorthandAttributeFormat($column); 342 | // don't call parent anymore 343 | } 344 | 345 | public function relationGrid($attribute) 346 | { 347 | Yii::trace("Rendering relationGrid", __METHOD__); 348 | return $this->callProviderQueue(__FUNCTION__, $attribute); 349 | } 350 | 351 | public function generateActionParams() 352 | { 353 | /* @var $class ActiveRecord */ 354 | $class = $this->modelClass; 355 | $pks = $class::primaryKey(); 356 | if (count($pks) === 1) { 357 | return '$'.$pks[0]; // fix for non-id columns 358 | } else { 359 | return '$' . implode(', $', $pks); 360 | } 361 | } 362 | 363 | /** 364 | * Generates URL parameters 365 | * @return string 366 | */ 367 | public function generateUrlParams() 368 | { 369 | /* @var $class ActiveRecord */ 370 | $class = $this->modelClass; 371 | $pks = $class::primaryKey(); 372 | if (count($pks) === 1) { 373 | if (is_subclass_of($class, 'yii\mongodb\ActiveRecord')) { 374 | return "'id' => (string)\$model->{$pks[0]}"; 375 | } else { 376 | return "'{$pks[0]}' => \$model->{$pks[0]}"; 377 | } 378 | } else { 379 | $params = []; 380 | foreach ($pks as $pk) { 381 | if (is_subclass_of($class, 'yii\mongodb\ActiveRecord')) { 382 | $params[] = "'$pk' => (string)\$model->$pk"; 383 | } else { 384 | $params[] = "'$pk' => \$model->$pk"; 385 | } 386 | } 387 | 388 | return implode(', ', $params); 389 | } 390 | } 391 | 392 | public function isPivotRelation(ActiveQuery $relation) 393 | { 394 | $model = new $relation->modelClass; 395 | $table = $model->tableSchema; 396 | $pk = $table->primaryKey; 397 | if (count($pk) !== 2) { 398 | return false; 399 | } 400 | $fks = []; 401 | foreach ($table->foreignKeys as $refs) { 402 | if (count($refs) === 2) { 403 | if (isset($refs[$pk[0]])) { 404 | $fks[$pk[0]] = [$refs[0], $refs[$pk[0]]]; 405 | } elseif (isset($refs[$pk[1]])) { 406 | $fks[$pk[1]] = [$refs[0], $refs[$pk[1]]]; 407 | } 408 | } 409 | } 410 | if (count($fks) === 2 && $fks[$pk[0]][0] !== $fks[$pk[1]][0]) { 411 | return $fks; 412 | } else { 413 | return false; 414 | } 415 | } 416 | 417 | private function callProviderQueue($func, $args) 418 | { 419 | $this->initializeProviders(); // TODO: should be done on init, but providerList is empty 420 | //var_dump($this->_p);exit; 421 | $args = func_get_args(); 422 | unset($args[0]); 423 | // walk through providers 424 | foreach ($this->_p AS $obj) { 425 | if (method_exists($obj, $func)) { 426 | $c = call_user_func_array(array(&$obj, $func), $args); 427 | // until a provider returns not null 428 | if ($c !== null) { 429 | if (is_object($args)) { 430 | $argsString = get_class($args); 431 | } elseif (is_array($args)) { 432 | $argsString = Json::encode($args); 433 | } else { 434 | $argsString = $args; 435 | } 436 | $msg = 'Using provider ' . get_class($obj) . '::' . $func . ' ' . $argsString; 437 | Yii::trace($msg, __METHOD__); 438 | return $c; 439 | } 440 | } 441 | } 442 | } 443 | 444 | private function shorthandAttributeFormat($attribute) 445 | { 446 | if ($attribute->phpType === 'boolean') { 447 | $format = 'boolean'; 448 | } elseif ($attribute->type === 'text') { 449 | $format = 'ntext'; 450 | } elseif (stripos($attribute->name, 'time') !== false && $attribute->phpType === 'integer') { 451 | $format = 'datetime'; 452 | } elseif (stripos($attribute->name, 'email') !== false) { 453 | $format = 'email'; 454 | } elseif (stripos($attribute->name, 'url') !== false) { 455 | $format = 'url'; 456 | } else { 457 | $format = 'text'; 458 | } 459 | 460 | return "\t\t\t'" . $attribute->name . ($format === 'text' ? "" : ":" . $format) . "'"; 461 | } 462 | } 463 | --------------------------------------------------------------------------------