diff --git a/.gitignore b/.gitignore
index f0eb58a3..eec23d5b 100644
--- a/.gitignore
+++ b/.gitignore
@@ -11,4 +11,3 @@ Homestead.json
Homestead.yaml
npm-debug.log
yarn-error.log
-/app/Classes/CodeGenerator
\ No newline at end of file
diff --git a/app/Classes/CodeGenerator/Commands/BaseCommand.php b/app/Classes/CodeGenerator/Commands/BaseCommand.php
new file mode 100644
index 00000000..e1f4c80c
--- /dev/null
+++ b/app/Classes/CodeGenerator/Commands/BaseCommand.php
@@ -0,0 +1,277 @@
+composer = app()['composer'];
+ }
+
+ public function handle()
+ {
+ $this->commandData->modelName = $this->argument('model');
+
+ $this->commandData->initCommandData();
+ $this->commandData->getFields();
+ }
+
+ public function generateCommonItems()
+ {
+ if (!$this->commandData->getOption('fromTable')) {
+ $migrationGenerator = new MigrationGenerator($this->commandData);
+ $migrationGenerator->generate();
+ }
+
+ $modelGenerator = new ModelGenerator($this->commandData);
+ $modelGenerator->generate();
+
+ $factoryGenerator = new FactoryGenerator($this->commandData);
+ $factoryGenerator->generate();
+
+ $seederGenerator = new SeederGenerator($this->commandData);
+ $seederGenerator->generate();
+
+ if($this->confirm("\nDo you want to include this seeder to the main seeder? [y|N]", false)){
+ $seederGenerator->updateMainSeeder();
+ }
+
+ }
+
+ public function generateMicroItems()
+ {
+ $dataTransferObjectGenerator = new DataTransferObjectGenerator($this->commandData);
+ $dataTransferObjectGenerator->generate();
+
+ $serviceGenerator = new ServicesGenerator($this->commandData);
+ $serviceGenerator->generate();
+ }
+
+ public function generateScaffoldItems()
+ {
+
+ $resourceGenerator = new ResourceGenerator($this->commandData);
+ $resourceGenerator->generate();
+
+ $validatorsGenerator = new ValidatorsGenerator($this->commandData);
+ $validatorsGenerator->generate();
+
+ $rulesGenerator = new RulesGenerator($this->commandData);
+ $rulesGenerator->generate();
+
+ if (!$this->isSkip('controllers') and !$this->isSkip('scaffold_controller')) {
+ $controllerLogicGenerator = new ControllerLogicGenerator($this->commandData);
+ $controllerLogicGenerator->generate();
+
+ $controllerGenerator = new ControllersGenerator($this->commandData);
+ $controllerGenerator->generate();
+
+ }
+
+ $routesGenerator = new RoutesGenerator($this->commandData);
+ $routesGenerator->generate();
+
+// $webRouteGenerator = new WebRouteGenerator($this->commandData);
+// $webRouteGenerator->generate();
+ }
+
+ public function generateViews(){
+ $viewGenerator = new ViewsGenerator($this->commandData);
+ $viewGenerator->generate();
+
+ $vueGenerator = new vueGenerator($this->commandData);
+ $vueGenerator->generate();
+
+ if($this->confirm("\nDo you want to compile your vue files? [y|N]", false)){
+ shell_exec('yarn dev');
+ }
+ }
+
+ public function performPostActions($runMigration = false)
+ {
+ if ($this->commandData->getOption('save')) {
+ $this->saveSchemaFile();
+ }
+
+ if ($runMigration) {
+ if ($this->commandData->getOption('forceMigrate')) {
+ $this->runMigration();
+ } elseif (!$this->commandData->getOption('fromTable') and !$this->isSkip('migration')) {
+ $requestFromConsole = (php_sapi_name() == 'cli') ? true : false;
+ if ($this->commandData->getOption('jsonFromGUI') && $requestFromConsole) {
+ $this->runMigration();
+ } elseif ($requestFromConsole && $this->confirm("\nDo you want to migrate database? [y|N]", false)) {
+ $this->runMigration();
+ }
+ }
+ }
+
+ if (!$this->isSkip('dump-autoload')) {
+ $this->info('Generating autoload files');
+ $this->composer->dumpOptimized();
+ }
+ }
+
+ public function runMigration()
+ {
+ $migrationPath = database_path('migrations/');
+ $path = Str::after($migrationPath, base_path()); // get path after base_path
+ $this->call('migrate', ['--path' => $path, '--force' => true]);
+
+ return true;
+ }
+
+ public function isSkip($skip)
+ {
+ if ($this->commandData->getOption('skip')) {
+ return in_array($skip, (array) $this->commandData->getOption('skip'));
+ }
+
+ return false;
+ }
+
+ public function performPostActionsWithMigration()
+ {
+ $this->performPostActions(true);
+ }
+
+ private function saveSchemaFile()
+ {
+ $fileFields = [];
+
+ foreach ($this->commandData->fields as $field) {
+ $fileFields[] = [
+ 'name' => $field->name,
+ 'dbType' => $field->dbInput,
+ 'htmlType' => $field->htmlInput,
+ 'validations' => $field->validations,
+ 'searchable' => $field->isSearchable,
+ 'fillable' => $field->isFillable,
+ 'primary' => $field->isPrimary,
+ 'inForm' => $field->inForm,
+ 'inIndex' => $field->inIndex,
+ 'inView' => $field->inView,
+ ];
+ }
+
+ foreach ($this->commandData->relations as $relation) {
+ $fileFields[] = [
+ 'type' => 'relation',
+ 'relation' => $relation->type.','.implode(',', $relation->inputs),
+ ];
+ }
+
+ $path = app_path('Classes/CodeGenerator/Schemas/');
+
+ $fileName = $this->commandData->modelName.'.json';
+
+ if (file_exists($path.$fileName) && !$this->confirmOverwrite($fileName)) {
+ return;
+ }
+ FileUtil::createFile($path, $fileName, json_encode($fileFields, JSON_PRETTY_PRINT));
+ $this->commandData->commandComment("\nSchema File saved: ");
+ $this->commandData->commandInfo($fileName);
+ }
+
+ /**
+ * @param $fileName
+ * @param string $prompt
+ *
+ * @return bool
+ */
+ protected function confirmOverwrite($fileName, $prompt = '')
+ {
+ $prompt = (empty($prompt))
+ ? $fileName.' already exists. Do you want to overwrite it? [y|N]'
+ : $prompt;
+
+ return $this->confirm($prompt, false);
+ }
+
+
+ /**
+ * Get the console command options.
+ *
+ * @return array
+ */
+ public function getOptions()
+ {
+ return [
+ ['fieldsFile', null, InputOption::VALUE_REQUIRED, 'Fields input as json file'],
+ ['jsonFromGUI', null, InputOption::VALUE_REQUIRED, 'Direct Json string while using GUI interface'],
+ ['plural', null, InputOption::VALUE_REQUIRED, 'Plural Model name'],
+ ['tableName', null, InputOption::VALUE_REQUIRED, 'Table Name'],
+ ['fromTable', null, InputOption::VALUE_NONE, 'Generate from existing table'],
+ ['ignoreFields', null, InputOption::VALUE_REQUIRED, 'Ignore fields while generating from table'],
+ ['save', null, InputOption::VALUE_NONE, 'Save model schema to file'],
+ ['primary', null, InputOption::VALUE_REQUIRED, 'Custom primary key'],
+ ['prefix', null, InputOption::VALUE_REQUIRED, 'Prefix for all files'],
+ ['paginate', null, InputOption::VALUE_REQUIRED, 'Pagination for index.blade.php'],
+ ['skip', null, InputOption::VALUE_REQUIRED, 'Skip Specific Items to Generate (migration,model,controllers,api_controller,scaffold_controller,repository,requests,api_requests,scaffold_requests,routes,api_routes,scaffold_routes,views,tests,menu,dump-autoload)'],
+ ['datatables', null, InputOption::VALUE_REQUIRED, 'Override datatables settings'],
+ ['views', null, InputOption::VALUE_REQUIRED, 'Specify only the views you want generated: index,create,edit,show'],
+ ['relations', null, InputOption::VALUE_NONE, 'Specify if you want to pass relationships for fields'],
+ ['softDelete', null, InputOption::VALUE_NONE, 'Soft Delete Option'],
+ ['forceMigrate', null, InputOption::VALUE_NONE, 'Specify if you want to run migration or not'],
+ ['factory', null, InputOption::VALUE_NONE, 'To generate factory'],
+ ['seeder', null, InputOption::VALUE_NONE, 'To generate seeder'],
+ ['localized', null, InputOption::VALUE_NONE, 'Localize files.'],
+ ['repositoryPattern', null, InputOption::VALUE_REQUIRED, 'Repository Pattern'],
+ ['connection', null, InputOption::VALUE_REQUIRED, 'Specify connection name'],
+ ];
+ }
+
+ /**
+ * Get the console command arguments.
+ *
+ * @return array
+ */
+ protected function getArguments()
+ {
+ return [
+ ['model', InputArgument::REQUIRED, 'Singular Model name'],
+ ];
+ }
+
+}
\ No newline at end of file
diff --git a/app/Classes/CodeGenerator/Commands/RollbackGeneratorCommand.php b/app/Classes/CodeGenerator/Commands/RollbackGeneratorCommand.php
new file mode 100644
index 00000000..dd7552d1
--- /dev/null
+++ b/app/Classes/CodeGenerator/Commands/RollbackGeneratorCommand.php
@@ -0,0 +1,187 @@
+composer = app()['composer'];
+ }
+
+ /**
+ * Execute the command.
+ *
+ * @return void
+ */
+ public function handle()
+ {
+ if (!in_array($this->argument('type'), [
+ 'scaffold', 'micro'
+ ])) {
+ $this->error('invalid rollback type');
+ }
+
+ $this->commandData = new CommandData($this, $this->argument('type'));
+ $this->commandData->config->mName = $this->commandData->modelName = $this->argument('model');
+
+ $this->commandData->config->init($this->commandData, ['tableName', 'prefix', 'plural', 'views']);
+
+ $this->rollbackCommon();
+ $this->rollbackMicros();
+
+ if($this->commandData->commandType === 'scaffold'){
+
+ $this->rollbackScaffold();
+ $this->rollbackViews();
+
+ }
+
+
+ $this->info('Generating autoload files');
+ $this->composer->dumpOptimized();
+ }
+
+ private function rollbackCommon(){
+ $migrationGenerator = new MigrationGenerator($this->commandData);
+ $migrationGenerator->rollback();
+
+ $modelGenerator = new ModelGenerator($this->commandData);
+ $modelGenerator->rollback();
+
+ $factoryGenerator = new FactoryGenerator($this->commandData);
+ $factoryGenerator->rollback();
+
+ $seederGenerator = new SeederGenerator($this->commandData);
+ $seederGenerator->rollback();
+ }
+
+ private function rollbackMicros(){
+
+ $dataTransferObjectGenerator = new DataTransferObjectGenerator($this->commandData);
+ $dataTransferObjectGenerator->rollback();
+
+ $serviceGenerator = new ServicesGenerator($this->commandData);
+ $serviceGenerator->rollback();
+
+ }
+
+ private function rollbackScaffold(){
+ $resourceGenerator = new ResourceGenerator($this->commandData);
+ $resourceGenerator->rollback();
+
+ $validatorsGenerator = new ValidatorsGenerator($this->commandData);
+ $validatorsGenerator->rollback();
+
+ $rulesGenerator = new RulesGenerator($this->commandData);
+ $rulesGenerator->rollback();
+
+ File::deleteDirectories(app_path('Classes/Modules/'. str::pluralStudly($this->commandData->modelName).'/Standards/'));
+
+ $routesGenerator = new RoutesGenerator($this->commandData);
+ $routesGenerator->rollback();
+
+ $webRouteGenerator = new WebRouteGenerator($this->commandData);
+ $webRouteGenerator->rollback();
+
+ $controllerGenerator = new ControllersGenerator($this->commandData);
+ $controllerGenerator->rollback();
+
+ $controllerLogicGenerator = new ControllerLogicGenerator($this->commandData);
+ $controllerLogicGenerator->rollback();
+
+ File::deleteDirectories(app_path('Classes/Modules/'. str::pluralStudly($this->commandData->modelName).'/'));
+
+ }
+
+ private function rollbackViews(){
+ $viewGenerator = new ViewsGenerator($this->commandData);
+ $viewGenerator->rollback();
+
+ $vueGenerator = new vueGenerator($this->commandData);
+ $vueGenerator->rollback();
+ }
+
+
+
+ /**
+ * Get the console command options.
+ *
+ * @return array
+ */
+ public function getOptions()
+ {
+ return [
+ ['tableName', null, InputOption::VALUE_REQUIRED, 'Table Name'],
+ ['prefix', null, InputOption::VALUE_REQUIRED, 'Prefix for all files'],
+ ['plural', null, InputOption::VALUE_REQUIRED, 'Plural Model name'],
+ ['views', null, InputOption::VALUE_REQUIRED, 'Views to rollback'],
+ ];
+ }
+
+ /**
+ * Get the console command arguments.
+ *
+ * @return array
+ */
+ protected function getArguments()
+ {
+ return [
+ ['model', InputArgument::REQUIRED, 'Singular Model name'],
+ ['type', InputArgument::REQUIRED, 'Rollback type: (micro / scaffold )'],
+ ];
+ }
+
+}
diff --git a/app/Classes/CodeGenerator/Commands/Scaffold/MicroGeneratorCommand.php b/app/Classes/CodeGenerator/Commands/Scaffold/MicroGeneratorCommand.php
new file mode 100644
index 00000000..2964a6b1
--- /dev/null
+++ b/app/Classes/CodeGenerator/Commands/Scaffold/MicroGeneratorCommand.php
@@ -0,0 +1,91 @@
+commandData = new CommandData($this, 'micro');
+ }
+
+ /**
+ * Execute the command.
+ *
+ * @return void
+ */
+ public function handle()
+ {
+ parent::handle();
+
+ if ($this->checkIsThereAnyDataToGenerate()) {
+
+ $this->generateCommonItems();
+
+ $this->generateMicroItems();
+
+ $this->performPostActionsWithMigration();
+
+ } else {
+
+ $this->commandData->commandInfo('There are not enough input fields for scaffold generation.');
+
+ }
+ }
+
+
+ /**
+ * Get the console command options.
+ *
+ * @return array
+ */
+ public function getOptions()
+ {
+ return array_merge(parent::getOptions(), []);
+ }
+
+ /**
+ * Get the console command arguments.
+ *
+ * @return array
+ */
+ protected function getArguments()
+ {
+ return array_merge(parent::getArguments(), []);
+ }
+
+ /**
+ * Check if there is anything to generate.
+ *
+ * @return bool
+ */
+ protected function checkIsThereAnyDataToGenerate()
+ {
+ if (count($this->commandData->fields) > 1) {
+ return true;
+ }
+ }
+}
diff --git a/app/Classes/CodeGenerator/Commands/Scaffold/ScaffoldGeneratorCommand.php b/app/Classes/CodeGenerator/Commands/Scaffold/ScaffoldGeneratorCommand.php
new file mode 100644
index 00000000..dc370e61
--- /dev/null
+++ b/app/Classes/CodeGenerator/Commands/Scaffold/ScaffoldGeneratorCommand.php
@@ -0,0 +1,96 @@
+commandData = new CommandData($this, 'scaffold');
+ }
+
+ /**
+ * Execute the command.
+ *
+ * @return void
+ */
+ public function handle()
+ {
+ parent::handle();
+
+ if ($this->checkIsThereAnyDataToGenerate()) {
+
+
+ $this->generateCommonItems();
+
+ $this->generateMicroItems();
+
+ $this->generateScaffoldItems();
+
+// $this->generateViews();
+
+ $this->performPostActionsWithMigration();
+
+ } else {
+
+ $this->commandData->commandInfo('There are not enough input fields for scaffold generation.');
+
+ }
+ }
+
+
+ /**
+ * Get the console command options.
+ *
+ * @return array
+ */
+ public function getOptions()
+ {
+ return array_merge(parent::getOptions(), []);
+ }
+
+ /**
+ * Get the console command arguments.
+ *
+ * @return array
+ */
+ protected function getArguments()
+ {
+ return array_merge(parent::getArguments(), []);
+ }
+
+ /**
+ * Check if there is anything to generate.
+ *
+ * @return bool
+ */
+ protected function checkIsThereAnyDataToGenerate()
+ {
+ if (count($this->commandData->fields) > 1) {
+ return true;
+ }
+ }
+}
diff --git a/app/Classes/CodeGenerator/Common/CommandData.php b/app/Classes/CodeGenerator/Common/CommandData.php
new file mode 100644
index 00000000..a58cb978
--- /dev/null
+++ b/app/Classes/CodeGenerator/Common/CommandData.php
@@ -0,0 +1,294 @@
+templateManager;
+ }
+
+
+ /**
+ * @param Command $commandObj
+ * @param $commandType
+ * @param TemplatesManager $templatesManager
+ */
+ public function __construct(Command $commandObj, $commandType, TemplatesManager $templatesManager = null)
+ {
+ $this->commandObj = $commandObj;
+
+ if (is_null($templatesManager)) {
+ $this->templateManager = app(TemplatesManager::class);
+ } else {
+ $this->templateManager = $templatesManager;
+ }
+
+ $this->commandType = $commandType;
+
+ $this->fieldNamesMapping = [
+ '$FIELD_NAME_TITLE$' => 'fieldTitle',
+ '$FIELD_NAME$' => 'name',
+ ];
+
+ $this->config = new GeneratorConfig();
+ }
+
+ public function commandError($error)
+ {
+ $this->commandObj->error($error);
+ }
+
+ public function commandComment($message)
+ {
+ $this->commandObj->comment($message);
+ }
+
+ public function commandWarn($warning)
+ {
+ $this->commandObj->warn($warning);
+ }
+
+ public function commandInfo($message)
+ {
+ $this->commandObj->info($message);
+ }
+
+ public function initCommandData()
+ {
+ $this->config->init($this);
+ }
+
+ public function getOption($option)
+ {
+ return $this->config->getOption($option);
+ }
+
+ public function getAddOn($option)
+ {
+ return $this->config->getAddOn($option);
+ }
+
+ public function setOption($option, $value)
+ {
+ $this->config->setOption($option, $value);
+ }
+
+ public function addDynamicVariable($name, $val)
+ {
+ $this->dynamicVars[$name] = $val;
+ }
+
+ public function getFields()
+ {
+ $this->fields = [];
+
+ if ($this->getOption('fieldsFile') or $this->getOption('jsonFromGUI')) {
+ $this->getInputFromFileOrJson();
+ } elseif ($this->getOption('fromTable')) {
+ $this->getInputFromTable();
+ } else {
+ $this->getInputFromConsole();
+ }
+ }
+
+ private function getInputFromConsole()
+ {
+ $this->commandInfo('Specify fields for the model (skip id & timestamp fields, we will add it automatically)');
+ $this->commandInfo('Read docs carefully to specify field inputs)');
+ $this->commandInfo('Enter "exit" to finish');
+
+ $this->addPrimaryKey();
+
+ while (true) {
+ $fieldInputStr = $this->commandObj->ask('Field: (name db_type html_type options)', '');
+
+ if (empty($fieldInputStr) || $fieldInputStr == false || $fieldInputStr == 'exit') {
+ break;
+ }
+
+ if (!GeneratorFieldsInputUtil::validateFieldInput($fieldInputStr)) {
+ $this->commandError('Invalid Input. Try again');
+ continue;
+ }
+
+ $validations = $this->commandObj->ask('Enter validations: ', false);
+ $validations = ($validations == false) ? '' : $validations;
+
+ if ($this->getOption('relations')) {
+ $relation = $this->commandObj->ask('Enter relationship (Leave Blank to skip):', false);
+ } else {
+ $relation = '';
+ }
+
+ $this->fields[] = GeneratorFieldsInputUtil::processFieldInput(
+ $fieldInputStr,
+ $validations
+ );
+
+ if (!empty($relation)) {
+ $this->relations[] = GeneratorFieldRelation::parseRelation($relation);
+ }
+ }
+
+ $this->addTimestamps();
+ }
+
+ private function addPrimaryKey()
+ {
+ $primaryKey = new GeneratorField();
+ if ($this->getOption('primary')) {
+ $primaryKey->name = $this->getOption('primary');
+ } else {
+ $primaryKey->name = 'id';
+ }
+ $primaryKey->parseDBType('increments');
+ $primaryKey->parseOptions('s,f,p,if,ii');
+
+ $this->fields[] = $primaryKey;
+ }
+
+ private function addTimestamps()
+ {
+ $createdAt = new GeneratorField();
+ $createdAt->name = 'created_at';
+ $createdAt->parseDBType('timestamp');
+ $createdAt->parseOptions('s,f,if,ii');
+ $this->fields[] = $createdAt;
+
+ $updatedAt = new GeneratorField();
+ $updatedAt->name = 'updated_at';
+ $updatedAt->parseDBType('timestamp');
+ $updatedAt->parseOptions('s,f,if,ii');
+ $this->fields[] = $updatedAt;
+ }
+
+ private function getInputFromFileOrJson()
+ {
+ // fieldsFile option will get high priority than json option if both options are passed
+ try {
+ if ($this->getOption('fieldsFile')) {
+ $fieldsFileValue = $this->getOption('fieldsFile');
+ if (file_exists($fieldsFileValue)) {
+ $filePath = $fieldsFileValue;
+ } elseif (file_exists(base_path($fieldsFileValue))) {
+ $filePath = base_path($fieldsFileValue);
+ } else {
+ $schemaFileDirector = app_path('Classes/CodeGenerator/Schemas/');
+ $filePath = $schemaFileDirector.$fieldsFileValue;
+ }
+
+ if (!file_exists($filePath)) {
+ $this->commandError('Fields file not found');
+ exit;
+ }
+
+ $fileContents = file_get_contents($filePath);
+ $jsonData = json_decode($fileContents, true);
+ $this->fields = [];
+ foreach ($jsonData as $field) {
+ if (isset($field['type']) && $field['relation']) {
+ $this->relations[] = GeneratorFieldRelation::parseRelation($field['relation']);
+ } else {
+ $this->fields[] = GeneratorField::parseFieldFromFile($field);
+ if (isset($field['relation'])) {
+ $this->relations[] = GeneratorFieldRelation::parseRelation($field['relation']);
+ }
+ }
+ }
+ } else {
+ $fileContents = $this->getOption('jsonFromGUI');
+ $jsonData = json_decode($fileContents, true);
+
+ // override config options from jsonFromGUI
+ $this->config->overrideOptionsFromJsonFile($jsonData);
+
+ // Manage custom table name option
+ if (isset($jsonData['tableName'])) {
+ $tableName = $jsonData['tableName'];
+ $this->config->tableName = $tableName;
+ $this->addDynamicVariable('$TABLE_NAME$', $tableName);
+ $this->addDynamicVariable('$TABLE_NAME_TITLE$', Str::studly($tableName));
+ }
+
+ // Manage migrate option
+ if (isset($jsonData['migrate']) && $jsonData['migrate'] == false) {
+ $this->config->options['skip'][] = 'migration';
+ }
+
+ foreach ($jsonData['fields'] as $field) {
+ if (isset($field['type']) && $field['relation']) {
+ $this->relations[] = GeneratorFieldRelation::parseRelation($field['relation']);
+ } else {
+ $this->fields[] = GeneratorField::parseFieldFromFile($field);
+ if (isset($field['relation'])) {
+ $this->relations[] = GeneratorFieldRelation::parseRelation($field['relation']);
+ }
+ }
+ }
+ }
+ } catch (Exception $e) {
+ $this->commandError($e->getMessage());
+ exit;
+ }
+ }
+
+ private function getInputFromTable()
+ {
+ $tableName = $this->dynamicVars['$TABLE_NAME$'];
+
+ $ignoredFields = $this->getOption('ignoreFields');
+ if (!empty($ignoredFields)) {
+ $ignoredFields = explode(',', trim($ignoredFields));
+ } else {
+ $ignoredFields = [];
+ }
+
+ $tableFieldsGenerator = new TableFieldsGenerator($tableName, $ignoredFields, $this->config->connection);
+ $tableFieldsGenerator->prepareFieldsFromTable();
+ $tableFieldsGenerator->prepareRelations();
+
+ $this->fields = $tableFieldsGenerator->fields;
+ $this->relations = $tableFieldsGenerator->relations;
+ }
+
+}
\ No newline at end of file
diff --git a/app/Classes/CodeGenerator/Common/GenerateGetters.php b/app/Classes/CodeGenerator/Common/GenerateGetters.php
new file mode 100644
index 00000000..fbf5be5a
--- /dev/null
+++ b/app/Classes/CodeGenerator/Common/GenerateGetters.php
@@ -0,0 +1,26 @@
+generatorHelpers->get_template('Micros.getter_function');
+
+ $template = str_replace('$FIELD_NAME$', str::camel($name), $template);
+ $template = str_replace('$DATA_TYPE$', $type, $template);
+ $template = str_replace('$FUNCTION_NAME$', $functionPrefix.str::studly($name), $template);
+
+ return $template;
+ }
+
+
+}
\ No newline at end of file
diff --git a/app/Classes/CodeGenerator/Common/GeneratorConfig.php b/app/Classes/CodeGenerator/Common/GeneratorConfig.php
new file mode 100644
index 00000000..6c96c118
--- /dev/null
+++ b/app/Classes/CodeGenerator/Common/GeneratorConfig.php
@@ -0,0 +1,413 @@
+mName = $commandData->modelName;
+
+ $this->prepareAddOns();
+ $this->prepareOptions($commandData);
+ $this->prepareModelNames();
+ $this->preparePrefixes();
+ $this->loadPaths();
+ $this->prepareTableName();
+ $this->preparePrimaryName();
+ $this->loadNamespaces($commandData);
+ $commandData = $this->loadDynamicVariables($commandData);
+ $this->commandData = &$commandData;
+ }
+
+ public function loadNamespaces(CommandData &$commandData)
+ {
+ $prefix = $this->prefixes['ns'];
+
+ if (!empty($prefix)) {
+ $prefix = '\\'.$prefix;
+ }
+
+ $this->nsApp = $commandData->commandObj->getLaravel()->getNamespace();
+ $this->nsApp = substr($this->nsApp, 0, strlen($this->nsApp) - 1);
+ $this->nsModel = 'App\Models';
+ $this->nsModelExtend = 'App\Models\AbstractModel';
+
+ $this->nsBaseController = 'App\Http\Controllers';
+ $this->nsController = 'App\Http\Controllers'.$prefix;
+
+ $this->nsApiTests = 'Tests\APIs';
+ $this->nsTests = 'Tests';
+ }
+
+ public function loadPaths()
+ {
+ $prefix = $this->prefixes['path'];
+
+ if (!empty($prefix)) {
+ $prefix .= '/';
+ }
+
+ $this->pathModel = app_path('Models/');
+
+ $this->pathApiRoutes = base_path('routes/api.php');
+
+ $this->pathApiTests = base_path('tests/APIs/');
+
+ $this->pathController = app_path('Http/Controllers/').$prefix;
+
+ $this->pathRoutes = base_path('routes/web.php');
+ $this->pathFactory = database_path('factories/');
+
+
+ $this->pathSeeder = database_path('seeds/');
+ $this->pathDatabaseSeeder = database_path('seeds/DatabaseSeeder.php');
+ }
+
+ public function loadDynamicVariables(CommandData &$commandData)
+ {
+ $commandData->addDynamicVariable('$NAMESPACE_APP$', $this->nsApp);
+ $commandData->addDynamicVariable('$NAMESPACE_MODEL$', $this->nsModel);
+ $commandData->addDynamicVariable('$NAMESPACE_MODEL_EXTEND$', $this->nsModelExtend);
+
+ $commandData->addDynamicVariable('$NAMESPACE_BASE_CONTROLLER$', $this->nsBaseController);
+ $commandData->addDynamicVariable('$NAMESPACE_CONTROLLER$', $this->nsController);
+
+ $commandData->addDynamicVariable('$NAMESPACE_API_TESTS$', $this->nsApiTests);
+ $commandData->addDynamicVariable('$NAMESPACE_TESTS$', $this->nsTests);
+
+ $commandData->addDynamicVariable('$TABLE_NAME$', $this->tableName);
+ $commandData->addDynamicVariable('$TABLE_NAME_TITLE$', Str::studly($this->tableName));
+ $commandData->addDynamicVariable('$PRIMARY_KEY_NAME$', $this->primaryName);
+
+ $commandData->addDynamicVariable('$MODEL_NAME$', $this->mName);
+ $commandData->addDynamicVariable('$MODEL_NAME_CAMEL$', $this->mCamel);
+ $commandData->addDynamicVariable('$MODEL_NAME_PLURAL$', $this->mPlural);
+ $commandData->addDynamicVariable('$MODEL_NAME_PLURAL_CAMEL$', $this->mCamelPlural);
+ $commandData->addDynamicVariable('$MODEL_NAME_SNAKE$', $this->mSnake);
+ $commandData->addDynamicVariable('$MODEL_NAME_PLURAL_SNAKE$', $this->mSnakePlural);
+ $commandData->addDynamicVariable('$MODEL_NAME_DASHED$', $this->mDashed);
+ $commandData->addDynamicVariable('$MODEL_NAME_PLURAL_DASHED$', $this->mDashedPlural);
+ $commandData->addDynamicVariable('$MODEL_NAME_SLASH$', $this->mSlash);
+ $commandData->addDynamicVariable('$MODEL_NAME_PLURAL_SLASH$', $this->mSlashPlural);
+ $commandData->addDynamicVariable('$MODEL_NAME_HUMAN$', $this->mHuman);
+ $commandData->addDynamicVariable('$MODEL_NAME_PLURAL_HUMAN$', $this->mHumanPlural);
+ $commandData->addDynamicVariable('$FILES$', '');
+
+ if (!empty($this->prefixes['route'])) {
+ $commandData->addDynamicVariable('$ROUTE_NAMED_PREFIX$', $this->prefixes['route'].'.');
+ $commandData->addDynamicVariable('$ROUTE_PREFIX$', str_replace('.', '/', $this->prefixes['route']).'/');
+ $commandData->addDynamicVariable('$RAW_ROUTE_PREFIX$', $this->prefixes['route']);
+ } else {
+ $commandData->addDynamicVariable('$ROUTE_PREFIX$', '');
+ $commandData->addDynamicVariable('$ROUTE_NAMED_PREFIX$', '');
+ }
+
+ if (!empty($this->prefixes['ns'])) {
+ $commandData->addDynamicVariable('$PATH_PREFIX$', $this->prefixes['ns'].'\\');
+ } else {
+ $commandData->addDynamicVariable('$PATH_PREFIX$', '');
+ }
+
+ if (!empty($this->prefixes['view'])) {
+ $commandData->addDynamicVariable('$VIEW_PREFIX$', str_replace('/', '.', $this->prefixes['view']).'.');
+ } else {
+ $commandData->addDynamicVariable('$VIEW_PREFIX$', '');
+ }
+
+ if (!empty($this->prefixes['public'])) {
+ $commandData->addDynamicVariable('$PUBLIC_PREFIX$', $this->prefixes['public']);
+ } else {
+ $commandData->addDynamicVariable('$PUBLIC_PREFIX$', '');
+ }
+
+ $commandData->addDynamicVariable(
+ '$API_PREFIX$',
+ 'api'
+ );
+
+ $commandData->addDynamicVariable(
+ '$API_VERSION$',
+ 'v1'
+ );
+
+ $commandData->addDynamicVariable('$SEARCHABLE$', '');
+
+ return $commandData;
+ }
+
+ public function prepareTableName()
+ {
+ if ($this->getOption('tableName')) {
+ $this->tableName = $this->getOption('tableName');
+ } else {
+ $this->tableName = $this->mSnakePlural;
+ }
+ }
+
+ public function preparePrimaryName()
+ {
+ if ($this->getOption('primary')) {
+ $this->primaryName = $this->getOption('primary');
+ } else {
+ $this->primaryName = 'id';
+ }
+ }
+
+ public function prepareModelNames()
+ {
+ if ($this->getOption('plural')) {
+ $this->mPlural = $this->getOption('plural');
+ } else {
+ $this->mPlural = Str::plural($this->mName);
+ }
+ $this->mCamel = Str::camel($this->mName);
+ $this->mCamelPlural = Str::camel($this->mPlural);
+ $this->mSnake = Str::snake($this->mName);
+ $this->mSnakePlural = Str::snake($this->mPlural);
+ $this->mDashed = str_replace('_', '-', Str::snake($this->mSnake));
+ $this->mDashedPlural = str_replace('_', '-', Str::snake($this->mSnakePlural));
+ $this->mSlash = str_replace('_', '/', Str::snake($this->mSnake));
+ $this->mSlashPlural = str_replace('_', '/', Str::snake($this->mSnakePlural));
+ $this->mHuman = Str::title(str_replace('_', ' ', Str::snake($this->mSnake)));
+ $this->mHumanPlural = Str::title(str_replace('_', ' ', Str::snake($this->mSnakePlural)));
+ }
+
+ public function prepareOptions(CommandData &$commandData)
+ {
+ foreach (self::$availableOptions as $option) {
+ $this->options[$option] = $commandData->commandObj->option($option);
+ }
+
+ if (isset($options['fromTable']) and $this->options['fromTable']) {
+ if (!$this->options['tableName']) {
+ $commandData->commandError('tableName required with fromTable option.');
+ exit;
+ }
+ }
+
+ $this->options['softDelete'] = true;
+ if (!empty($this->options['skip'])) {
+ $this->options['skip'] = array_map('trim', explode(',', $this->options['skip']));
+ }
+ }
+
+ public function preparePrefixes()
+ {
+ $this->prefixes['route'] = explode('/', '');
+ $this->prefixes['path'] = explode('/', '');
+ $this->prefixes['view'] = explode('.', '');
+ $this->prefixes['public'] = explode('/', '');
+
+ if ($this->getOption('prefix')) {
+ $multiplePrefixes = explode('/', $this->getOption('prefix'));
+
+ $this->prefixes['route'] = array_merge($this->prefixes['route'], $multiplePrefixes);
+ $this->prefixes['path'] = array_merge($this->prefixes['path'], $multiplePrefixes);
+ $this->prefixes['view'] = array_merge($this->prefixes['view'], $multiplePrefixes);
+ $this->prefixes['public'] = array_merge($this->prefixes['public'], $multiplePrefixes);
+ }
+
+ $this->prefixes['route'] = array_diff($this->prefixes['route'], ['']);
+ $this->prefixes['path'] = array_diff($this->prefixes['path'], ['']);
+ $this->prefixes['view'] = array_diff($this->prefixes['view'], ['']);
+ $this->prefixes['public'] = array_diff($this->prefixes['public'], ['']);
+
+ $routePrefix = '';
+
+ foreach ($this->prefixes['route'] as $singlePrefix) {
+ $routePrefix .= Str::camel($singlePrefix).'.';
+ }
+
+ if (!empty($routePrefix)) {
+ $routePrefix = substr($routePrefix, 0, strlen($routePrefix) - 1);
+ }
+
+ $this->prefixes['route'] = $routePrefix;
+
+ $nsPrefix = '';
+
+ foreach ($this->prefixes['path'] as $singlePrefix) {
+ $nsPrefix .= Str::title($singlePrefix).'\\';
+ }
+
+ if (!empty($nsPrefix)) {
+ $nsPrefix = substr($nsPrefix, 0, strlen($nsPrefix) - 1);
+ }
+
+ $this->prefixes['ns'] = $nsPrefix;
+
+ $pathPrefix = '';
+
+ foreach ($this->prefixes['path'] as $singlePrefix) {
+ $pathPrefix .= Str::title($singlePrefix).'/';
+ }
+
+ if (!empty($pathPrefix)) {
+ $pathPrefix = substr($pathPrefix, 0, strlen($pathPrefix) - 1);
+ }
+
+ $this->prefixes['path'] = $pathPrefix;
+
+ $viewPrefix = '';
+
+ foreach ($this->prefixes['view'] as $singlePrefix) {
+ $viewPrefix .= Str::camel($singlePrefix).'/';
+ }
+
+ if (!empty($viewPrefix)) {
+ $viewPrefix = substr($viewPrefix, 0, strlen($viewPrefix) - 1);
+ }
+
+ $this->prefixes['view'] = $viewPrefix;
+
+ $publicPrefix = '';
+
+ foreach ($this->prefixes['public'] as $singlePrefix) {
+ $publicPrefix .= Str::camel($singlePrefix).'/';
+ }
+
+ if (!empty($publicPrefix)) {
+ $publicPrefix = substr($publicPrefix, 0, strlen($publicPrefix) - 1);
+ }
+
+ $this->prefixes['public'] = $publicPrefix;
+ }
+
+ public function overrideOptionsFromJsonFile($jsonData)
+ {
+ $options = self::$availableOptions;
+
+ foreach ($options as $option) {
+ if (isset($jsonData['options'][$option])) {
+ $this->setOption($option, $jsonData['options'][$option]);
+ }
+ }
+
+ // prepare prefixes than reload namespaces, paths and dynamic variables
+ if (!empty($this->getOption('prefix'))) {
+ $this->preparePrefixes();
+ $this->loadPaths();
+ $this->loadNamespaces($this->commandData);
+ $this->loadDynamicVariables($this->commandData);
+ }
+ }
+
+ public function getOption($option)
+ {
+ if (isset($this->options[$option])) {
+ return $this->options[$option];
+ }
+
+ return false;
+ }
+
+ public function getAddOn($addOn)
+ {
+ if (isset($this->addOns[$addOn])) {
+ return $this->addOns[$addOn];
+ }
+
+ return false;
+ }
+
+ public function setOption($option, $value)
+ {
+ $this->options[$option] = $value;
+ }
+
+ public function prepareAddOns()
+ {
+ $this->addOns['tests'] = false;
+ }
+
+ public function excludeFields()
+ {
+ return self::$excludeFields;
+ }
+}
diff --git a/app/Classes/CodeGenerator/Common/GeneratorField.php b/app/Classes/CodeGenerator/Common/GeneratorField.php
new file mode 100644
index 00000000..b3a7adff
--- /dev/null
+++ b/app/Classes/CodeGenerator/Common/GeneratorField.php
@@ -0,0 +1,173 @@
+dbInput = $dbInput;
+ if (!is_null($column)) {
+ $this->dbInput = ($column->getLength() > 0) ? $this->dbInput.','.$column->getLength() : $this->dbInput;
+ $this->dbInput = (!$column->getNotnull()) ? $this->dbInput.':nullable' : $this->dbInput;
+ }
+ $this->prepareMigrationText();
+ }
+
+ public function parseHtmlInput($htmlInput)
+ {
+ $this->htmlInput = $htmlInput;
+ $this->htmlValues = [];
+
+ if (empty($htmlInput)) {
+ $this->htmlType = 'text';
+
+ return;
+ }
+
+ if (Str::contains($htmlInput, 'selectTable')) {
+ $inputsArr = explode(':', $htmlInput);
+ $this->htmlType = array_shift($inputsArr);
+ $this->htmlValues = $inputsArr;
+
+ return;
+ }
+
+ $inputsArr = explode(',', $htmlInput);
+
+ $this->htmlType = array_shift($inputsArr);
+
+ if (count($inputsArr) > 0) {
+ $this->htmlValues = $inputsArr;
+ }
+ }
+
+ public function parseOptions($options)
+ {
+ $options = strtolower($options);
+ $optionsArr = explode(',', $options);
+ if (in_array('s', $optionsArr)) {
+ $this->isSearchable = false;
+ }
+ if (in_array('p', $optionsArr)) {
+ // if field is primary key, then its not searchable, fillable, not in index & form
+ $this->isPrimary = true;
+ $this->isSearchable = false;
+ $this->isFillable = false;
+ $this->inForm = false;
+ $this->inIndex = false;
+ $this->inView = false;
+ }
+ if (in_array('f', $optionsArr)) {
+ $this->isFillable = false;
+ }
+ if (in_array('if', $optionsArr)) {
+ $this->inForm = false;
+ }
+ if (in_array('ii', $optionsArr)) {
+ $this->inIndex = false;
+ }
+ if (in_array('iv', $optionsArr)) {
+ $this->inView = false;
+ }
+ }
+
+ private function prepareMigrationText()
+ {
+ $inputsArr = explode(':', $this->dbInput);
+ $this->migrationText = '$table->';
+
+ $fieldTypeParams = explode(',', array_shift($inputsArr));
+ $this->fieldType = array_shift($fieldTypeParams);
+ $this->migrationText .= $this->fieldType."('".$this->name."'";
+
+ if ($this->fieldType == 'enum') {
+ $this->migrationText .= ', [';
+ foreach ($fieldTypeParams as $param) {
+ $this->migrationText .= "'".$param."',";
+ }
+ $this->migrationText = substr($this->migrationText, 0, strlen($this->migrationText) - 1);
+ $this->migrationText .= ']';
+ } else {
+ foreach ($fieldTypeParams as $param) {
+ $this->migrationText .= ', '.$param;
+ }
+ }
+
+ $this->migrationText .= ')';
+
+ foreach ($inputsArr as $input) {
+ $inputParams = explode(',', $input);
+ $functionName = array_shift($inputParams);
+ if ($functionName == 'foreign') {
+ $foreignTable = array_shift($inputParams);
+ $foreignField = array_shift($inputParams);
+ $this->foreignKeyText .= "\$table->foreign('".$this->name."')->references('".$foreignField."')->on('".$foreignTable."');";
+ } else {
+ $this->migrationText .= '->'.$functionName;
+ $this->migrationText .= '(';
+ $this->migrationText .= implode(', ', $inputParams);
+ $this->migrationText .= ')';
+ }
+ }
+
+ $this->migrationText .= ';';
+ }
+
+ public static function parseFieldFromFile($fieldInput)
+ {
+ $field = new self();
+ $field->name = $fieldInput['name'];
+ $field->parseDBType($fieldInput['dbType']);
+ $field->parseHtmlInput(isset($fieldInput['htmlType']) ? $fieldInput['htmlType'] : '');
+ $field->validations = isset($fieldInput['validations']) ? $fieldInput['validations'] : '';
+ $field->isSearchable = isset($fieldInput['searchable']) ? $fieldInput['searchable'] : false;
+ $field->isFillable = isset($fieldInput['fillable']) ? $fieldInput['fillable'] : true;
+ $field->isPrimary = isset($fieldInput['primary']) ? $fieldInput['primary'] : false;
+ $field->inForm = isset($fieldInput['inForm']) ? $fieldInput['inForm'] : true;
+ $field->inIndex = isset($fieldInput['inIndex']) ? $fieldInput['inIndex'] : true;
+ $field->inView = isset($fieldInput['inView']) ? $fieldInput['inView'] : true;
+
+ return $field;
+ }
+
+ public function __get($key)
+ {
+ if ($key == 'fieldTitle') {
+ return Str::title(str_replace('_', ' ', $this->name));
+ }
+
+ return $this->$key;
+ }
+}
diff --git a/app/Classes/CodeGenerator/Common/GeneratorFieldRelation.php b/app/Classes/CodeGenerator/Common/GeneratorFieldRelation.php
new file mode 100644
index 00000000..eb569c75
--- /dev/null
+++ b/app/Classes/CodeGenerator/Common/GeneratorFieldRelation.php
@@ -0,0 +1,103 @@
+type = array_shift($inputs);
+ $modelWithRelation = explode(':', array_shift($inputs)); //e.g ModelName:relationName
+ if (count($modelWithRelation) == 2) {
+ $relation->relationName = $modelWithRelation[1];
+ unset($modelWithRelation[1]);
+ }
+ $relation->inputs = array_merge($modelWithRelation, $inputs);
+
+ return $relation;
+ }
+
+ public function getRelationFunctionText($relationText = null)
+ {
+ $singularRelation = (!empty($this->relationName)) ? $this->relationName : Str::camel($relationText);
+ $pluralRelation = (!empty($this->relationName)) ? $this->relationName : Str::camel(Str::plural($relationText));
+
+ switch ($this->type) {
+ case '1t1':
+ $functionName = $singularRelation;
+ $relation = 'hasOne';
+ $relationClass = 'HasOne';
+ break;
+ case '1tm':
+ $functionName = $pluralRelation;
+ $relation = 'hasMany';
+ $relationClass = 'HasMany';
+ break;
+ case 'mt1':
+ if (!empty($this->relationName)) {
+ $singularRelation = $this->relationName;
+ } elseif (isset($this->inputs[1])) {
+ $singularRelation = Str::camel(str_replace('_id', '', strtolower($this->inputs[1])));
+ }
+ $functionName = $singularRelation;
+ $relation = 'belongsTo';
+ $relationClass = 'BelongsTo';
+ break;
+ case 'mtm':
+ $functionName = $pluralRelation;
+ $relation = 'belongsToMany';
+ $relationClass = 'BelongsToMany';
+ break;
+ case 'hmt':
+ $functionName = $pluralRelation;
+ $relation = 'hasManyThrough';
+ $relationClass = 'HasManyThrough';
+ break;
+ default:
+ $functionName = '';
+ $relation = '';
+ $relationClass = '';
+ break;
+ }
+
+ if (!empty($functionName) and !empty($relation)) {
+ return $this->generateRelation($functionName, $relation, $relationClass);
+ }
+
+ return '';
+ }
+
+ private function generateRelation($functionName, $relation, $relationClass)
+ {
+ $inputs = $this->inputs;
+ $modelName = array_shift($inputs);
+
+ $template = (new GeneratorHelpers())->get_template('Models.relationship');
+
+ $template = str_replace('$RELATIONSHIP_CLASS$', $relationClass, $template);
+ $template = str_replace('$FUNCTION_NAME$', $functionName, $template);
+ $template = str_replace('$RELATION$', $relation, $template);
+ $template = str_replace('$RELATION_MODEL_NAME$', $modelName, $template);
+
+ if (count($inputs) > 0) {
+ $inputFields = implode("', '", $inputs);
+ $inputFields = ", '".$inputFields."'";
+ } else {
+ $inputFields = '';
+ }
+
+ $template = str_replace('$INPUT_FIELDS$', $inputFields, $template);
+
+ return $template;
+ }
+}
diff --git a/app/Classes/CodeGenerator/Common/GeneratorHelpers.php b/app/Classes/CodeGenerator/Common/GeneratorHelpers.php
new file mode 100644
index 00000000..350faf57
--- /dev/null
+++ b/app/Classes/CodeGenerator/Common/GeneratorHelpers.php
@@ -0,0 +1,78 @@
+generator_tab($spaces), $tabs);
+ }
+
+ public function generator_nl($count = 1)
+ {
+ return str_repeat(PHP_EOL, $count);
+ }
+
+ public function generator_nls($count, $nls = 1)
+ {
+ return str_repeat($this->generator_nl($nls), $count);
+ }
+
+ public function generator_nl_tab($lns = 1, $tabs = 1)
+ {
+ return $this->generator_nl($lns) . $this->generator_tabs($tabs);
+ }
+
+ public function get_template_file_path($templateName)
+ {
+ $templateName = str_replace('.', '/', $templateName);
+
+ return base_path('App/Classes/CodeGenerator/Stubs/'.$templateName.'.stub');
+ }
+
+ public function get_template($templateName)
+ {
+ $path = $this->get_template_file_path($templateName);
+
+ return file_get_contents($path);
+ }
+
+ public function fill_template($variables, $template)
+ {
+ foreach ($variables as $variable => $value) {
+ $template = str_replace($variable, $value, $template);
+ }
+
+ return $template;
+ }
+
+ public function fill_field_template($variables, $template, $field)
+ {
+ foreach ($variables as $variable => $key) {
+ $template = str_replace($variable, $field->$key, $template);
+ }
+
+ return $template;
+ }
+
+ public function fill_template_with_field_data($variables, $fieldVariables, $template, $field)
+ {
+ $template = $this->fill_template($variables, $template);
+
+ return $this->fill_field_template($fieldVariables, $template, $field);
+ }
+
+ public function model_name_from_table_name($tableName)
+ {
+ return Str::ucfirst(Str::camel(Str::singular($tableName)));
+ }
+}
\ No newline at end of file
diff --git a/app/Classes/CodeGenerator/Common/TemplatesManager.php b/app/Classes/CodeGenerator/Common/TemplatesManager.php
new file mode 100644
index 00000000..48420aff
--- /dev/null
+++ b/app/Classes/CodeGenerator/Common/TemplatesManager.php
@@ -0,0 +1,24 @@
+useLocale;
+ }
+
+ /**
+ * @param bool $useLocale
+ */
+ public function setUseLocale(bool $useLocale): void
+ {
+ $this->useLocale = $useLocale;
+ }
+}
diff --git a/app/Classes/CodeGenerator/Generators/BaseGenerator.php b/app/Classes/CodeGenerator/Generators/BaseGenerator.php
new file mode 100644
index 00000000..a2105b2d
--- /dev/null
+++ b/app/Classes/CodeGenerator/Generators/BaseGenerator.php
@@ -0,0 +1,31 @@
+generatorHelpers = new GeneratorHelpers();
+ }
+
+
+ public function rollbackFile($path, $fileName)
+ {
+ if (file_exists($path.$fileName)) {
+ return FileUtil::deleteFile($path, $fileName);
+ }
+
+ return false;
+ }
+}
diff --git a/app/Classes/CodeGenerator/Generators/FactoryGenerator.php b/app/Classes/CodeGenerator/Generators/FactoryGenerator.php
new file mode 100644
index 00000000..8a5f0c94
--- /dev/null
+++ b/app/Classes/CodeGenerator/Generators/FactoryGenerator.php
@@ -0,0 +1,119 @@
+commandData = $commandData;
+ $this->path = $commandData->config->pathFactory;
+ $this->fileName = Str::studly(Str::singular($this->commandData->modelName)).'Factory.php';
+ }
+
+ public function generate()
+ {
+ $templateData = $this->generatorHelpers->get_template('Factories.model_factory');
+
+ $templateData = $this->fillTemplate($templateData);
+
+ FileUtil::createFile($this->path, $this->fileName, $templateData);
+
+ $this->commandData->commandObj->comment("\nFactory created: ");
+ $this->commandData->commandObj->info($this->fileName);
+ }
+
+ /**
+ * @param string $templateData
+ *
+ * @return mixed|string
+ */
+ private function fillTemplate($templateData)
+ {
+ $templateData = $this->generatorHelpers->fill_template($this->commandData->dynamicVars, $templateData);
+
+ $templateData = str_replace(
+ '$FIELDS$',
+ implode(','.$this->generatorHelpers->generator_nl_tab(1, 2), $this->generateFields()),
+ $templateData
+ );
+
+ return $templateData;
+ }
+
+ /**
+ * @return array
+ */
+ private function generateFields()
+ {
+ $fields = [];
+
+ foreach ($this->commandData->fields as $field) {
+ if ($field->isPrimary) {
+ continue;
+ }
+
+ $fieldData = "'".$field->name."' => ".'$faker->';
+
+ switch ($field->fieldType) {
+ case 'integer':
+ case 'float':
+ $fakerData = 'randomDigitNotNull';
+ break;
+ case 'string':
+ $fakerData = 'word';
+ break;
+ case 'text':
+ $fakerData = 'text';
+ break;
+ case 'datetime':
+ case 'timestamp':
+ $fakerData = "date('Y-m-d H:i:s')";
+ break;
+ case 'enum':
+ $fakerData = 'randomElement('.
+ GeneratorFieldsInputUtil::prepareValuesArrayStr($field->htmlValues).
+ ')';
+ break;
+ default:
+ $fakerData = 'word';
+ }
+
+ $fieldData .= $fakerData;
+
+ $fields[] = $fieldData;
+ }
+
+ return $fields;
+ }
+
+ public function rollback()
+ {
+ if ($this->rollbackFile($this->path, $this->fileName)) {
+ $this->commandData->commandComment('Factory file deleted: '.$this->fileName);
+ }
+ }
+}
diff --git a/app/Classes/CodeGenerator/Generators/Micros/DataTransferObjectGenerator.php b/app/Classes/CodeGenerator/Generators/Micros/DataTransferObjectGenerator.php
new file mode 100644
index 00000000..7e246cbb
--- /dev/null
+++ b/app/Classes/CodeGenerator/Generators/Micros/DataTransferObjectGenerator.php
@@ -0,0 +1,140 @@
+commandData = $commandData;
+ $this->path = app_path('Classes/Modules/'. str::pluralStudly($this->commandData->modelName).'/DataTransferObjects/');
+ $this->fileName = Str::studly(Str::singular($this->commandData->modelName)).'Object.php';
+ }
+
+ public function generate()
+ {
+
+ $templateData = $this->generatorHelpers->get_template('Micros.data_transfer_object');
+
+ $templateData = $this->fillTemplate($templateData);
+
+ FileUtil::createFile($this->path, $this->fileName, $templateData);
+
+ $this->commandData->commandComment("\nDataTransferObject created: ");
+ $this->commandData->commandObj->info($this->fileName);
+
+ }
+
+ /**
+ * @param string $templateData
+ *
+ * @return mixed|string
+ */
+ private function fillTemplate($templateData)
+ {
+ $properties = [];
+ $docs = [];
+ $injection = [];
+ $body = [];
+ $getters = [];
+
+ foreach ($this->commandData->fields as $field) {
+ if(!in_array($field->name, $this->commandData->config->excludeFields())){
+ $docType = $this->getPHPDocType($field->fieldType);
+ $fieldName = $docType === 'bool' ? 'is'.str::studly($field->name) : str::camel($field->name);
+ $properties[] = '/** @var '.$docType.' */'.PHP_EOL.$this->generatorHelpers->generator_nl_tab(0, 1).'private $'.str::camel($fieldName).';';
+ $docs[] = '* @param '.$docType.' $'.str::camel($fieldName);
+ $injection[] = $docType.' $'.str::camel($fieldName);
+ $body[] = '$this->'.str::camel($fieldName).' = $'.str::camel($fieldName).';';
+ $getters[] = (new GenerateGetters())->generate($fieldName, $docType);
+ }
+
+ }
+
+ $templateData = $this->generatorHelpers->fill_template($this->commandData->dynamicVars, $templateData);
+
+ $templateData = str_replace(
+ '$PROPERTIES$',
+ implode(PHP_EOL.$this->generatorHelpers->generator_nl_tab(1, 1), $properties),
+ $templateData
+ );
+
+ $templateData = str_replace(
+ '$CONSTRUCTOR_DOCS$',
+ implode($this->generatorHelpers->generator_nl_tab(1, 2), $docs),
+ $templateData
+ );
+
+ $templateData = str_replace(
+ '$CONSTRUCTOR_PROPERTIES$',
+ implode(', ', $injection),
+ $templateData
+ );
+
+ $templateData = str_replace(
+ '$CONSTRUCTOR_BODY$',
+ implode($this->generatorHelpers->generator_nl_tab(1, 2), $body),
+ $templateData
+ );
+
+ $templateData = str_replace(
+ '$GETTER_FUNCTIONS$',
+ implode($this->generatorHelpers->generator_nl_tab(1, 0), $getters),
+ $templateData
+ );
+
+ return $templateData;
+ }
+
+
+
+ private function getPHPDocType($db_type){
+ switch ($db_type) {
+ case 'text':
+ return 'string';
+ case 'datetime':
+ return '\Carbon\Carbon';
+ case 'boolean':
+ return 'bool';
+ default:
+ return $db_type;
+
+ }
+ }
+
+ public function rollback()
+ {
+ if ($this->rollbackFile($this->path, $this->fileName)) {
+ File::deleteDirectory($this->path);
+ $this->commandData->commandComment('DataTransferObject file deleted: '.$this->fileName);
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/app/Classes/CodeGenerator/Generators/Micros/ResourceGenerator.php b/app/Classes/CodeGenerator/Generators/Micros/ResourceGenerator.php
new file mode 100644
index 00000000..a6db66e8
--- /dev/null
+++ b/app/Classes/CodeGenerator/Generators/Micros/ResourceGenerator.php
@@ -0,0 +1,75 @@
+commandData = $commandData;
+ $this->path = app_path('Http/Resources/');
+ $this->fileName = Str::studly(Str::singular($this->commandData->modelName)).'Resource.php';
+ }
+
+ public function generate()
+ {
+ $templateData = $this->generatorHelpers->get_template('Resource.model_resource');
+
+ $templateData = $this->generatorHelpers->fill_template($this->commandData->dynamicVars, $templateData);
+
+ $templateData = str_replace('$FIELDS$', implode(','.$this->generatorHelpers->generator_nl_tab(1, 3), $this->generateFields()), $templateData);
+
+ FileUtil::createFile($this->path, $this->fileName, $templateData);
+
+ $this->commandData->commandComment("\n Resource Object created: ");
+ $this->commandData->commandObj->info($this->fileName);
+
+ }
+
+
+ private function generateFields()
+ {
+
+ $fields = [];
+
+ foreach ($this->commandData->fields as $field) {
+
+ $field = "'" . $field->name . "' => " . "$" . "this->" . str::snake($field->name);
+ $fields[] = $field;
+ }
+
+ return $fields;
+ }
+
+ public function rollback()
+ {
+ if ($this->rollbackFile($this->path, $this->fileName)) {
+ $this->commandData->commandComment('Resource Object file deleted: '.$this->fileName);
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/Classes/CodeGenerator/Generators/Micros/RulesGenerator.php b/app/Classes/CodeGenerator/Generators/Micros/RulesGenerator.php
new file mode 100644
index 00000000..be1b72d4
--- /dev/null
+++ b/app/Classes/CodeGenerator/Generators/Micros/RulesGenerator.php
@@ -0,0 +1,68 @@
+commandData = $commandData;
+ $this->path = app_path('Classes/Modules/'. str::pluralStudly($this->commandData->modelName).'/Standards/Rules/');
+ }
+
+ public function generate()
+ {
+ foreach(['create', 'list', 'fetch', 'update', 'delete'] as $type){
+ $name = $type === 'list' ? str::pluralStudly($this->commandData->modelName) : str::singular($this->commandData->modelName);
+ $filename = 'Can'.str::studly($type.$name).'.php';
+ $templateData = $this->generatorHelpers->get_template('Rules.can_'.$type);
+
+ $templateData = $this->generatorHelpers->fill_template($this->commandData->dynamicVars, $templateData);
+
+ FileUtil::createFile($this->path, $filename, $templateData);
+
+ $this->commandData->commandComment("\n" . $type . " Rules created: ");
+ $this->commandData->commandObj->info($filename);
+ }
+
+ }
+
+ public function rollback()
+ {
+ foreach(['create', 'list', 'fetch', 'update', 'delete'] as $type){
+
+ $name = $type === 'list' ? str::pluralStudly($this->commandData->modelName) : str::singular($this->commandData->modelName);
+ $filename ='Can'.str::studly($type.$name).'.php';
+
+ if ($this->rollbackFile($this->path, $filename)) {
+ $this->commandData->commandComment($type . ' Rules file deleted: '.$filename);
+ }
+ }
+
+ File::deleteDirectory($this->path);
+
+ }
+}
\ No newline at end of file
diff --git a/app/Classes/CodeGenerator/Generators/Micros/ServicesGenerator.php b/app/Classes/CodeGenerator/Generators/Micros/ServicesGenerator.php
new file mode 100644
index 00000000..ad45cb0f
--- /dev/null
+++ b/app/Classes/CodeGenerator/Generators/Micros/ServicesGenerator.php
@@ -0,0 +1,96 @@
+commandData = $commandData;
+ $this->path = app_path('Classes/Modules/'. str::pluralStudly($this->commandData->modelName).'/Services/');
+ }
+
+ public function generate()
+ {
+ foreach(['create', 'list', 'fetch', 'update', 'delete'] as $type){
+ $name = $type === 'list' ? str::pluralStudly($this->commandData->modelName) : str::singular($this->commandData->modelName);
+ $filename = str::studly($type.($type === 'fetch'?'es':'s').$name).'.php';
+
+ $templateData = $this->generatorHelpers->get_template("Services.".str::snake($type.'_service'));
+
+ $templateData = $this->generatorHelpers->fill_template($this->commandData->dynamicVars, $templateData);
+
+ $templateData = str_replace(
+ '$FIELDS$',
+ implode($this->generatorHelpers->generator_nl_tab(1, 2), $this->generateFields()),
+ $templateData
+ );
+
+
+ FileUtil::createFile($this->path, $filename, $templateData);
+
+ $this->commandData->commandComment("\n" . $type . " service class created: ");
+ $this->commandData->commandObj->info($filename);
+
+ }
+
+ }
+
+
+ private function generateFields()
+ {
+
+ $fields = [];
+
+ foreach ($this->commandData->fields as $field) {
+
+ if(!in_array($field->name, $this->commandData->config->excludeFields())) {
+
+ $getterName = $field->dbInput === 'boolean' ? 'is' . str::studly($field->name) : 'get' . str::studly($field->name);
+
+ $field = "$" . "model->" . $field->name . " = $" . "object->" . $getterName . "();";
+ $fields[] = $field;
+ }
+ }
+
+ return $fields;
+ }
+
+ public function rollback()
+ {
+
+ foreach(['create', 'list', 'fetch', 'update', 'delete'] as $type){
+ $name = $type === 'list' ? str::pluralStudly($this->commandData->modelName) : str::singular($this->commandData->modelName);
+ $filename = str::studly($type.($type === 'fetch'?'es':'s').$name).'.php';
+ if ($this->rollbackFile($this->path, $filename)) {
+ $this->commandData->commandComment( $type . ' service class file deleted: '.$filename);
+ }
+ }
+
+ File::deleteDirectory(($this->path));
+ }
+}
\ No newline at end of file
diff --git a/app/Classes/CodeGenerator/Generators/Micros/ValidatorsGenerator.php b/app/Classes/CodeGenerator/Generators/Micros/ValidatorsGenerator.php
new file mode 100644
index 00000000..d9059548
--- /dev/null
+++ b/app/Classes/CodeGenerator/Generators/Micros/ValidatorsGenerator.php
@@ -0,0 +1,112 @@
+commandData = $commandData;
+ $this->path = app_path('Classes/Modules/'. str::pluralStudly($this->commandData->modelName).'/Standards/Validators/');
+ $this->fileName = Str::studly(Str::singular($this->commandData->modelName)).'Validation.php';
+ }
+
+ public function generate()
+ {
+ $templateData = $this->generatorHelpers->get_template('Validator.request_validation');
+
+ $templateData = $this->generatorHelpers->fill_template($this->commandData->dynamicVars, $templateData);
+
+ $templateData = str_replace('$FIELDS$', implode(','.$this->generatorHelpers->generator_nl_tab(1, 3), $this->generateFields()), $templateData);
+ $templateData = str_replace('$RULES$', implode(','.$this->generatorHelpers->generator_nl_tab(1, 3), $this->generateRules()), $templateData);
+
+ FileUtil::createFile($this->path, $this->fileName, $templateData);
+
+ $this->commandData->commandComment("\n Request Validator created: ");
+ $this->commandData->commandObj->info($this->fileName);
+
+ }
+
+ private function generateRules()
+ {
+ $dont_require_fields = [];
+
+ $rules = [];
+
+ foreach ($this->commandData->fields as $field) {
+ if (!$field->isPrimary && $field->isNotNull && empty($field->validations) &&
+ !in_array($field->name, $dont_require_fields)) {
+ $field->validations = 'required';
+ }
+
+ if (!empty($field->validations)) {
+ if (Str::contains($field->validations, 'unique:')) {
+ $rule = explode('|', $field->validations);
+ // move unique rule to last
+ usort($rule, function ($record) {
+ return (Str::contains($record, 'unique:')) ? 1 : 0;
+ });
+ $field->validations = implode('|', $rule);
+ }
+ $rule = "'".$field->name."' => '".$field->validations."'";
+ $rules[] = $rule;
+ }
+ }
+
+ return $rules;
+ }
+
+ private function generateFields()
+ {
+
+ $fields = [];
+
+ foreach ($this->commandData->fields as $field) {
+
+ if(!in_array($field->name, $this->commandData->config->excludeFields())) {
+
+ $getterName = $field->dbInput === 'boolean' ? 'is' . str::studly($field->name) : 'get' . str::studly($field->name);
+
+ $field = "'" . $field->name . "' => " . "$" . "object->" . $getterName . "()";
+ $fields[] = $field;
+ }
+ }
+
+ return $fields;
+ }
+
+ public function rollback()
+ {
+ if ($this->rollbackFile($this->path, $this->fileName)) {
+ File::deleteDirectory($this->path);
+ $this->commandData->commandComment('Request Validation file deleted: '.$this->fileName);
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/app/Classes/CodeGenerator/Generators/MigrationGenerator.php b/app/Classes/CodeGenerator/Generators/MigrationGenerator.php
new file mode 100644
index 00000000..32adf6c7
--- /dev/null
+++ b/app/Classes/CodeGenerator/Generators/MigrationGenerator.php
@@ -0,0 +1,92 @@
+commandData = $commandData;
+ $this->path = database_path('migrations/');
+ }
+
+ public function generate()
+ {
+ $templateData = $this->generatorHelpers->get_template('Migration.migration');
+
+ $templateData = $this->generatorHelpers->fill_template($this->commandData->dynamicVars, $templateData);
+
+ $templateData = str_replace('$FIELDS$', $this->generateFields(), $templateData);
+
+ $tableName = $this->commandData->dynamicVars['$TABLE_NAME$'];
+
+ $fileName = date('Y_m_d_His').'_'.'create_'.Str::snake(Str::plural($tableName)).'_table.php';
+
+ FileUtil::createFile($this->path, $fileName, $templateData);
+
+ $this->commandData->commandComment("\nMigration created: ");
+ $this->commandData->commandInfo($fileName);
+ }
+
+ private function generateFields()
+ {
+ $fields = [];
+ $foreignKeys = [];
+ $createdAtField = null;
+ $updatedAtField = null;
+
+ $fields[] = '$table->id();';
+
+ foreach ($this->commandData->fields as $field) {
+ $fields[] = $field->migrationText;
+ if (!empty($field->foreignKeyText)) {
+ $foreignKeys[] = $field->foreignKeyText;
+ }
+ }
+
+ $fields[] = '$table->timestamps();';
+
+ if ($this->commandData->getOption('softDelete')) {
+ $fields[] = '$table->softDeletes();';
+ }
+
+ return implode($this->generatorHelpers->generator_nl_tab(1, 3), array_merge($fields, $foreignKeys));
+ }
+
+ public function rollback()
+ {
+ $fileName = 'create_'.$this->commandData->config->tableName.'_table.php';
+
+ $allFiles = File::allFiles($this->path);
+
+ $files = [];
+
+ foreach ($allFiles as $file) {
+ $files[] = $file->getFilename();
+ }
+
+ $files = array_reverse($files);
+
+ foreach ($files as $file) {
+ if (Str::contains($file, $fileName)) {
+ if ($this->rollbackFile($this->path, $file)) {
+ $this->commandData->commandComment('Migration file deleted: '.$file);
+ }
+ break;
+ }
+ }
+ }
+}
diff --git a/app/Classes/CodeGenerator/Generators/ModelGenerator.php b/app/Classes/CodeGenerator/Generators/ModelGenerator.php
new file mode 100644
index 00000000..0127d57a
--- /dev/null
+++ b/app/Classes/CodeGenerator/Generators/ModelGenerator.php
@@ -0,0 +1,351 @@
+commandData = $commandData;
+ $this->path = $commandData->config->pathModel;
+ $this->fileName = Str::studly(Str::singular($this->commandData->modelName)).'.php';
+ $this->table = $this->commandData->dynamicVars['$TABLE_NAME$'];
+ }
+
+ public function generate()
+ {
+ $templateData = $this->generatorHelpers->get_template('Models.model');
+
+ $templateData = $this->fillTemplate($templateData);
+
+ FileUtil::createFile($this->path, $this->fileName, $templateData);
+
+ $this->commandData->commandComment("\nModel created: ");
+ $this->commandData->commandInfo($this->fileName);
+ }
+
+ private function fillTemplate($templateData)
+ {
+ $templateData = $this->generatorHelpers->fill_template($this->commandData->dynamicVars, $templateData);
+
+ $templateData = $this->fillSoftDeletes($templateData);
+
+ $fillables = [];
+
+ foreach ($this->commandData->fields as $field) {
+ if ($field->isFillable) {
+ $fillables[] = "'".$field->name."'";
+ }
+ }
+
+ $templateData = $this->fillDocs($templateData);
+
+ $templateData = $this->fillTimestamps($templateData);
+
+ if ($this->commandData->getOption('primary')) {
+ $primary = $this->generatorHelpers->generator_tab()."protected \$primaryKey = '".$this->commandData->getOption('primary')."';\n";
+ } else {
+ $primary = '';
+ }
+
+ $templateData = str_replace('$PRIMARY$', $primary, $templateData);
+
+ $templateData = str_replace('$FIELDS$', implode(','.$this->generatorHelpers->generator_nl_tab(1, 2), $fillables), $templateData);
+
+ $templateData = str_replace('$RULES$', implode(','.$this->generatorHelpers->generator_nl_tab(1, 2), $this->generateRules()), $templateData);
+
+ $templateData = str_replace('$CAST$', implode(','.$this->generatorHelpers->generator_nl_tab(1, 2), $this->generateCasts()), $templateData);
+
+ $templateData = str_replace(
+ '$RELATIONS$',
+ $this->generatorHelpers->fill_template($this->commandData->dynamicVars, implode(PHP_EOL.$this->generatorHelpers->generator_nl_tab(1, 1), $this->generateRelations())),
+ $templateData
+ );
+
+ $templateData = str_replace('$GENERATE_DATE$', date('F j, Y, g:i a T'), $templateData);
+
+ return $templateData;
+ }
+
+ private function fillSoftDeletes($templateData)
+ {
+ if (!$this->commandData->getOption('softDelete')) {
+ $templateData = str_replace('$SOFT_DELETE_IMPORT$', '', $templateData);
+ $templateData = str_replace('$SOFT_DELETE$', '', $templateData);
+ $templateData = str_replace('$SOFT_DELETE_DATES$', '', $templateData);
+ } else {
+ $templateData = str_replace(
+ '$SOFT_DELETE_IMPORT$',
+ "use Illuminate\\Database\\Eloquent\\SoftDeletes;\n",
+ $templateData
+ );
+ $templateData = str_replace('$SOFT_DELETE$', $this->generatorHelpers->generator_tab()."use SoftDeletes;\n", $templateData);
+ $deletedAtTimestamp = 'deleted_at';
+ $templateData = str_replace(
+ '$SOFT_DELETE_DATES$',
+ $this->generatorHelpers->generator_nl_tab()."protected \$dates = ['".$deletedAtTimestamp."'];\n",
+ $templateData
+ );
+ }
+
+ return $templateData;
+ }
+
+ private function fillDocs($templateData)
+ {
+
+ $docsTemplate = $this->generatorHelpers->get_template('Docs.model');
+ $docsTemplate = $this->generatorHelpers->fill_template($this->commandData->dynamicVars, $docsTemplate);
+
+ $fillables = '';
+ $fieldsArr = [];
+ $count = 1;
+ foreach ($this->commandData->relations as $relation) {
+ $field = $relationText = (isset($relation->inputs[0])) ? $relation->inputs[0] : null;
+ if (in_array($field, $fieldsArr)) {
+ $relationText = $relationText.'_'.$count;
+ $count++;
+ }
+
+ $fillables .= ' * @property '.$this->getPHPDocType($relation->type, $relation, $relationText).PHP_EOL;
+ $fieldsArr[] = $field;
+ }
+
+ foreach ($this->commandData->fields as $field) {
+ if ($field->isFillable) {
+ $fillables .= ' * @property '.$this->getPHPDocType($field->fieldType).' '.$field->name.PHP_EOL;
+ }
+ }
+ $docsTemplate = str_replace('$GENERATE_DATE$', date('F j, Y, g:i a'), $docsTemplate);
+ $docsTemplate = str_replace('$PHPDOC$', $fillables, $docsTemplate);
+
+ $templateData = str_replace('$DOCS$', $docsTemplate, $templateData);
+
+ return $templateData;
+ }
+
+ /**
+ * @param $db_type
+ * @param GeneratorFieldRelation|null $relation
+ * @param string|null $relationText
+ *
+ * @return string
+ */
+ private function getPHPDocType($db_type, $relation = null, $relationText = null)
+ {
+ $relationText = (!empty($relationText)) ? $relationText : null;
+
+ switch ($db_type) {
+ case 'text':
+ return 'string';
+ case 'datetime':
+ return 'string|\Carbon\Carbon';
+ case '1t1':
+ return '\\'.$this->commandData->config->nsModel.'\\'.$relation->inputs[0].' '.Str::camel($relationText);
+ case 'mt1':
+ if (isset($relation->inputs[1])) {
+ $relationName = str_replace('_id', '', strtolower($relation->inputs[1]));
+ } else {
+ $relationName = $relationText;
+ }
+
+ return '\\'.$this->commandData->config->nsModel.'\\'.$relation->inputs[0].' '.Str::camel($relationName);
+ case '1tm':
+ case 'mtm':
+ case 'hmt':
+ return '\Illuminate\Database\Eloquent\Collection'.' '.Str::camel(Str::plural($relationText));
+ default:
+ if (!empty($fieldData['fieldType'])) {
+ return $fieldData['fieldType'];
+ }
+
+ return $db_type;
+ }
+ }
+
+ private function fillTimestamps($templateData)
+ {
+ $timestamps = TableFieldsGenerator::getTimestampFieldNames();
+
+ $replace = '';
+ if (empty($timestamps)) {
+ $replace = $this->generatorHelpers->generator_nl_tab()."public \$timestamps = false;\n";
+ }
+
+ if ($this->commandData->getOption('fromTable') && !empty($timestamps)) {
+ list($created_at, $updated_at) = collect($timestamps)->map(function ($field) {
+ return !empty($field) ? "'$field'" : 'null';
+ });
+
+ $replace .= $this->generatorHelpers->generator_nl_tab()."const CREATED_AT = $created_at;";
+ $replace .= $this->generatorHelpers->generator_nl_tab()."const UPDATED_AT = $updated_at;\n";
+ }
+
+ return str_replace('$TIMESTAMPS$', $replace, $templateData);
+ }
+
+ private function generateRules()
+ {
+ $dont_require_fields = [];
+
+ $rules = [];
+
+ foreach ($this->commandData->fields as $field) {
+ if (!$field->isPrimary && $field->isNotNull && empty($field->validations) &&
+ !in_array($field->name, $dont_require_fields)) {
+ $field->validations = 'required';
+ }
+
+ if (!empty($field->validations)) {
+ if (Str::contains($field->validations, 'unique:')) {
+ $rule = explode('|', $field->validations);
+ // move unique rule to last
+ usort($rule, function ($record) {
+ return (Str::contains($record, 'unique:')) ? 1 : 0;
+ });
+ $field->validations = implode('|', $rule);
+ }
+ $rule = "'".$field->name."' => '".$field->validations."'";
+ $rules[] = $rule;
+ }
+ }
+
+ return $rules;
+ }
+
+ public function generateUniqueRules()
+ {
+ $tableNameSingular = Str::singular($this->commandData->config->tableName);
+ $uniqueRules = '';
+ foreach ($this->generateRules() as $rule) {
+ if (Str::contains($rule, 'unique:')) {
+ $rule = explode('=>', $rule);
+ $string = '$rules['.trim($rule[0]).'].","';
+
+ $uniqueRules .= '$rules['.trim($rule[0]).'] = '.$string.'.$this->route("'.$tableNameSingular.'");';
+ }
+ }
+
+ return $uniqueRules;
+ }
+
+ public function generateCasts()
+ {
+ $casts = [];
+
+ $timestamps = TableFieldsGenerator::getTimestampFieldNames();
+
+ foreach ($this->commandData->fields as $field) {
+ if (in_array($field->name, $timestamps)) {
+ continue;
+ }
+
+ $rule = "'".$field->name."' => ";
+
+ switch (strtolower($field->fieldType)) {
+ case 'integer':
+ case 'increments':
+ case 'smallinteger':
+ case 'long':
+ case 'biginteger':
+ $rule .= "'integer'";
+ break;
+ case 'double':
+ $rule .= "'double'";
+ break;
+ case 'float':
+ case 'decimal':
+ $rule .= "'float'";
+ break;
+ case 'boolean':
+ $rule .= "'boolean'";
+ break;
+ case 'datetime':
+ case 'datetimetz':
+ $rule .= "'datetime'";
+ break;
+ case 'date':
+ $rule .= "'date'";
+ break;
+ case 'enum':
+ case 'string':
+ case 'char':
+ case 'text':
+ $rule .= "'string'";
+ break;
+ default:
+ $rule = '';
+ break;
+ }
+
+ if (!empty($rule)) {
+ $casts[] = $rule;
+ }
+ }
+
+ return $casts;
+ }
+
+ private function generateRelations()
+ {
+ $relations = [];
+
+ $count = 1;
+ $fieldsArr = [];
+ foreach ($this->commandData->relations as $relation) {
+ $field = (isset($relation->inputs[0])) ? $relation->inputs[0] : null;
+
+ $relationShipText = $field;
+ if (in_array($field, $fieldsArr)) {
+ $relationShipText = $relationShipText.'_'.$count;
+ $count++;
+ }
+
+ $relationText = $relation->getRelationFunctionText($relationShipText);
+ if (!empty($relationText)) {
+ $fieldsArr[] = $field;
+ $relations[] = $relationText;
+ }
+ }
+
+ return $relations;
+ }
+
+ public function rollback()
+ {
+ if ($this->rollbackFile($this->path, $this->fileName)) {
+ $this->commandData->commandComment('Model file deleted: '.$this->fileName);
+ }
+ }
+}
diff --git a/app/Classes/CodeGenerator/Generators/Scaffold/ControllerLogicGenerator.php b/app/Classes/CodeGenerator/Generators/Scaffold/ControllerLogicGenerator.php
new file mode 100644
index 00000000..dd10bf31
--- /dev/null
+++ b/app/Classes/CodeGenerator/Generators/Scaffold/ControllerLogicGenerator.php
@@ -0,0 +1,83 @@
+commandData = $commandData;
+ $this->path = app_path('Classes/Modules/'. str::pluralStudly($this->commandData->modelName).'/ControllersLogic/');
+ }
+
+ public function generate()
+ {
+
+ foreach(['create', 'list', 'fetch', 'update', 'delete'] as $type){
+
+ $name = $type === 'list' ? str::pluralStudly($this->commandData->modelName) : str::singular($this->commandData->modelName);
+ $filename = str::studly($type.$name).'Logic.php';
+
+ $templateData = $this->generatorHelpers->get_template("Scaffold.ControllersLogic.".$type."_controller_logic");
+
+ $templateData = $this->generatorHelpers->fill_template($this->commandData->dynamicVars, $templateData);
+
+ $templateData = str_replace(
+ '$REQUEST_FIELDS$',
+ implode(', ', $this->generateFields()),
+ $templateData
+ );
+
+
+ FileUtil::createFile($this->path, $filename, $templateData);
+
+ $this->commandData->commandComment("\n" . $type . " Controller logic created: ");
+ $this->commandData->commandInfo($filename);
+ }
+
+ }
+
+ private function generateFields(){
+ $fields = [];
+
+ foreach ($this->commandData->fields as $field) {
+ if(!in_array($field->name, $this->commandData->config->excludeFields())) {
+ $fields[] = '$request->input(\'' . $field->name . '\')';
+ }
+ }
+
+ return $fields;
+ }
+
+ public function rollback()
+ {
+ foreach(['create', 'list', 'fetch', 'update', 'delete'] as $type){
+
+ $name = $type === 'list' ? str::pluralStudly($this->commandData->modelName) : str::singular($this->commandData->modelName);
+ $filename = str::studly($type.$name).'Logic.php';
+
+ if ($this->rollbackFile($this->path, $filename)) {
+ $this->commandData->commandComment($type . ' Controller logic file deleted: '.$filename);
+ }
+ }
+
+ File::deleteDirectory($this->path);
+
+ }
+}
diff --git a/app/Classes/CodeGenerator/Generators/Scaffold/ControllersGenerator.php b/app/Classes/CodeGenerator/Generators/Scaffold/ControllersGenerator.php
new file mode 100644
index 00000000..c651a39d
--- /dev/null
+++ b/app/Classes/CodeGenerator/Generators/Scaffold/ControllersGenerator.php
@@ -0,0 +1,64 @@
+commandData = $commandData;
+ $this->path = $commandData->config->pathController.'/'.str::pluralStudly($this->commandData->modelName).'/';
+ }
+
+ public function generate()
+ {
+
+ foreach(['create', 'list', 'fetch', 'update', 'delete'] as $type){
+
+ $name = $type === 'list' ? str::pluralStudly($this->commandData->modelName) : str::singular($this->commandData->modelName);
+ $filename = str::studly($type.$name).'Controller.php';
+
+ $templateData = $this->generatorHelpers->get_template("Scaffold.Controllers.".$type."_controller");
+
+ $templateData = $this->generatorHelpers->fill_template($this->commandData->dynamicVars, $templateData);
+
+ FileUtil::createFile($this->path, $filename, $templateData);
+
+ $this->commandData->commandComment("\n" . $type . " Controller created: ");
+ $this->commandData->commandInfo($filename);
+ }
+
+ }
+
+ public function rollback()
+ {
+ foreach(['create', 'list', 'fetch', 'update', 'delete'] as $type){
+
+ $name = $type === 'list' ? str::pluralStudly($this->commandData->modelName) : str::singular($this->commandData->modelName);
+ $filename = str::studly($type.$name).'Controller.php';
+
+ if ($this->rollbackFile($this->path, $filename)) {
+ $this->commandData->commandComment($type . ' Controller file deleted: '.$filename);
+ }
+ }
+
+ File::deleteDirectory($this->path);
+
+ }
+}
diff --git a/app/Classes/CodeGenerator/Generators/Scaffold/RoutesGenerator.php b/app/Classes/CodeGenerator/Generators/Scaffold/RoutesGenerator.php
new file mode 100644
index 00000000..33daa971
--- /dev/null
+++ b/app/Classes/CodeGenerator/Generators/Scaffold/RoutesGenerator.php
@@ -0,0 +1,55 @@
+commandData = $commandData;
+ $this->path = base_path('routes/crud.php');;
+ $this->routeContents = file_get_contents($this->path);
+ $this->routesTemplate = $this->generatorHelpers->get_template('Routes.route');
+ $this->routesTemplate = $this->generatorHelpers->fill_template($this->commandData->dynamicVars, $this->routesTemplate);
+ }
+
+ public function generate()
+ {
+ if (Str::contains($this->routeContents, "Route::group(['prefix' => '".$this->commandData->config->mSnake."',")) {
+ $this->commandData->commandObj->info('Routes for '.$this->commandData->config->mName.' already exists, Skipping Adjustment.');
+
+ return;
+ }
+
+ file_put_contents($this->path, $this->routeContents.$this->routesTemplate);
+ $this->commandData->commandComment("\n".$this->commandData->config->mName.' routes added.');
+ }
+
+ public function rollback()
+ {
+ if (Str::contains($this->routeContents, $this->routesTemplate)) {
+ $this->routeContents = str_replace($this->routesTemplate, '', $this->routeContents);
+ file_put_contents($this->path, $this->routeContents);
+ $this->commandData->commandComment('Routes deleted');
+ }
+ }
+}
diff --git a/app/Classes/CodeGenerator/Generators/Scaffold/ViewsGenerator.php b/app/Classes/CodeGenerator/Generators/Scaffold/ViewsGenerator.php
new file mode 100644
index 00000000..bac5b4a1
--- /dev/null
+++ b/app/Classes/CodeGenerator/Generators/Scaffold/ViewsGenerator.php
@@ -0,0 +1,53 @@
+commandData = $commandData;
+ $this->path = resource_path('views/pages/'.str::plural(str::snake($this->commandData->modelName)).'/');
+ $this->fileName = 'index.blade.php';
+ }
+
+ public function generate()
+ {
+
+ $templateData = $this->generatorHelpers->get_template("Views.view");
+
+ $templateData = $this->generatorHelpers->fill_template($this->commandData->dynamicVars, $templateData);
+
+ FileUtil::createFile($this->path, $this->fileName, $templateData);
+
+ $this->commandData->commandComment("\nView created: ");
+ $this->commandData->commandInfo($this->fileName);
+ }
+
+ public function rollback()
+ {
+ if ($this->rollbackFile($this->path, $this->fileName)) {
+ $this->commandData->commandComment('View file deleted: '.$this->fileName);
+ File::deleteDirectory($this->path);
+ }
+ }
+}
diff --git a/app/Classes/CodeGenerator/Generators/Scaffold/VueGenerator.php b/app/Classes/CodeGenerator/Generators/Scaffold/VueGenerator.php
new file mode 100644
index 00000000..0d700371
--- /dev/null
+++ b/app/Classes/CodeGenerator/Generators/Scaffold/VueGenerator.php
@@ -0,0 +1,196 @@
+commandData = $commandData;
+ $this->path = resource_path('assets/vue/components/'.str::camel($this->commandData->modelName).'/');
+
+ }
+
+ public function generate()
+ {
+
+ $fields = [];
+ $i = 1;
+ foreach ($this->commandData->fields as $index => $field) {
+ if (!$field->inIndex) {
+ continue;
+ }
+
+ $fieldTemplate = $this->generatorHelpers->get_template("Views.column");
+ $fieldTemplate = str_replace('$FIRST_COLUMN_CLASS$', $i === 1 ? 'ist-item-heading truncate' : 'text-small', $fieldTemplate);
+ $fieldTemplate = str_replace('$COLUMN_SIZE$', $i === 1 ? 'col-3' : 'col', $fieldTemplate);
+ $fieldTemplate = $this->generatorHelpers->fill_template_with_field_data(
+ $this->commandData->dynamicVars,
+ $this->commandData->fieldNamesMapping,
+ $fieldTemplate,
+ $field
+ );
+
+ $fields[] = $fieldTemplate;
+
+ $i++;
+
+ }
+ $templateData = $this->generatorHelpers->get_template("VueJs.element_component");
+ $templateData = $this->generatorHelpers->fill_template($this->commandData->dynamicVars, $templateData);
+ $templateData = str_replace('$FIELD_BODY$', implode($this->generatorHelpers->generator_nl_tab(1, 0), $fields), $templateData);
+ $fileName = $this->commandData->modelName.'Component.vue';
+ FileUtil::createFile($this->path.'elements/', $fileName, $templateData);
+
+ $this->commandData->commandComment("\nvue element created: ");
+ $this->commandData->commandInfo($fileName);
+
+ $this->generateForm();
+ $this->generateFilterForm();
+ }
+
+ private function generateForm()
+ {
+
+ $this->htmlFields = [];
+ $formFields = [];
+ $validations = [];
+
+ foreach ($this->commandData->fields as $field) {
+ if (!$field->inForm) {
+ continue;
+ }
+
+ $formFields[] = $field->name.": ''";
+
+ $validations[] = $field->name . ": { " . implode(', ', explode('|', $field->validations)) ." }";
+
+ $fieldTemplate = $this->generatorHelpers->get_template('Fields.field');
+
+ $fieldInputTemplate = HTMLFieldGenerator::generateHTML($field);
+
+ if($field->htmlType === 'selectTable'){
+ $fieldTemplate = str_replace('$v.parameters.$FIELD_NAME$', '$v.parameters.'.$field->name, $fieldTemplate);
+ $fieldTemplate = str_replace('$FIELD_NAME$', Str::replaceLast('_id', '', $field->name), $fieldTemplate);
+ }
+
+ $fieldTemplate = str_replace('$FIELD$', $fieldInputTemplate, $fieldTemplate);
+
+
+ if (!empty($fieldTemplate)) {
+ $fieldTemplate = $this->generatorHelpers->fill_template_with_field_data(
+ $this->commandData->dynamicVars,
+ $this->commandData->fieldNamesMapping,
+ $fieldTemplate,
+ $field
+ );
+
+ $this->htmlFields[] = $fieldTemplate;
+ }
+ }
+
+ $templateData = $this->generatorHelpers->get_template('VueJs.form_component');
+ $templateData = $this->generatorHelpers->fill_template($this->commandData->dynamicVars, $templateData);
+
+ $templateData = str_replace('$FIELDS$', implode("\n", $this->htmlFields), $templateData);
+ $templateData = str_replace('$REQUEST_FIELDS$', implode(','.$this->generatorHelpers->generator_nl_tab(1, 5), $formFields), $templateData);
+ $templateData = str_replace('$VALIDATION$', implode(','.$this->generatorHelpers->generator_nl_tab(1, 4), $validations), $templateData);
+
+ FileUtil::createFile($this->path.'forms/', $this->commandData->modelName.'FormComponent.vue', $templateData);
+ $this->commandData->commandComment("\nvue form created: ");
+ $this->commandData->commandInfo($this->commandData->modelName.'FormComponent.vue');
+ }
+
+ private function generateFilterForm()
+ {
+
+ $filterFields = [];
+ $filterMap = [];
+
+ foreach ($this->commandData->fields as $field) {
+ if (!$field->isSearchable) {
+ continue;
+ }
+
+
+ $filterMap[] = $field->name.": ''";
+
+ $fieldTemplate = $this->generatorHelpers->get_template('Fields.filter_field');
+
+ $fieldInputTemplate = HTMLFieldGenerator::generateHTML($field);
+
+ if($field->htmlType === 'selectTable'){
+ $fieldTemplate = str_replace('$v.parameters.$FIELD_NAME$', '$v.parameters.'.$field->name, $fieldTemplate);
+ $fieldTemplate = str_replace('$FIELD_NAME$', Str::replaceLast('_id', '', $field->name), $fieldTemplate);
+ }
+
+ $fieldTemplate = str_replace('$FIELD$', $fieldInputTemplate, $fieldTemplate);
+
+
+ if (!empty($fieldTemplate)) {
+ $fieldTemplate = $this->generatorHelpers->fill_template_with_field_data(
+ $this->commandData->dynamicVars,
+ $this->commandData->fieldNamesMapping,
+ $fieldTemplate,
+ $field
+ );
+
+ $filterFields[] = $fieldTemplate;
+ }
+ }
+
+ $templateData = $this->generatorHelpers->get_template('VueJs.filter_component');
+ $templateData = $this->generatorHelpers->fill_template($this->commandData->dynamicVars, $templateData);
+
+ $templateData = str_replace('$FIELDS$', implode("\n", $filterFields), $templateData);
+ $templateData = str_replace('$REQUEST_FIELDS$', implode(','.$this->generatorHelpers->generator_nl_tab(1, 5), $filterMap), $templateData);
+
+ FileUtil::createFile($this->path.'forms/', $this->commandData->modelName.'FiltersComponent.vue', $templateData);
+ $this->commandData->commandComment("\nvue filter created: ");
+ $this->commandData->commandInfo($this->commandData->modelName.'FiltersComponent.vue');
+ }
+
+
+
+ public function rollback()
+ {
+ foreach(['elements', 'forms'] as $folderType){
+ if($folderType === 'elements'){
+ if ($this->rollbackFile($this->path.$folderType.'/', $this->commandData->modelName.'Component.vue')) {
+ $this->commandData->commandComment('Vue element file deleted: '.$this->commandData->modelName.'Component.vue');
+ }
+ } else {
+ foreach(['form', 'filter'] as $type){
+ if ($this->rollbackFile($this->path.$folderType.'/', $this->commandData->modelName.str::studly($type).'Component.vue')) {
+ $this->commandData->commandComment('Vue '.$type.' file deleted: '.$this->commandData->modelName.str::studly($type).'Component.vue');
+ }
+ }
+
+ }
+ File::deleteDirectory($this->path.$folderType.'/');
+ }
+
+ File::deleteDirectory($this->path);
+ }
+}
diff --git a/app/Classes/CodeGenerator/Generators/Scaffold/WebRouteGenerator.php b/app/Classes/CodeGenerator/Generators/Scaffold/WebRouteGenerator.php
new file mode 100644
index 00000000..e32fd4d6
--- /dev/null
+++ b/app/Classes/CodeGenerator/Generators/Scaffold/WebRouteGenerator.php
@@ -0,0 +1,55 @@
+commandData = $commandData;
+ $this->path = base_path('routes/web.php');;
+ $this->routeContents = file_get_contents($this->path);
+ $this->routesTemplate = $this->generatorHelpers->get_template('Routes.web');
+ $this->routesTemplate = $this->generatorHelpers->fill_template($this->commandData->dynamicVars, $this->routesTemplate);
+ }
+
+ public function generate()
+ {
+ if (Str::contains($this->routeContents, "Route::get('/".$this->commandData->config->mSnakePlural."'")) {
+ $this->commandData->commandObj->info('Web route for '.$this->commandData->config->mName.' already exists, Skipping Adjustment.');
+
+ return;
+ }
+
+ file_put_contents($this->path, $this->routeContents.$this->routesTemplate);
+ $this->commandData->commandComment("\n".$this->commandData->config->mName.' web route added.');
+ }
+
+ public function rollback()
+ {
+ if (Str::contains($this->routeContents, $this->routesTemplate)) {
+ $this->routeContents = str_replace($this->routesTemplate, '', $this->routeContents);
+ file_put_contents($this->path, $this->routeContents);
+ $this->commandData->commandComment('web route deleted');
+ }
+ }
+}
diff --git a/app/Classes/CodeGenerator/Generators/SeederGenerator.php b/app/Classes/CodeGenerator/Generators/SeederGenerator.php
new file mode 100644
index 00000000..9161ea0f
--- /dev/null
+++ b/app/Classes/CodeGenerator/Generators/SeederGenerator.php
@@ -0,0 +1,86 @@
+commandData = $commandData;
+ $this->path = $commandData->config->pathSeeder;
+ $this->fileName = Str::studly($this->commandData->config->mPlural).'TableSeeder.php';
+ }
+
+ public function generate()
+ {
+ $templateData = $this->generatorHelpers->get_template('Seeds.model_seeder');
+
+ $templateData = $this->generatorHelpers->fill_template($this->commandData->dynamicVars, $templateData);
+
+ FileUtil::createFile($this->path, $this->fileName, $templateData);
+
+ $this->commandData->commandComment("\nSeeder created: ");
+ $this->commandData->commandInfo($this->fileName);
+ }
+
+ public function updateMainSeeder()
+ {
+ $mainSeederContent = file_get_contents($this->commandData->config->pathDatabaseSeeder);
+
+ $newSeederStatement = '$this->call('.$this->commandData->config->mPlural.'TableSeeder::class);';
+
+ if (strpos($mainSeederContent, $newSeederStatement) != false) {
+ $this->commandData->commandObj->info($this->commandData->config->mPlural.'TableSeeder entry found in DatabaseSeeder. Skipping Adjustment.');
+
+ return;
+ }
+
+ $newSeederStatement = $this->generatorHelpers->generator_tabs(2).$newSeederStatement.$this->generatorHelpers->generator_nl();
+
+ preg_match_all('/\\$this->call\\((.*);/', $mainSeederContent, $matches);
+
+ $totalMatches = count($matches[0]);
+ $lastSeederStatement = $matches[0][$totalMatches - 1];
+
+ $replacePosition = strpos($mainSeederContent, $lastSeederStatement);
+
+ $mainSeederContent = substr_replace($mainSeederContent, $newSeederStatement, $replacePosition + strlen($lastSeederStatement) + 1, 0);
+
+ file_put_contents($this->commandData->config->pathDatabaseSeeder, $mainSeederContent);
+ $this->commandData->commandComment('Main Seeder file updated.');
+ }
+
+ public function rollback()
+ {
+ if ($this->rollbackFile($this->path, $this->fileName)) {
+ $this->commandData->commandComment('Seeder file deleted: '.$this->fileName);
+ }
+
+ $mainSeederContent = file_get_contents($this->commandData->config->pathDatabaseSeeder);
+ $mainSeederContent = str_replace('$this->call('.$this->commandData->config->mPlural.'TableSeeder::class);', '', $mainSeederContent);
+ file_put_contents($this->commandData->config->pathDatabaseSeeder, $mainSeederContent);
+ $this->commandData->commandComment('Main Seeder file updated.');
+
+ }
+}
diff --git a/app/Classes/CodeGenerator/Schemas/addresses.json b/app/Classes/CodeGenerator/Schemas/addresses.json
new file mode 100644
index 00000000..138889e2
--- /dev/null
+++ b/app/Classes/CodeGenerator/Schemas/addresses.json
@@ -0,0 +1,102 @@
+[
+ {
+ "name": "company_id",
+ "dbType": "foreignId:foreign,companies,id",
+ "htmlType": "selectTable:companies:name,id",
+ "validations": "required",
+ "searchable": true,
+ "fillable": false,
+ "primary": false,
+ "inForm": false,
+ "inIndex": true,
+ "relation": "1t1,Company,company_id,id"
+ },
+ {
+ "name": "street_one",
+ "dbType": "string",
+ "htmlType": "text",
+ "validations": "required",
+ "searchable": false,
+ "fillable": true,
+ "primary": false,
+ "inForm": true,
+ "inIndex": true
+ },
+ {
+ "name": "street_two",
+ "dbType": "string:nullable",
+ "htmlType": "text",
+ "validations": "",
+ "searchable": false,
+ "fillable": true,
+ "primary": false,
+ "inForm": true,
+ "inIndex": true
+ },
+ {
+ "name": "city",
+ "dbType": "string",
+ "htmlType": "text",
+ "validations": "required",
+ "searchable": false,
+ "fillable": true,
+ "primary": false,
+ "inForm": true,
+ "inIndex": true
+ },
+ {
+ "name": "state",
+ "dbType": "string",
+ "htmlType": "text",
+ "validations": "required",
+ "searchable": false,
+ "fillable": true,
+ "primary": false,
+ "inForm": true,
+ "inIndex": true
+ },
+ {
+ "name": "post_code",
+ "dbType": "string",
+ "htmlType": "text",
+ "validations": "required",
+ "searchable": false,
+ "fillable": true,
+ "primary": false,
+ "inForm": true,
+ "inIndex": true
+ },
+ {
+ "name": "country",
+ "dbType": "string",
+ "htmlType": "text",
+ "validations": "required",
+ "searchable": false,
+ "fillable": true,
+ "primary": false,
+ "inForm": true,
+ "inIndex": true
+ },
+ {
+ "name": "default",
+ "dbType": "integer",
+ "htmlType": "number",
+ "validations": "required",
+ "searchable": false,
+ "fillable": false,
+ "primary": false,
+ "inForm": false,
+ "inIndex": false
+ },
+ {
+ "name": "billing",
+ "dbType": "integer",
+ "htmlType": "number",
+ "validations": "required",
+ "searchable": false,
+ "fillable": false,
+ "primary": false,
+ "inForm": false,
+ "inIndex": false
+ }
+]
diff --git a/app/Classes/CodeGenerator/Schemas/companies.json b/app/Classes/CodeGenerator/Schemas/companies.json
new file mode 100644
index 00000000..53a80132
--- /dev/null
+++ b/app/Classes/CodeGenerator/Schemas/companies.json
@@ -0,0 +1,35 @@
+[
+ {
+ "name": "reference_no",
+ "dbType": "integer:unique",
+ "htmlType": "number",
+ "validations": "required",
+ "searchable": true,
+ "fillable": false,
+ "primary": false,
+ "inForm": false,
+ "inIndex": true
+ },
+ {
+ "name": "name",
+ "dbType": "string",
+ "htmlType": "text",
+ "validations": "required",
+ "searchable": true,
+ "fillable": true,
+ "primary": false,
+ "inForm": true,
+ "inIndex": true
+ },
+ {
+ "name": "type",
+ "dbType": "integer",
+ "htmlType": "number",
+ "validations": "required",
+ "searchable": true,
+ "fillable": true,
+ "primary": false,
+ "inForm": true,
+ "inIndex": true
+ }
+]
diff --git a/app/Classes/CodeGenerator/Schemas/contacts.json b/app/Classes/CodeGenerator/Schemas/contacts.json
new file mode 100644
index 00000000..7b8cf0fc
--- /dev/null
+++ b/app/Classes/CodeGenerator/Schemas/contacts.json
@@ -0,0 +1,70 @@
+[
+ {
+ "name": "company_id",
+ "dbType": "foreignId:foreign,companies,id",
+ "htmlType": "selectTable:companies:name,id",
+ "validations": "required",
+ "searchable": true,
+ "fillable": false,
+ "primary": false,
+ "inForm": false,
+ "inIndex": true,
+ "relation": "1t1,Company,company_id,id"
+ },
+ {
+ "name": "name",
+ "dbType": "string",
+ "htmlType": "text",
+ "validations": "required",
+ "searchable": false,
+ "fillable": true,
+ "primary": false,
+ "inForm": true,
+ "inIndex": true
+ },
+ {
+ "name": "designation",
+ "dbType": "string:nullable",
+ "htmlType": "text",
+ "validations": "",
+ "searchable": false,
+ "fillable": true,
+ "primary": false,
+ "inForm": true,
+ "inIndex": true
+ },
+ {
+ "name": "email",
+ "dbType": "string:nullable",
+ "htmlType": "text",
+ "validations": "",
+ "searchable": false,
+ "fillable": true,
+ "primary": false,
+ "inForm": true,
+ "inIndex": true
+ }
+,
+ {
+ "name": "phone",
+ "dbType": "string:nullable",
+ "htmlType": "text",
+ "validations": "",
+ "searchable": false,
+ "fillable": true,
+ "primary": false,
+ "inForm": true,
+ "inIndex": true
+ },
+ {
+ "name": "wechat_id",
+ "dbType": "string:nullable",
+ "htmlType": "text",
+ "validations": "",
+ "searchable": false,
+ "fillable": true,
+ "primary": false,
+ "inForm": true,
+ "inIndex": true
+ }
+]
diff --git a/app/Classes/CodeGenerator/Stubs/Docs/model.stub b/app/Classes/CodeGenerator/Stubs/Docs/model.stub
new file mode 100644
index 00000000..31e51baa
--- /dev/null
+++ b/app/Classes/CodeGenerator/Stubs/Docs/model.stub
@@ -0,0 +1,6 @@
+/**
+ * Class $MODEL_NAME$
+ * @package $NAMESPACE_MODEL$
+ * @version $GENERATE_DATE$
+ *
+$PHPDOC$ */
\ No newline at end of file
diff --git a/app/Classes/CodeGenerator/Stubs/Factories/model_factory.stub b/app/Classes/CodeGenerator/Stubs/Factories/model_factory.stub
new file mode 100644
index 00000000..94781477
--- /dev/null
+++ b/app/Classes/CodeGenerator/Stubs/Factories/model_factory.stub
@@ -0,0 +1,12 @@
+define($NAMESPACE_MODEL$\$MODEL_NAME$::class, function (Faker $faker) {
+
+ return [
+ $FIELDS$
+ ];
+});
diff --git a/app/Classes/CodeGenerator/Stubs/Fields/date.stub b/app/Classes/CodeGenerator/Stubs/Fields/date.stub
new file mode 100644
index 00000000..8e7cfd97
--- /dev/null
+++ b/app/Classes/CodeGenerator/Stubs/Fields/date.stub
@@ -0,0 +1 @@
+