├── LICENSE.md ├── README.md ├── composer.json ├── docs ├── CRUD.md ├── configuration.md └── index.md ├── mkdocs.yml └── src ├── Commands ├── AbstractCommand.php ├── BreadcrumbCommand.php ├── ControllerCommand.php ├── CrudGenerator.php ├── FactoryCommand.php ├── FromExistingCommand.php ├── LangCommand.php ├── MigrationCommand.php ├── MigrationPivotCommand.php ├── ModelCommand.php ├── NavCommand.php ├── PackageCommand.php ├── RequestsCommand.php ├── ResourcesCommand.php ├── RouteCommand.php ├── TestsCommand.php └── ViewCommand.php ├── Config └── ha-generator.php ├── GeneratorException.php ├── GeneratorServiceProvider.php ├── Parsers ├── MigrationNameParser.php └── SchemaParser.php ├── SyntaxBuilders ├── AbstractSintaxBuilder.php ├── ControllerApiSyntaxBuilder.php ├── FactorySintaxeBuilder.php ├── LangSyntaxBuilder.php ├── MigrationSyntaxBuilder.php ├── ModelSyntaxBuilder.php ├── RequestSyntaxBuilder.php ├── TestSyntaxBuilder.php └── ViewSyntaxBuilder.php └── stubs ├── app ├── ApiModel.stub ├── ColumnModel.stub ├── ForeignModel.stub ├── Http │ ├── Controllers │ │ ├── ApiController.stub │ │ ├── WebController.stub │ │ └── WebControllerDatatables.stub │ ├── Requests │ │ └── Request.stub │ └── Resources │ │ ├── Collection.stub │ │ └── Resource.stub ├── SearchablesModel.stub ├── WebModel.stub └── WebModelDatatables.stub ├── database ├── factories │ └── factory.stub └── migrations │ ├── migration.stub │ ├── pivot.stub │ ├── schema-change.stub │ └── schema-create.stub ├── packages ├── README.stub ├── composer.stub ├── gitignore.stub ├── phpunit.stub ├── src │ ├── Config │ │ └── config.stub │ ├── Providers │ │ ├── RouteServiceProvider.stub │ │ └── ServiceProvider.stub │ └── Routes │ │ ├── api.stub │ │ └── web.stub └── tests │ └── TestCase.stub ├── resources ├── lang │ └── lang.stub └── views │ ├── create.blade.stub │ ├── delete.blade.stub │ ├── edit.blade.stub │ ├── elements │ ├── check.blade.stub │ ├── date.blade.stub │ ├── decimal.blade.stub │ ├── number.blade.stub │ ├── radio.blade.stub │ ├── select.blade.stub │ ├── text.blade.stub │ └── textarea.blade.stub │ ├── index.blade.stub │ ├── indexDatatables.blade.stub │ ├── layouts │ └── nav.stub │ └── show.blade.stub └── tests └── Feature ├── ApiTest.stub └── WebTest.stub /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) Laravelha 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 6 | 7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 10 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Laravelha Generator 2 | [Laravel](https://laravel.com/) [RAD](https://pt.wikipedia.org/wiki/Desenvolvimento_r%C3%A1pido_de_aplica%C3%A7%C3%B5es) Package based on [Laravel-5-Generators-Extended](https://github.com/laracasts/Laravel-5-Generators-Extended) 3 | 4 | ## Install 5 | After install fresh Laravel application: 6 | 7 | 1. Install preset `composer require laravelha/preset-api --dev` or `composer require laravelha/preset-web --dev` 8 | 2. Run preset `php artisan preset ha-api` or `php artisan preset ha-web --option=auth` 9 | 3. Via `composer require laravelha/generator --dev` 10 | 4. Run `php artisan ha-generator: ` to create automatically generated code. 11 | 5. The following commands are available.: 12 | ```shell script 13 | ha-generator:migration "Create a new migration class and apply schema at the same time" 14 | ha-generator:model "Create a new model class and apply schema at the same time" 15 | ha-generator:factory "Create a new factory class and apply schema at the same time" 16 | ha-generator:requests "Create a new requests class and apply schema at the same time" 17 | ha-generator:controller "Create a new controller and resources for api" 18 | ha-generator:resources "Create a new resources class and apply schema at the same time" 19 | ha-generator:route "Insert new resources routes" 20 | ha-generator:test "Create a new feature test and apply schema at the same time" 21 | ha-generator:lang "Create a new lang resource and apply schema at the same time" 22 | ha-generator:view "Create a new views resource and apply schema at the same time" 23 | ha-generator:breadcrumb "Insert new resources breadcrumb" 24 | ha-generator:nav "Insert new nav item" 25 | ha-generator:crud "Run all commands" 26 | ha-generator:existing:crud "Run all commands from a existing database" 27 | ha-generator:package "Create scaffolding structure to packages" 28 | ``` 29 | 6. For more information for each command use: 30 | `php artisan help ha-generator:` 31 | 32 | ## Happy way 33 | This is my approach to use it. 34 | 35 | 1. Install laravel fresh application 36 | ```shell script 37 | composer create-project --prefer-dist laravel/laravel blog && cd blog 38 | ``` 39 | 40 | 2. Make the first commit 41 | ```shell script 42 | git init 43 | git add . 44 | git commit -m 'feat: install laravel fresh app' 45 | ``` 46 | 47 | 3. Install Laravelha/Preset for your case 48 | ```shell script 49 | composer require laravelha/preset-web --dev 50 | ``` 51 | 52 | 4. Run preset 53 | ```shell script 54 | php artisan preset ha-web --option=auth 55 | ``` 56 | 57 | 5. Make the commit 58 | ```shell script 59 | git add . 60 | git commit -m 'feat: install and run laravel laravelha/preset-web with auth' 61 | ``` 62 | 63 | 6. Install generator and publish config 64 | ```shell script 65 | composer require laravelha/generator --dev 66 | php artisan vendor:publish --tag=ha-generator 67 | ``` 68 | 69 | 7. Run crud generator 70 | ```shell script 71 | php artisan ha-generator:crud Category -s 'title:string(150), description:text:nullable, published_at:timestamp:nullable' 72 | ``` 73 | 74 | 8. Commit then 75 | ```shell script 76 | git add . 77 | git commit -m 'feat: create category crud by generator' 78 | ``` 79 | 80 | 9. Run other crud generator 81 | ```shell script 82 | php artisan ha-generator:crud Post -s 'title:string(150), content:text, published_at:timestamp:nullable, category_id:unsignedBigInteger:foreign' 83 | ``` 84 | 85 | 10. Commit last crud 86 | ```shell script 87 | git add . 88 | git commit -m 'feat: create post crud by generator' 89 | ``` 90 | 91 | > It is very important that the stage is clean before running the generator, because if you give up what was generated it is possible to undo completely with `git clean -fd; git checkout .` 92 | 93 | 94 | > Every command generated is store on /storage/logs, if you need detailer each command within crud, use the option `--log-details` 95 | 96 | 97 | ## Auto generated structure 98 | 99 | ``` 100 | app/ 101 | ├── .php 102 | │ 103 | └── Http 104 | ├── Controllers 105 | | ├── Auth 106 | | ├── IndexController.php 107 | | └── Controller.php 108 | | 109 | └── Requests 110 | └── Requests 111 | ├── StoreRequest.php 112 | └── UpdateRequest.php 113 | config/ 114 | └── ha-generator.php 115 | 116 | database 117 | ├── factories 118 | | └── Factory.php 119 | | 120 | └── migrations 121 | └── YYYY_MM_DD_HHmmSS_create__table.php 122 | 123 | resources 124 | ├── lang/pt-br 125 | | └── .php 126 | | 127 | └── views 128 | └── 129 | ├── create.blade 130 | ├── delete.blade 131 | ├── edit.blade 132 | ├── index.blade 133 | └── show.blade 134 | 135 | routes 136 | ├── api.php 137 | ├── breadcrumbs.php 138 | └── web.php 139 | 140 | ``` 141 | 142 | ## Examples 143 | [API](https://github.com/laravelha/poc-api) api generated from generator 144 | [WEB](https://github.com/laravelha/poc-api) web application generated from generator 145 | 146 | ## Screenshots 147 | ![API](/.github/images/api.jpeg) 148 | ![WEB](/.github/images/web.jpeg) 149 | 150 | 151 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "laravelha/generator", 3 | "description": "Laravelha Generator", 4 | "keywords": ["RAD", "Laravel", "Generator"], 5 | "license": "MIT", 6 | "authors": [ 7 | { 8 | "name": "Pedro Augusto", 9 | "email": "pedro.e@ufv.br" 10 | } 11 | ], 12 | "require": { 13 | "doctrine/dbal": "^2.10", 14 | "illuminate/console": "^8.0", 15 | "illuminate/filesystem": "^8.0", 16 | "illuminate/support": "^8.0", 17 | "laravelha/support": "^1.0", 18 | "orangehill/iseed": "^3.0", 19 | "ext-dom": "*" 20 | }, 21 | "autoload": { 22 | "psr-4": { 23 | "Laravelha\\Generator\\": "src/" 24 | } 25 | }, 26 | "extra": { 27 | "laravel": { 28 | "providers": [ 29 | "Laravelha\\Generator\\GeneratorServiceProvider" 30 | ] 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /docs/CRUD.md: -------------------------------------------------------------------------------- 1 | #Basic Crud 2 | 3 | ## Generating a basic Category CRUD 4 | To generate a basic CRUD run: 5 | ``` 6 | php artisan ha-generator:crud Category -s 'title:string(150), description:text:nullable, published_at:timestamp:nullable' 7 | ``` 8 | Then you will be prompted to generate a Migration, Model, Controller, Tests, Factory, Routes and Requests. 9 | ``` 10 | Do you wish to create the migration? [Y/n] 11 | ``` 12 | Accept all and your files will be created. If you do not wish to be prompted , you can run the past command with the -y flag: 13 | ``` 14 | php artisan ha-generator:crud Category -s 'title:string(150), description:text:nullable, published_at:timestamp:nullable -y' 15 | ``` 16 | And that's it! 17 | -------------------------------------------------------------------------------- /docs/configuration.md: -------------------------------------------------------------------------------- 1 | ## Publish configuration files 2 | To modify the generator configuration, first you must publish the file. Run: 3 | ``` 4 | php artisan vendor:publish --tag=ha-generator 5 | ``` 6 | -------------------------------------------------------------------------------- /docs/index.md: -------------------------------------------------------------------------------- 1 | # Getting Started 2 | ## Introduction 3 | **Laravelha generator** is an open source code generation tool (MIT) made to speed up your development and make your life easier. 4 | Stop writing boilerplate code, with a unique command you can build a whole CRUD (views included), using 5 | Bootstrap and soon Tailwindcss. 6 | ## Installation 7 | After a fresh laravel application install, run the following command: 8 | ``` 9 | composer require laravelha/preset-api --dev 10 | ``` 11 | Or if you're building an api run: 12 | ``` 13 | composer require laravelha/preset-web --dev 14 | ``` 15 | and finally: 16 | ``` 17 | composer require laravelha/generator --dev 18 | ``` 19 | Congratulations! Laravelha is installed. 20 | ## Running commands 21 | ``` 22 | ha-generator:migration "Create a new migration class and apply schema at the same time" 23 | ha-generator:model "Create a new model class and apply schema at the same time" 24 | ha-generator:factory "Create a new factory class and apply schema at the same time" 25 | ha-generator:requests "Create a new requests class and apply schema at the same time" 26 | ha-generator:controller "Create a new controller and resources for api" 27 | ha-generator:resources "Create a new resources class and apply schema at the same time" 28 | ha-generator:route "Insert new resources routes" 29 | ha-generator:test "Create a new feature test and apply schema at the same time" 30 | ha-generator:lang "Create a new lang resource and apply schema at the same time" 31 | ha-generator:view "Create a new views resource and apply schema at the same time" 32 | ha-generator:breadcrumb "Insert new resources breadcrumb" 33 | ha-generator:nav "Insert new nav item" 34 | ha-generator:crud "Run all commands" 35 | ha-generator:existing:crud "Run all commands from a existing database" 36 | ha-generator:package "Create scaffolding structure to packages" 37 | ``` 38 | For more information, use: 39 | ``` 40 | php artisan help ha-generator: 41 | ``` 42 | -------------------------------------------------------------------------------- /mkdocs.yml: -------------------------------------------------------------------------------- 1 | site_name: Laravelha Docs 2 | 3 | theme: 4 | name: material 5 | -------------------------------------------------------------------------------- /src/Commands/AbstractCommand.php: -------------------------------------------------------------------------------- 1 | files = $files; 80 | $this->composer = $composer; 81 | } 82 | 83 | /** 84 | * Set ModelName, TableName, RouteName and ObjectName by argument. 85 | * 86 | * @return void 87 | */ 88 | protected function setNames(): void 89 | { 90 | $this->modelName = Str::studly($this->argument('name')); 91 | $this->namespace = 'App'; 92 | $this->tableName = Str::plural(Str::snake($this->modelName)); 93 | $this->routeName = Str::kebab(Str::plural($this->modelName)); 94 | $this->objectName = Str::snake($this->modelName); 95 | 96 | if ($this->hasPackage()) { 97 | $this->packageName = Str::studly($this->argument('package')); 98 | $this->namespace = config('ha-generator.packagesNamespace').'\\'.$this->packageName; 99 | $this->packageFolder = Str::kebab($this->packageName); 100 | $this->packageRoute = Str::plural($this->packageFolder); 101 | $this->packagePath = config('ha-generator.packagesFolder').'/'.$this->packageFolder; 102 | } 103 | } 104 | 105 | /** 106 | * Build the directory for the command result if necessary. 107 | * 108 | * @param string $path 109 | */ 110 | protected function makeDirectory($path): void 111 | { 112 | if (!$this->files->isDirectory(dirname($path))) { 113 | $this->files->makeDirectory(dirname($path), 0755, true, true); 114 | } 115 | } 116 | 117 | /** 118 | * Replace the namespace in the stub. 119 | * 120 | * @param string $stub 121 | * 122 | * @return AbstractCommand 123 | */ 124 | protected function replaceNamespace(string &$stub): AbstractCommand 125 | { 126 | $stub = str_replace('{{namespace}}', $this->namespace, $stub); 127 | 128 | return $this; 129 | } 130 | 131 | /** 132 | * Replace the namespace in the stub. 133 | * 134 | * @param string $stub 135 | * 136 | * @return AbstractCommand 137 | */ 138 | protected function replaceTestNamespace(string &$stub): AbstractCommand 139 | { 140 | $namespace = $this->namespace === 'App' ? 'Tests' : $this->namespace.'\Tests'; 141 | 142 | $stub = str_replace('{{namespace}}', $namespace, $stub); 143 | 144 | return $this; 145 | } 146 | 147 | /** 148 | * Replace the model namespace in the stub. 149 | * 150 | * @param string $stub 151 | * 152 | * @return AbstractCommand 153 | */ 154 | protected function replaceModelNamespace(string &$stub): AbstractCommand 155 | { 156 | $namespace = $this->hasPackage() ? $this->namespace.'\\Models' : config('ha-generator.modelNamespace'); 157 | 158 | $stub = str_replace('{{modelNamespace}}', $namespace, $stub); 159 | 160 | return $this; 161 | } 162 | 163 | /** 164 | * Replace the model name in the stub. 165 | * 166 | * @param string $stub 167 | * 168 | * @return AbstractCommand 169 | */ 170 | protected function replaceModelName(string &$stub): AbstractCommand 171 | { 172 | $stub = str_replace('{{modelName}}', $this->modelName, $stub); 173 | 174 | return $this; 175 | } 176 | 177 | /** 178 | * Replace the class name in the stub. 179 | * 180 | * @param string $stub 181 | * 182 | * @return AbstractCommand 183 | */ 184 | protected function replaceClassName(string &$stub): AbstractCommand 185 | { 186 | $className = ucwords(Str::camel($this->argument('name'))); 187 | 188 | $stub = str_replace('{{class}}', $className, $stub); 189 | 190 | return $this; 191 | } 192 | 193 | /** 194 | * Replace the table name in the stub. 195 | * 196 | * @param string $stub 197 | * 198 | * @return AbstractCommand 199 | */ 200 | protected function replaceTableName(string &$stub): AbstractCommand 201 | { 202 | $stub = str_replace('{{tableName}}', $this->tableName, $stub); 203 | 204 | return $this; 205 | } 206 | 207 | /** 208 | * Replace the route name in the stub. 209 | * 210 | * @param string $stub 211 | * 212 | * @return AbstractCommand 213 | */ 214 | protected function replacePackageRouteName(string &$stub): AbstractCommand 215 | { 216 | $packageRouteName = $this->packageRoute ? '/'.$this->packageRoute : ''; 217 | 218 | $stub = str_replace('{{packageRouteName}}', $packageRouteName, $stub); 219 | 220 | return $this; 221 | } 222 | 223 | /** 224 | * Replace the route name in the stub. 225 | * 226 | * @param string $stub 227 | * 228 | * @return AbstractCommand 229 | */ 230 | protected function replaceRouteName(string &$stub): AbstractCommand 231 | { 232 | $stub = str_replace('{{routeName}}', $this->routeName, $stub); 233 | 234 | return $this; 235 | } 236 | 237 | /** 238 | * Replace the object name in the stub. 239 | * 240 | * @param string $stub 241 | * 242 | * @return AbstractCommand 243 | */ 244 | protected function replaceObjectName(string &$stub): AbstractCommand 245 | { 246 | $stub = str_replace('{{objectName}}', $this->objectName, $stub); 247 | 248 | return $this; 249 | } 250 | 251 | /** 252 | * Get the path to where we should store the command result. 253 | * 254 | * @param array $args 255 | * 256 | * @return string 257 | */ 258 | abstract protected function getPath(...$args): string; 259 | 260 | /** 261 | * Compile the command result stub. 262 | * 263 | * @return string 264 | */ 265 | abstract protected function compileStub(): string; 266 | 267 | /** 268 | * Log command. 269 | * 270 | * @return void 271 | */ 272 | protected function writeLog(): void 273 | { 274 | if (!$this->option('no-log')) { 275 | $log = 'php artisan '.$this->name.' '.$this->argument('name'); 276 | $log .= ($this->hasArgument('package') and $this->argument('package')) ? ' '.$this->argument('package') : ''; 277 | $log .= ($this->hasArgument('view') and $this->argument('view')) ? ' '.$this->argument('view') : ''; 278 | $log .= ($this->hasArgument('folder') and $this->argument('folder')) ? ' '.$this->argument('folder') : ''; 279 | $log .= ($this->hasOption('api') and $this->option('api')) ? ' -a' : ''; 280 | $log .= ($this->hasOption('schema') and $this->option('schema')) ? " -s '".$this->option('schema')."'" : ''; 281 | $log .= ($this->hasOption('parent') and $this->option('parent')) ? " -p '".$this->option('parent')."'" : ''; 282 | $log .= ($this->hasOption('ownarg') and $this->option('ownarg')) ? ' -o' : ''; 283 | $log .= ($this->hasOption('argparent') and $this->option('argparent')) ? ' -ar' : ''; 284 | 285 | $this->files->append(storage_path('logs/'.config('ha-generator.logFile').'-'.now()->format('Y-m-d').'.log'), $log.PHP_EOL); 286 | } 287 | } 288 | 289 | /** 290 | * @return bool 291 | */ 292 | protected function hasPackage(): bool 293 | { 294 | return $this->hasArgument('package') and $this->argument('package'); 295 | } 296 | 297 | /** 298 | * @return mixed 299 | */ 300 | protected function resolveStubPath($stub) 301 | { 302 | return file_exists($customPath = base_path('stubs/ha-generator/'.trim($stub, '/'))) && config('ha-generator.customStubs') 303 | ? $customPath 304 | : static::STUB_DIR.$stub; 305 | } 306 | } 307 | -------------------------------------------------------------------------------- /src/Commands/BreadcrumbCommand.php: -------------------------------------------------------------------------------- 1 | setNames(); 40 | 41 | $this->viewName = strtolower($this->argument('view')); 42 | 43 | if (!$this->files->exists($path = $this->getPath())) { 44 | $this->error('Breadcrumb file not found'); 45 | 46 | return; 47 | } 48 | 49 | $this->files->append($path, $this->compileStub()); 50 | 51 | $this->line("Inserted Breadcrumb: {$this->viewName}"); 52 | 53 | $this->writeLog(); 54 | 55 | $this->composer->dumpAutoloads(); 56 | } 57 | 58 | /** 59 | * Get the path to where we should store the breadcrumb. 60 | * 61 | * @param array $args 62 | * 63 | * @return string 64 | */ 65 | protected function getPath(...$args): string 66 | { 67 | return base_path('routes/breadcrumbs.php'); 68 | } 69 | 70 | /** 71 | * Compile the breadcrumb stub. 72 | * 73 | * @return string 74 | */ 75 | protected function compileStub(): string 76 | { 77 | return $stub = $this->option('parent') ? $this->compileWithParentStub() : $this->compileWithoutParentStub(); 78 | } 79 | 80 | /** 81 | * Compile the breadcrumb without parent stub. 82 | * 83 | * @return string 84 | */ 85 | protected function compileWithParentStub(): string 86 | { 87 | $parentName = strtolower($this->option('parent')); 88 | 89 | $stub = $this->option('ownarg') 90 | ? PHP_EOL.'Breadcrumbs::for(\'{{routeName}}.'.$this->viewName.'\', function ($breadcrumbs, ${{objectName}}) {'.PHP_EOL 91 | : PHP_EOL.'Breadcrumbs::for(\'{{routeName}}.'.$this->viewName.'\', function ($breadcrumbs) {'.PHP_EOL; 92 | 93 | $stub .= $this->option('argparent') 94 | ? ' $breadcrumbs->parent(\'{{routeName}}.'.$parentName.'\', ${{objectName}});'.PHP_EOL 95 | : ' $breadcrumbs->parent(\'{{routeName}}.'.$parentName.'\');'.PHP_EOL; 96 | 97 | $stub .= $this->option('ownarg') 98 | ? ' $breadcrumbs->push(Lang::get(\'{{routeName}}.'.$this->viewName.'\', [\'{{objectName}}\' => ${{objectName}}->id]), route(\'{{routeName}}.'.$this->viewName.'\', ${{objectName}}->id));'.PHP_EOL. 99 | '});'.PHP_EOL 100 | : ' $breadcrumbs->push(Lang::get(\'{{routeName}}.'.$this->viewName.'\'), route(\'{{routeName}}.'.$this->viewName.'\'));'.PHP_EOL. 101 | '});'.PHP_EOL; 102 | 103 | $this->replaceRouteName($stub) 104 | ->replaceObjectName($stub); 105 | 106 | return $stub; 107 | } 108 | 109 | /** 110 | * Compile the breadcrumb without parent stub. 111 | * 112 | * @return string 113 | */ 114 | protected function compileWithoutParentStub(): string 115 | { 116 | $stub = PHP_EOL.'Breadcrumbs::for(\'{{routeName}}.'.$this->viewName.'\', function ($breadcrumbs) {'.PHP_EOL. 117 | ' $breadcrumbs->push(Lang::get(\'{{routeName}}.'.$this->viewName.'\'), route(\'{{routeName}}.'.$this->viewName.'\'));'.PHP_EOL. 118 | '});'.PHP_EOL; 119 | 120 | $this->replaceRouteName($stub); 121 | 122 | return $stub; 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /src/Commands/ControllerCommand.php: -------------------------------------------------------------------------------- 1 | setNames(); 46 | 47 | if ($this->files->exists($path = $this->getPath())) { 48 | $this->error('Controller already exists!'); 49 | 50 | return; 51 | } 52 | 53 | $this->files->put($path, $this->compileStub()); 54 | 55 | $filename = pathinfo($path, PATHINFO_FILENAME); 56 | $this->line("Created Controller: {$filename}"); 57 | 58 | $this->writeLog(); 59 | 60 | $this->composer->dumpAutoloads(); 61 | } 62 | 63 | /** 64 | * Get the path to where we should store the controller. 65 | * 66 | * @param array $args 67 | * 68 | * @return string 69 | */ 70 | protected function getPath(...$args): string 71 | { 72 | if ($this->hasPackage()) { 73 | $this->makeDirectory($path = $this->packagePath."/src/Http/Controllers/{$this->modelName}Controller.php"); 74 | 75 | return $path; 76 | } 77 | 78 | return app_path("Http/Controllers/{$this->modelName}Controller.php"); 79 | } 80 | 81 | /** 82 | * Compile the controller stub. 83 | * 84 | * @throws FileNotFoundException 85 | * 86 | * @return string 87 | */ 88 | protected function compileStub(): string 89 | { 90 | return $stub = $this->option('api') ? $this->compileApiControllerStub() : $this->compileWebControllerStub(); 91 | } 92 | 93 | /** 94 | * Compile the controller api stub. 95 | * 96 | * @throws FileNotFoundException 97 | * 98 | * @return string 99 | */ 100 | protected function compileApiControllerStub(): string 101 | { 102 | $stub = $this->files->get($this->resolveStubPath('/app/Http/Controllers/ApiController.stub')); 103 | 104 | $this->apiResources(); 105 | 106 | $this 107 | ->replaceSchema($stub) 108 | ->replaceNamespace($stub) 109 | ->replaceModelNamespace($stub) 110 | ->replaceModelName($stub) 111 | ->replaceTableName($stub) 112 | ->replacePackageRouteName($stub) 113 | ->replaceRouteName($stub) 114 | ->replaceObjectName($stub); 115 | 116 | return $stub; 117 | } 118 | 119 | /** 120 | * Compile the controller web stub. 121 | * 122 | * @throws FileNotFoundException 123 | * 124 | * @return string 125 | */ 126 | protected function compileWebControllerStub(): string 127 | { 128 | $datatables = $this->option('datatables') ? 'Datatables' : ''; 129 | 130 | $stub = $this->files->get($this->resolveStubPath("/app/Http/Controllers/WebController{$datatables}.stub")); 131 | 132 | $this 133 | ->replaceNamespace($stub) 134 | ->replaceModelNamespace($stub) 135 | ->replaceModelName($stub) 136 | ->replaceTableName($stub) 137 | ->replaceRouteName($stub) 138 | ->replaceObjectName($stub); 139 | 140 | return $stub; 141 | } 142 | 143 | /** 144 | * Run api resources command. 145 | * 146 | * @return void 147 | */ 148 | private function apiResources(): void 149 | { 150 | try { 151 | $params = ['name' => $this->modelName, '--no-log' => $this->option('no-log')]; 152 | $params += $this->hasPackage() ? ['package' => $this->argument('package')] : []; 153 | 154 | $this->call('ha-generator:resources', $params); 155 | } catch (\Exception $exception) { 156 | $this->warn("Não foi possível criar recursos para {$this->modelName}"); 157 | } 158 | } 159 | 160 | /** 161 | * Replace the schema for the stub. 162 | * 163 | * @param string $stub 164 | * 165 | * @return ControllerCommand 166 | */ 167 | protected function replaceSchema(string &$stub): ControllerCommand 168 | { 169 | if ($schema = $this->option('schema')) { 170 | $schema = (new SchemaParser())->parse($schema); 171 | } 172 | 173 | $stub = (new ControllerApiSyntaxBuilder())->create($schema); 174 | 175 | return $this; 176 | } 177 | } 178 | -------------------------------------------------------------------------------- /src/Commands/CrudGenerator.php: -------------------------------------------------------------------------------- 1 | option('schema')) { 53 | $this->error('The "schema" argument is required!'); 54 | 55 | return; 56 | } 57 | 58 | $this->noLogDetailed = !($this->option('log-detailed') and !$this->option('no-log')); 59 | 60 | $name = $this->argument('name'); 61 | 62 | $this->option('yes') ? $this->withoutConfirmation($name) : $this->withConfirmation($name); 63 | 64 | if (!$this->option('log-detailed')) { 65 | $this->writeLog(); 66 | } 67 | } 68 | 69 | /** 70 | * @param string $name 71 | * 72 | * @return void 73 | */ 74 | protected function migration(string $name): void 75 | { 76 | $namePluralLower = Str::plural(Str::snake($name)); 77 | 78 | try { 79 | $params = ['name' => "create_{$namePluralLower}_table", '--no-log' => $this->noLogDetailed, '--schema' => $this->option('schema')]; 80 | $params += $this->hasArgument('package') ? ['package' => $this->argument('package')] : []; 81 | 82 | $this->call('ha-generator:migration', $params); 83 | } catch (\Exception $exception) { 84 | $this->warn("Unable to create migration 'create_{$namePluralLower}_table'. {$exception->getMessage()}"); 85 | } 86 | } 87 | 88 | /** 89 | * @param string $name 90 | * 91 | * @return void 92 | */ 93 | protected function model(string $name): void 94 | { 95 | try { 96 | $params = ['name' => $name, '--no-log' => $this->noLogDetailed, '--schema' => $this->option('schema'), '--api' => $this->option('api')]; 97 | $params += $this->hasArgument('package') ? ['package' => $this->argument('package')] : []; 98 | $params += $this->option('datatables') ? ['--datatables' => $this->option('datatables')] : []; 99 | 100 | $this->call('ha-generator:model', $params); 101 | } catch (\Exception $exception) { 102 | $this->warn("Unable to create model {$name}. {$exception->getMessage()}"); 103 | } 104 | } 105 | 106 | /** 107 | * @param string $name 108 | * 109 | * @return void 110 | */ 111 | protected function factory(string $name): void 112 | { 113 | try { 114 | $params = ['name' => $name, '--no-log' => $this->noLogDetailed, '--schema' => $this->option('schema')]; 115 | $params += $this->hasArgument('package') ? ['package' => $this->argument('package')] : []; 116 | 117 | $this->call('ha-generator:factory', $params); 118 | } catch (\Exception $exception) { 119 | $this->warn("Unable to create factory {$name}Factory. {$exception->getMessage()}"); 120 | } 121 | } 122 | 123 | /** 124 | * @param string $name 125 | * 126 | * @return void 127 | */ 128 | protected function requests(string $name): void 129 | { 130 | try { 131 | $params = ['name' => $name, '--no-log' => $this->noLogDetailed, '--schema' => $this->option('schema')]; 132 | $params += $this->hasArgument('package') ? ['package' => $this->argument('package')] : []; 133 | 134 | $this->call('ha-generator:request', $params); 135 | } catch (\Exception $exception) { 136 | $this->warn("Unable to create requests {$name}Request. {$exception->getMessage()}"); 137 | } 138 | } 139 | 140 | /** 141 | * @param string $name 142 | * 143 | * @return void 144 | */ 145 | protected function controller(string $name): void 146 | { 147 | try { 148 | $params = ['name' => $name, '--no-log' => $this->noLogDetailed, '--schema' => $this->option('schema'), '--api' => $this->option('api')]; 149 | $params += $this->hasArgument('package') ? ['package' => $this->argument('package')] : []; 150 | $params += $this->option('datatables') ? ['--datatables' => $this->option('datatables')] : []; 151 | 152 | $this->call('ha-generator:controller', $params); 153 | } catch (\Exception $exception) { 154 | $this->warn("Unable to create controller {$name}. {$exception->getMessage()}"); 155 | $this->warn($exception->getMessage()); 156 | } 157 | } 158 | 159 | /** 160 | * @param string $name 161 | * 162 | * @return void 163 | */ 164 | protected function routes(string $name): void 165 | { 166 | try { 167 | $params = ['name' => $name, '--no-log' => $this->noLogDetailed, '--api' => $this->option('api')]; 168 | $params += $this->hasArgument('package') ? ['package' => $this->argument('package')] : []; 169 | $params += $this->option('datatables') ? ['--datatables' => $this->option('datatables')] : []; 170 | 171 | $this->call('ha-generator:route', $params); 172 | } catch (\Exception $exception) { 173 | $this->warn("Unable to create routes to {$name}"); 174 | } 175 | } 176 | 177 | /** 178 | * @param string $name 179 | * 180 | * @return void 181 | */ 182 | protected function tests(string $name): void 183 | { 184 | try { 185 | $params = ['name' => $name, '--no-log' => $this->noLogDetailed, '--schema' => $this->option('schema'), '--api' => $this->option('api')]; 186 | $params += $this->hasArgument('package') ? ['package' => $this->argument('package')] : []; 187 | 188 | $this->call('ha-generator:tests', $params); 189 | } catch (\Exception $exception) { 190 | $this->warn("Unable to create tests to {$name}"); 191 | } 192 | } 193 | 194 | /** 195 | * @param string $name 196 | * 197 | * @return void 198 | */ 199 | protected function views(string $name): void 200 | { 201 | if ($this->option('api')) { 202 | return; 203 | } 204 | 205 | $this->lang($name); 206 | $this->nav($name); 207 | 208 | // create view index 209 | $this->breadcrumbs($name); 210 | $this->viewFile($name); 211 | 212 | // create view create 213 | $this->breadcrumbs($name, 'create', 'index'); 214 | $this->viewFile($name, 'create', $this->option('schema')); 215 | 216 | // create view show 217 | $this->breadcrumbs($name, 'show', 'index'); 218 | $this->viewFile($name, 'show'); 219 | 220 | // create view edit 221 | $this->breadcrumbs($name, 'edit', 'show'); 222 | $this->viewFile($name, 'edit', $this->option('schema')); 223 | 224 | // create view delete 225 | $this->breadcrumbs($name, 'delete', 'show'); 226 | $this->viewFile($name, 'delete'); 227 | 228 | $this->info("Views '".Str::plural(strtolower($name))."' created"); 229 | $this->comment('Run "npm install && npm run dev" to complete.'); 230 | } 231 | 232 | /** 233 | * @param string $name 234 | * 235 | * @return void 236 | */ 237 | protected function viewsWithConfirmation(string $name): void 238 | { 239 | if ($this->option('api')) { 240 | return; 241 | } 242 | 243 | if ($this->confirm('Do you wish to create the lang?', true)) { 244 | $this->lang($name); 245 | } 246 | 247 | if ($this->confirm('Do you wish to create the navigation?', true)) { 248 | $this->nav($name); 249 | } 250 | 251 | if ($this->confirm('Do you wish to create the view index?', true)) { 252 | $this->breadcrumbs($name); 253 | $this->viewFile($name); 254 | } 255 | 256 | if ($this->confirm('Do you wish to create the view create?', true)) { 257 | $this->breadcrumbs($name, 'create', 'index'); 258 | $this->viewFile($name, 'create', $this->option('schema')); 259 | } 260 | 261 | if ($this->confirm('Do you wish to create the view show?', true)) { 262 | $this->breadcrumbs($name, 'show', 'index'); 263 | $this->viewFile($name, 'show'); 264 | } 265 | 266 | if ($this->confirm('Do you wish to create the view edit?', true)) { 267 | $this->breadcrumbs($name, 'edit', 'show'); 268 | $this->viewFile($name, 'edit', $this->option('schema')); 269 | } 270 | 271 | if ($this->confirm('Do you wish to create the view delete?', true)) { 272 | $this->breadcrumbs($name, 'delete', 'show'); 273 | $this->viewFile($name, 'delete'); 274 | } 275 | 276 | $this->info("Views '".Str::plural(strtolower($name))."' created"); 277 | $this->comment('Run "npm install && npm run dev" to complete.'); 278 | } 279 | 280 | /** 281 | * @param string $name 282 | * 283 | * @return void 284 | */ 285 | protected function lang(string $name): void 286 | { 287 | try { 288 | $this->call('ha-generator:lang', ['name' => $name, '--no-log' => $this->noLogDetailed, '--schema' => $this->option('schema')]); 289 | } catch (\Exception $exception) { 290 | $this->warn("Unable to create lang to {$name}. {$exception->getMessage()}"); 291 | } 292 | } 293 | 294 | /** 295 | * @param string $name 296 | * 297 | * @return void 298 | */ 299 | protected function nav(string $name): void 300 | { 301 | try { 302 | $this->call('ha-generator:nav', ['name' => $name, '--no-log' => $this->noLogDetailed]); 303 | } catch (\Exception $exception) { 304 | $this->warn("Unable to create nav to {$name}"); 305 | } 306 | } 307 | 308 | /** 309 | * @param string $name 310 | * @param string|null $view 311 | * @param string|null $parent 312 | * 313 | * @return void 314 | */ 315 | protected function breadcrumbs(string $name, string $view = null, string $parent = null): void 316 | { 317 | $params = ['name' => $name, '--no-log' => $this->noLogDetailed]; 318 | 319 | if ($view) { 320 | $params['view'] = $view; 321 | } 322 | 323 | if ($parent) { 324 | $params['--parent'] = $parent; 325 | } 326 | 327 | if ($parent != 'index') { 328 | $params['--argparent'] = true; 329 | } 330 | 331 | if ($view != 'create') { 332 | $params['--ownarg'] = true; 333 | } 334 | 335 | try { 336 | $this->call('ha-generator:breadcrumb', $params); 337 | } catch (\Exception $exception) { 338 | $this->warn("Unable to create breadcrumb to {$name} {$view}"); 339 | } 340 | } 341 | 342 | /** 343 | * @param string $name 344 | * @param string|null $view 345 | * @param string $schema 346 | * 347 | * @return void 348 | */ 349 | private function viewFile(string $name, string $view = null, string $schema = null): void 350 | { 351 | $params = ['name' => $name, '--no-log' => $this->noLogDetailed]; 352 | $params += $this->option('datatables') ? ['--datatables' => $this->option('datatables')] : []; 353 | 354 | if ($view) { 355 | $params['view'] = $view; 356 | } 357 | 358 | if ($schema) { 359 | $params['--schema'] = $schema; 360 | } 361 | 362 | try { 363 | $this->call('ha-generator:view', $params); 364 | } catch (\Exception $exception) { 365 | $this->warn("Unable to create view to {$name} {$view}. {$exception->getMessage()}"); 366 | } 367 | } 368 | 369 | /** 370 | * Log command. 371 | * 372 | * @return void 373 | */ 374 | private function writeLog(): void 375 | { 376 | if (!$this->option('no-log')) { 377 | $log = 'php artisan '.$this->name.' '.$this->argument('name'); 378 | $log .= ($this->hasOption('api') and $this->option('api')) ? ' -a' : ''; 379 | $log .= ($this->hasOption('schema') and $this->option('schema')) ? " -s '".$this->option('schema')."'" : ''; 380 | 381 | File::append(storage_path('logs/'.config('ha-generator.logFile').'-'.now()->format('Y-m-d').'.log'), $log.PHP_EOL); 382 | } 383 | } 384 | 385 | /** 386 | * @param string $name 387 | */ 388 | private function withConfirmation(string $name): void 389 | { 390 | if ($this->confirm('Do you wish to create the migration?', true)) { 391 | $this->migration($name); 392 | } 393 | 394 | if ($this->confirm('Do you wish to create the model?', true)) { 395 | $this->model($name); 396 | } 397 | 398 | if ($this->confirm('Do you wish to create the factory?', true)) { 399 | $this->factory($name); 400 | } 401 | 402 | if ($this->confirm('Do you wish to create the requests?', true)) { 403 | $this->requests($name); 404 | } 405 | 406 | if ($this->confirm('Do you wish to create the controller?', true)) { 407 | $this->controller($name); 408 | } 409 | 410 | if ($this->confirm('Do you wish to create the routes?', true)) { 411 | $this->routes($name); 412 | } 413 | 414 | if ($this->confirm('Do you wish to create the tests?', true)) { 415 | $this->tests($name); 416 | } 417 | 418 | if (!$this->option('api') and $this->confirm('Do you wish to create the views?', true)) { 419 | $this->viewsWithConfirmation($name); 420 | } 421 | } 422 | 423 | /** 424 | * @param string $name 425 | */ 426 | private function withoutConfirmation(string $name): void 427 | { 428 | $this->migration($name); 429 | $this->model($name); 430 | $this->factory($name); 431 | $this->requests($name); 432 | $this->controller($name); 433 | $this->routes($name); 434 | $this->tests($name); 435 | $this->views($name); 436 | } 437 | } 438 | -------------------------------------------------------------------------------- /src/Commands/FactoryCommand.php: -------------------------------------------------------------------------------- 1 | setNames(); 44 | 45 | if ($this->files->exists($path = $this->getPath())) { 46 | $this->error('Factory already exists!'); 47 | 48 | return; 49 | } 50 | 51 | $this->makeDirectory($path); 52 | 53 | $this->files->put($path, $this->compileStub()); 54 | 55 | $filename = pathinfo($path, PATHINFO_FILENAME); 56 | $this->line("Created Factory: {$filename}"); 57 | 58 | $this->writeLog(); 59 | 60 | $this->composer->dumpAutoloads(); 61 | } 62 | 63 | /** 64 | * Get the path to where we should store the factory. 65 | * 66 | * @param array $args 67 | * 68 | * @return string 69 | */ 70 | protected function getPath(...$args): string 71 | { 72 | if ($this->hasPackage()) { 73 | return $this->packagePath.'/'.config('ha-generator.packageFactoriesFolder')."/{$this->modelName}Factory.php"; 74 | } 75 | 76 | return database_path("factories/{$this->modelName}Factory.php"); 77 | } 78 | 79 | /** 80 | * Compile the factory stub. 81 | * 82 | * @throws FileNotFoundException 83 | * 84 | * @return string 85 | */ 86 | protected function compileStub(): string 87 | { 88 | $stub = $this->files->get($this->resolveStubPath('/database/factories/factory.stub')); 89 | 90 | $this 91 | ->replaceSchema($stub) 92 | ->replaceNamespace($stub) 93 | ->replaceModelNamespace($stub) 94 | ->replaceModelName($stub); 95 | 96 | return $stub; 97 | } 98 | 99 | /** 100 | * Replace the schema for the stub. 101 | * 102 | * @param string $stub 103 | * 104 | * @return FactoryCommand 105 | */ 106 | protected function replaceSchema(string &$stub): FactoryCommand 107 | { 108 | if ($schema = $this->option('schema')) { 109 | $schema = (new SchemaParser())->parse($schema); 110 | } 111 | 112 | $stub = (new FactorySintaxeBuilder())->create($schema); 113 | 114 | return $this; 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /src/Commands/FromExistingCommand.php: -------------------------------------------------------------------------------- 1 | 'char', 56 | 'date' => 'date', 57 | 'datetime' => 'dateTime', 58 | 'datetimetz' => 'dateTimeTz', 59 | 'biginteger' => 'bigInteger', 60 | 'bigint' => 'bigInteger', 61 | 'tinyblob' => 'binary', 62 | 'mediumblob' => 'binary', 63 | 'blob' => 'binary', 64 | 'longblob' => 'binary', 65 | 'binary' => 'binary', 66 | 'bool' => 'boolean', 67 | 'boolean' => 'boolean', 68 | 'bit' => 'boolean', 69 | 'decimal' => 'decimal', 70 | 'double' => 'double', 71 | 'enum' => 'enum', 72 | 'list' => 'enum', 73 | 'float' => 'float', 74 | 'int' => 'integer', 75 | 'integer' => 'integer', 76 | 'ipaddress' => 'ipAddress', 77 | 'json' => 'json', 78 | 'jsonb' => 'jsonb', 79 | 'longtext' => 'longText', 80 | 'macaddress' => 'macAddress', 81 | 'mediuminteger' => 'mediumInteger', 82 | 'mediumint' => 'mediumInteger', 83 | 'mediumtext' => 'mediumText', 84 | 'smallInteger' => 'smallInteger', 85 | 'smallint' => 'smallInteger', 86 | 'morphs' => 'morphs', 87 | 'string' => 'string', 88 | 'varchar' => 'string', 89 | 'nvarchar' => 'string', 90 | 'text' => 'text', 91 | 'time' => 'time', 92 | 'timetz' => 'timeTz', 93 | 'tinyinteger' => 'tinyInteger', 94 | 'tinyint' => 'tinyInteger', 95 | 'timestamp' => 'timestamp', 96 | 'timestamptz' => 'timestampTz', 97 | 'unsignedbiginteger' => 'unsignedBigInteger', 98 | 'unsignedbigint' => 'unsignedBigInteger', 99 | 'unsignedInteger' => 'unsignedInteger', 100 | 'unsignedint' => 'unsignedInteger', 101 | 'unsignedmediuminteger' => 'unsignedMediumInteger', 102 | 'unsignedmediumint' => 'unsignedMediumInteger', 103 | 'unsignedsmallinteger' => 'unsignedSmallInteger', 104 | 'unsignedsmallint' => 'unsignedSmallInteger', 105 | 'unsignedtinyinteger' => 'unsignedTinyInteger', 106 | 'uuid' => 'uuid', 107 | ]; 108 | 109 | /** 110 | * Store tables created. 111 | * 112 | * @var array 113 | */ 114 | protected $created = []; 115 | 116 | /** 117 | * Execute the console command. 118 | * 119 | * @throws \InvalidArgumentException 120 | * 121 | * @return void 122 | */ 123 | public function handle() 124 | { 125 | $this->connection = Schema::connection($this->hasOption('connection') ? $this->option('connection') : config('database.default')); 126 | 127 | $this->schemaManager = $this->connection->getConnection()->getDoctrineSchemaManager(); 128 | 129 | if ($this->option('table')) { 130 | $this->migrateTable($this->option('table')); 131 | } 132 | 133 | if (!$this->option('table')) { 134 | $this->migrateAll(); 135 | } 136 | } 137 | 138 | /** 139 | * @return void 140 | */ 141 | private function migrateAll(): void 142 | { 143 | $tables = $this->schemaManager->listTables(); 144 | 145 | foreach ($tables as $table) { 146 | if (in_array($table->getName(), array_merge(explode(',', $this->option('ignore')), ['migrations']))) { 147 | continue; 148 | } 149 | 150 | if (in_array($table->getName(), $this->created)) { 151 | continue; 152 | } 153 | 154 | $this->migrateForeignKeys($table->getForeignKeys()); 155 | 156 | $this->migrateTable($table->getName()); 157 | } 158 | } 159 | 160 | /** 161 | * @param ForeignKeyConstraint[] $foreignKeys 162 | * 163 | * @return void 164 | */ 165 | private function migrateForeignKeys(array $foreignKeys): void 166 | { 167 | foreach ($foreignKeys as $foreign) { 168 | $tableDetails = $this->schemaManager->listTableDetails($foreign->getForeignTableName()); 169 | 170 | $this->migrateForeignKeys($tableDetails->getForeignKeys()); 171 | 172 | if (in_array($foreign->getForeignTableName(), $this->created)) { 173 | continue; 174 | } 175 | 176 | $this->migrateTable($foreign->getForeignTableName()); 177 | } 178 | } 179 | 180 | /** 181 | * @param string|null $table 182 | * 183 | * @return void 184 | */ 185 | private function migrateTable(?string $table): void 186 | { 187 | $schema = ''; 188 | 189 | if ($this->connection->hasTable($table)) { 190 | $tableDetails = $this->schemaManager->listTableDetails($table); 191 | 192 | $this->call('iseed', ['tables' => $table, '--force' => true, '--clean' => true]); 193 | $this->created[] = $table; 194 | 195 | if (count($pivotTables = $this->getPivotTables($table)) == 2) { 196 | $this->call('ha-generator:migration:pivot', ['tableOne' => $pivotTables['tableOne'], 'tableTwo' => $pivotTables['tableTwo']]); 197 | 198 | return; 199 | } 200 | 201 | foreach ($tableDetails->getColumns() as $column) { 202 | if (in_array($column->getName(), ['id', 'created_at', 'updated_at'])) { 203 | continue; 204 | } 205 | 206 | $type = $this->mapper[strtolower($column->getType()->getName())]; 207 | 208 | $schema .= "{$column->getName()}"; 209 | 210 | $schema .= $this->isForeign($tableDetails->getForeignKeys(), $column->getName()) ? ':bigInteger:foreign' : ":{$type}"; 211 | 212 | $schema .= $column->getUnsigned() ? ':unsigned' : ''; 213 | 214 | $schema .= (in_array($type, ['char', 'string']) and $column->getLength()) ? "({$column->getLength()})" : ''; 215 | 216 | $schema .= 'decimal' == $type ? "({$column->getPrecision()}, {$column->getScale()})" : ''; 217 | 218 | $schema .= $this->getUnique($tableDetails->getIndexes(), $column->getName()); 219 | 220 | $schema .= $column->getNotnull() ? '' : ':nullable'; 221 | 222 | $schema .= $column->getDefault() ? ":default('{$column->getDefault()}')" : ''; 223 | 224 | $schema .= ', '; 225 | } 226 | 227 | $schema = rtrim($schema, ', '); 228 | 229 | $noLogDetailed = !($this->option('log-detailed') and !$this->option('no-log')); 230 | 231 | $params = ['name' => Str::singular($table), '--no-log' => $noLogDetailed, '--yes' => $this->option('yes'), '--schema' => $schema, '--api' => $this->option('api')]; 232 | 233 | $this->call('ha-generator:crud', $params); 234 | } 235 | } 236 | 237 | /** 238 | * @param ForeignKeyConstraint[] $keys 239 | * @param string $column 240 | * 241 | * @return bool 242 | */ 243 | private function isForeign(array $keys, string $column) 244 | { 245 | $fkColumns = []; 246 | foreach ($keys as $key) { 247 | $fkColumns = array_merge($fkColumns, $key->getColumns()); 248 | } 249 | 250 | return in_array($column, $fkColumns); 251 | } 252 | 253 | /** 254 | * @param array $indexes 255 | * @param string $column 256 | * 257 | * @return string 258 | */ 259 | private function getUnique(array $indexes, string $column): string 260 | { 261 | foreach ($indexes as $index) { 262 | if (in_array($column, $index->getColumns())) { 263 | return $index->isUnique() ? ':unique' : ''; 264 | } 265 | } 266 | 267 | return ''; 268 | } 269 | 270 | /** 271 | * @param string $table 272 | * 273 | * @return array 274 | */ 275 | private function getPivotTables(string $table): array 276 | { 277 | $tables = []; 278 | $names = explode('_', $table); 279 | 280 | if (count($names) != 2) { 281 | return []; 282 | } 283 | 284 | if (in_array(Str::plural($names[0]), $this->created)) { 285 | $tables['tableOne'] = Str::plural($names[0]); 286 | } 287 | 288 | if (in_array(Str::plural($names[1]), $this->created)) { 289 | $tables['tableTwo'] = Str::plural($names[1]); 290 | } 291 | 292 | return $tables; 293 | } 294 | } 295 | -------------------------------------------------------------------------------- /src/Commands/LangCommand.php: -------------------------------------------------------------------------------- 1 | setNames(); 45 | 46 | if ($this->files->exists($path = $this->getPath())) { 47 | $this->error('Lang already exists!'); 48 | 49 | return; 50 | } 51 | 52 | $this->makeDirectory($path); 53 | 54 | $this->files->put($path, $this->compileStub()); 55 | 56 | $filename = pathinfo($path, PATHINFO_FILENAME); 57 | $this->line("Created Lang: {$this->argument('folder')}/{$filename}"); 58 | 59 | $this->writeLog(); 60 | 61 | $this->composer->dumpAutoloads(); 62 | } 63 | 64 | /** 65 | * Get the path to where we should store the lang. 66 | * 67 | * @param array $args 68 | * 69 | * @return string 70 | */ 71 | protected function getPath(...$args): string 72 | { 73 | return resource_path("lang/{$this->argument('folder')}/{$this->tableName}.php"); 74 | } 75 | 76 | /** 77 | * Compile the lang stub. 78 | * 79 | * @throws FileNotFoundException 80 | * 81 | * @return string 82 | */ 83 | protected function compileStub(): string 84 | { 85 | $stub = $this->files->get($this->resolveStubPath('/resources/lang/lang.stub')); 86 | 87 | if ($this->option('schema')) { 88 | $this->replaceSchema($stub); 89 | } 90 | 91 | $stub = str_replace('{{column}}', '', $stub); 92 | 93 | $this->replaceModelNamePlural($stub) 94 | ->replaceModelName($stub) 95 | ->replaceObjectName($stub); 96 | 97 | return $stub; 98 | } 99 | 100 | /** 101 | * Replace the table name in the stub. 102 | * 103 | * @param string $stub 104 | * 105 | * @return LangCommand 106 | */ 107 | protected function replaceModelNamePlural(string &$stub): LangCommand 108 | { 109 | $stub = str_replace('{{modelNamePlural}}', Str::plural($this->modelName), $stub); 110 | 111 | return $this; 112 | } 113 | 114 | /** 115 | * Replace the schema for the stub. 116 | * 117 | * @param string $stub 118 | * 119 | * @return LangCommand 120 | */ 121 | protected function replaceSchema(string &$stub): LangCommand 122 | { 123 | if ($schema = $this->option('schema')) { 124 | $schema = (new SchemaParser())->parse($schema); 125 | } 126 | 127 | $stub = (new LangSyntaxBuilder())->create($schema); 128 | 129 | return $this; 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /src/Commands/MigrationCommand.php: -------------------------------------------------------------------------------- 1 | setNames(); 54 | 55 | $this->meta = (new MigrationNameParser())->parse($this->argument('name')); 56 | 57 | $name = $this->argument('name'); 58 | 59 | if ($this->files->exists($path = $this->getPath($name))) { 60 | $this->error('Migration already exists!'); 61 | 62 | return; 63 | } 64 | 65 | if ($this->migrationAlreadyExist($name)) { 66 | $this->error("A {$this->getClassName($name)} class already exists."); 67 | 68 | return; 69 | } 70 | 71 | $this->makeDirectory($path); 72 | 73 | $this->files->put($path, $this->compileStub()); 74 | 75 | $filename = pathinfo($path, PATHINFO_FILENAME); 76 | $this->line("Created Migration: {$filename}"); 77 | 78 | $this->writeLog(); 79 | 80 | $this->composer->dumpAutoloads(); 81 | } 82 | 83 | /** 84 | * Get the path to where we should store the migration. 85 | * 86 | * @param array $args 87 | * 88 | * @return string 89 | */ 90 | protected function getPath(...$args): string 91 | { 92 | if ($this->hasPackage()) { 93 | return $this->packagePath.'/'.config('ha-generator.packageMigrationsFolder').'/'.date('Y_m_d_His').'_'.$args[0].'.php'; 94 | } 95 | 96 | return base_path().'/database/migrations/'.date('Y_m_d_His').'_'.$args[0].'.php'; 97 | } 98 | 99 | /** 100 | * Compile the migration stub. 101 | * 102 | * @throws FileNotFoundException 103 | * 104 | * @return string 105 | */ 106 | protected function compileStub(): string 107 | { 108 | $stub = $this->files->get($this->resolveStubPath('/database/migrations/migration.stub')); 109 | 110 | $this->replaceClassName($stub) 111 | ->replaceSchema($stub) 112 | ->replaceTableName($stub); 113 | 114 | return $stub; 115 | } 116 | 117 | /** 118 | * Replace the table name in the stub. 119 | * 120 | * @param string $stub 121 | * 122 | * @return AbstractCommand 123 | */ 124 | protected function replaceTableName(string &$stub): AbstractCommand 125 | { 126 | $table = $this->meta['table']; 127 | 128 | $stub = str_replace('{{table}}', $table, $stub); 129 | 130 | return $this; 131 | } 132 | 133 | /** 134 | * Replace the schema for the stub. 135 | * 136 | * @param string $stub 137 | * 138 | * @throws GeneratorException 139 | * 140 | * @return MigrationCommand 141 | */ 142 | protected function replaceSchema(string &$stub): MigrationCommand 143 | { 144 | if ($schema = $this->option('schema')) { 145 | $schema = (new SchemaParser())->parse($schema); 146 | } 147 | 148 | $schema = (new MigrationSyntaxBuilder())->create($schema, $this->meta); 149 | 150 | $stub = str_replace(['{{schema_up}}', '{{schema_down}}'], $schema, $stub); 151 | 152 | return $this; 153 | } 154 | 155 | /** 156 | * @param $name 157 | * 158 | * @return string 159 | */ 160 | private function getClassName($name) 161 | { 162 | return Str::studly($name); 163 | } 164 | 165 | /** 166 | * @param $name 167 | * 168 | * @return bool 169 | */ 170 | protected function migrationAlreadyExist($name) 171 | { 172 | $migrationFiles = $this->files->glob(config('ha-generator.packageMigrationsFolder').'/*.php'); 173 | 174 | foreach ($migrationFiles as $migrationFile) { 175 | $this->files->requireOnce($migrationFile); 176 | } 177 | 178 | return class_exists($this->getClassName($name)); 179 | } 180 | } 181 | -------------------------------------------------------------------------------- /src/Commands/MigrationPivotCommand.php: -------------------------------------------------------------------------------- 1 | files = $files; 64 | $this->composer = $composer; 65 | } 66 | 67 | /** 68 | * Execute the console command. 69 | * 70 | * @throws FileNotFoundException 71 | * 72 | * @return void 73 | */ 74 | public function handle(): void 75 | { 76 | if ($this->files->exists($path = $this->getPath())) { 77 | $this->error('Migration already exists!'); 78 | 79 | return; 80 | } 81 | 82 | if ($this->migrationAlreadyExist()) { 83 | $this->error("A {$this->getClassName()} class already exists."); 84 | 85 | return; 86 | } 87 | 88 | $this->files->put($path, $this->buildClass()); 89 | 90 | $filename = pathinfo($path, PATHINFO_FILENAME); 91 | $this->line("Created Migration: {$filename}"); 92 | 93 | $this->composer->dumpAutoloads(); 94 | } 95 | 96 | /** 97 | * Get the class name from table names. 98 | * 99 | * @return string 100 | */ 101 | protected function getClassName(): string 102 | { 103 | $name = implode('', array_map('ucwords', $this->getSortedSingularTableNames())); 104 | 105 | $name = preg_replace_callback('/(\_)([a-z]{1})/', function ($matches) { 106 | return Str::studly($matches[0]); 107 | }, $name); 108 | 109 | return "Create{$name}PivotTable"; 110 | } 111 | 112 | /** 113 | * Get the stub file for the generator. 114 | * 115 | * @return string 116 | */ 117 | protected function getStub(): string 118 | { 119 | return $this->resolveStubPath('/database/migrations/pivot.stub'); 120 | } 121 | 122 | /** 123 | * @return mixed 124 | */ 125 | protected function resolveStubPath($stub) 126 | { 127 | return file_exists($customPath = base_path('stubs/ha-generator/'.trim($stub, '/'))) && config('ha-generator.customStubs') 128 | ? $customPath 129 | : static::STUB_DIR.$stub; 130 | } 131 | 132 | /** 133 | * Get the destination class path. 134 | * 135 | * @return string 136 | */ 137 | protected function getPath(): string 138 | { 139 | return base_path().'/database/migrations/'.date('Y_m_d_His'). 140 | '_create_'.$this->getPivotTableName().'_pivot_table.php'; 141 | } 142 | 143 | /** 144 | * Build the class with the given name. 145 | * 146 | * @throws FileNotFoundException 147 | * 148 | * @return string 149 | */ 150 | protected function buildClass(): string 151 | { 152 | $stub = $this->files->get($this->getStub()); 153 | 154 | return $this->replacePivotTableName($stub) 155 | ->replaceSchema($stub) 156 | ->replaceClass($stub, $this->getClassName()); 157 | } 158 | 159 | /** 160 | * Apply the name of the pivot table to the stub. 161 | * 162 | * @param string $stub 163 | * 164 | * @return MigrationPivotCommand 165 | */ 166 | protected function replacePivotTableName(&$stub): MigrationPivotCommand 167 | { 168 | $stub = str_replace('{{pivotTableName}}', $this->getPivotTableName(), $stub); 169 | 170 | return $this; 171 | } 172 | 173 | /** 174 | * Apply the correct schema to the stub. 175 | * 176 | * @param string $stub 177 | * 178 | * @return MigrationPivotCommand 179 | */ 180 | protected function replaceSchema(&$stub): MigrationPivotCommand 181 | { 182 | $tables = array_merge( 183 | $this->getSortedSingularTableNames(), 184 | $this->getSortedTableNames() 185 | ); 186 | 187 | $stub = str_replace( 188 | ['{{columnOne}}', '{{columnTwo}}', '{{tableOne}}', '{{tableTwo}}'], 189 | $tables, 190 | $stub 191 | ); 192 | 193 | return $this; 194 | } 195 | 196 | /** 197 | * Replace the class name for the given stub. 198 | * 199 | * @param string $stub 200 | * @param string $name 201 | * 202 | * @return string 203 | */ 204 | protected function replaceClass($stub, $name): string 205 | { 206 | $stub = str_replace('{{class}}', $name, $stub); 207 | 208 | return $stub; 209 | } 210 | 211 | /** 212 | * Get the name of the pivot table. 213 | * 214 | * @return string 215 | */ 216 | protected function getPivotTableName(): string 217 | { 218 | return implode('_', $this->getSortedSingularTableNames()); 219 | } 220 | 221 | /** 222 | * Sort the two tables in alphabetical order. 223 | * 224 | * @return array 225 | */ 226 | protected function getSortedTableNames(): array 227 | { 228 | $tables = $this->getTableNamesFromInput(); 229 | 230 | sort($tables); 231 | 232 | return $tables; 233 | } 234 | 235 | /** 236 | * Sort the two tables in alphabetical order, in singular form. 237 | * 238 | * @return array 239 | */ 240 | protected function getSortedSingularTableNames(): array 241 | { 242 | $tables = array_map('Str::singular', $this->getTableNamesFromInput()); 243 | 244 | sort($tables); 245 | 246 | return $tables; 247 | } 248 | 249 | /** 250 | * Get the table names from input. 251 | * 252 | * @return array 253 | */ 254 | protected function getTableNamesFromInput(): array 255 | { 256 | return [ 257 | strtolower($this->argument('tableOne')), 258 | strtolower($this->argument('tableTwo')), 259 | ]; 260 | } 261 | 262 | /** 263 | * @return bool 264 | */ 265 | protected function migrationAlreadyExist() 266 | { 267 | $migrationFiles = $this->files->glob(config('ha-generator.packageMigrationsFolder').'/*.php'); 268 | 269 | foreach ($migrationFiles as $migrationFile) { 270 | $this->files->requireOnce($migrationFile); 271 | } 272 | 273 | return class_exists($this->getClassName()); 274 | } 275 | } 276 | -------------------------------------------------------------------------------- /src/Commands/ModelCommand.php: -------------------------------------------------------------------------------- 1 | setNames(); 46 | 47 | if ($this->files->exists($path = $this->getPath())) { 48 | $this->error('Model already exists!'); 49 | 50 | return; 51 | } 52 | 53 | $this->files->put($path, $this->compileStub()); 54 | 55 | $filename = pathinfo($path, PATHINFO_FILENAME); 56 | $this->line("Created Model: {$filename}"); 57 | 58 | $this->writeLog(); 59 | 60 | $this->composer->dumpAutoloads(); 61 | } 62 | 63 | /** 64 | * Get the path to where we should store the model. 65 | * 66 | * @param array $args 67 | * 68 | * @return string 69 | */ 70 | protected function getPath(...$args): string 71 | { 72 | if ($this->hasPackage()) { 73 | $this->makeDirectory($path = $this->packagePath."/src/Models/{$this->modelName}.php"); 74 | 75 | return $path; 76 | } 77 | 78 | return is_dir(app_path('Models')) ? app_path("Models/{$this->modelName}.php") : app_path("{$this->modelName}.php"); 79 | } 80 | 81 | /** 82 | * Compile the model stub. 83 | * 84 | * @throws FileNotFoundException 85 | * 86 | * @return string 87 | */ 88 | protected function compileStub(): string 89 | { 90 | return $stub = $this->option('api') ? $this->compileApiModelStub() : $this->compileWebModelStub(); 91 | } 92 | 93 | /** 94 | * Compile the model api stub. 95 | * 96 | * @throws FileNotFoundException 97 | * 98 | * @return string 99 | */ 100 | protected function compileApiModelStub(): string 101 | { 102 | $stub = $this->files->get($this->resolveStubPath('/app/ApiModel.stub')); 103 | 104 | $this 105 | ->replaceSchema($stub) 106 | ->replaceModelNamespace($stub) 107 | ->replaceModelName($stub); 108 | 109 | return $stub; 110 | } 111 | 112 | /** 113 | * Compile the model web stub. 114 | * 115 | * @throws FileNotFoundException 116 | * 117 | * @return string 118 | */ 119 | protected function compileWebModelStub(): string 120 | { 121 | $datatables = $this->option('datatables') ? 'Datatables' : ''; 122 | 123 | $stub = $this->files->get($this->resolveStubPath("/app/WebModel{$datatables}.stub")); 124 | 125 | $this 126 | ->replaceSchema($stub) 127 | ->replaceModelNamespace($stub) 128 | ->replaceModelName($stub); 129 | 130 | return $stub; 131 | } 132 | 133 | /** 134 | * Replace the schema for the stub. 135 | * 136 | * @param string $stub 137 | * 138 | * @return ModelCommand 139 | */ 140 | protected function replaceSchema(string &$stub): ModelCommand 141 | { 142 | if ($schema = $this->option('schema')) { 143 | $schema = (new SchemaParser())->parse($schema); 144 | } 145 | 146 | $schema = (new ModelSyntaxBuilder())->create($schema); 147 | 148 | $stub = str_replace(['{{column}}', '{{foreign}}', '{{searchables}}'], $schema, $stub); 149 | 150 | return $this; 151 | } 152 | } 153 | -------------------------------------------------------------------------------- /src/Commands/NavCommand.php: -------------------------------------------------------------------------------- 1 | setNames(); 41 | 42 | if (!$this->files->exists($path = $this->getPath())) { 43 | $this->error('Nav not exists!'); 44 | 45 | return; 46 | } 47 | 48 | $this->append($path, $this->compileStub()); 49 | 50 | $filename = pathinfo($path, PATHINFO_FILENAME); 51 | $this->line("Inserted Nav in: {$filename}"); 52 | 53 | $this->writeLog(); 54 | 55 | $this->composer->dumpAutoloads(); 56 | } 57 | 58 | /** 59 | * Get the path to where we should store the menu. 60 | * 61 | * @param array $args 62 | * 63 | * @return string 64 | */ 65 | protected function getPath(...$args): string 66 | { 67 | return resource_path('views/layouts/nav.blade.php'); 68 | } 69 | 70 | /** 71 | * Append menu. 72 | * 73 | * @param string $path 74 | * @param string $compileStub 75 | */ 76 | protected function append(string $path, string $compileStub) 77 | { 78 | $menu = new DOMDocument(); 79 | $menu->load($path); 80 | 81 | $item = new DOMDocument(); 82 | $item->loadXML($compileStub); 83 | 84 | $menu->getElementsByTagName('ul')->item(0)->appendChild( 85 | $menu->importNode($item->getElementsByTagName('li')->item(0), true) 86 | ); 87 | 88 | // LIBXML_NOXMLDECL remove xml header to Libxml >= 2.6.21 89 | $menu->save($path, LIBXML_NOXMLDECL); 90 | 91 | // If you don't have Libxml >= 2.6.21 92 | file_put_contents($path, preg_replace('/<\?xml[^>]+>\s+/', '', $this->indentContent(file_get_contents($path)))); 93 | } 94 | 95 | /** 96 | * Compile the menu stub. 97 | * 98 | * @throws FileNotFoundException 99 | * 100 | * @return string 101 | */ 102 | protected function compileStub(): string 103 | { 104 | $stub = $this->files->get($this->resolveStubPath('/resources/views/layouts/nav.stub')); 105 | 106 | $this->replaceRouteName($stub) 107 | ->replaceTableName($stub); 108 | 109 | return $stub; 110 | } 111 | 112 | /** 113 | * @param $content 114 | * @param string $tab 115 | * 116 | * @return string 117 | * @See https://stackoverflow.com/questions/7838929/keeping-line-breaks-when-using-phps-domdocument-appendchild 118 | */ 119 | protected function indentContent($content, $tab = "\t") 120 | { 121 | $indent = 0; 122 | 123 | // add marker linefeeds to aid the pretty-tokeniser (adds a linefeed between all tag-end boundaries) 124 | $content = preg_replace('/(>)(<)(\/*)/', "$1\n$2$3", $content); 125 | 126 | // now indent the tags 127 | $token = strtok($content, "\n"); 128 | $result = ''; // holds formatted version as it is built 129 | $pad = 0; // initial indent 130 | $matches = []; // returns from preg_matches() 131 | 132 | // scan each line and adjust indent based on opening/closing tags 133 | while ($token !== false) { 134 | $token = trim($token); 135 | // test for the various tag states 136 | 137 | // 1. open and closing tags on same line - no change 138 | if (preg_match('/.+<\/\w[^>]*>$/', $token, $matches)) { 139 | $indent = 0; 140 | } 141 | // 2. closing tag - outdent now 142 | elseif (preg_match('/^<\/\w/', $token, $matches)) { 143 | $pad--; 144 | if ($indent > 0) { 145 | $indent = 0; 146 | } 147 | } 148 | // 3. opening tag - don't pad this one, only subsequent tags 149 | elseif (preg_match('/^<\w[^>]*[^\/]>.*$/', $token, $matches)) { 150 | $indent = 1; 151 | } 152 | // 4. no indentation needed 153 | else { 154 | $indent = 0; 155 | } 156 | 157 | // pad the line with the required number of leading spaces 158 | $line = str_pad($token, strlen($token) + $pad, $tab, STR_PAD_LEFT); 159 | $result .= $line."\n"; // add to the cumulative result, with linefeed 160 | $token = strtok("\n"); // get the next token 161 | $pad += $indent; // update the pad size for subsequent lines 162 | } 163 | 164 | return $result; 165 | } 166 | } 167 | -------------------------------------------------------------------------------- /src/Commands/RequestsCommand.php: -------------------------------------------------------------------------------- 1 | setNames(); 44 | 45 | if ($directoryExists = $this->files->exists($path = $this->getPath('Store'))) { 46 | $this->error('StoreRequest already exists!'); 47 | } 48 | 49 | if ($directoryExists = $this->files->exists($path = $this->getPath('Update'))) { 50 | $this->error('UpdateRequest already exists!'); 51 | } 52 | 53 | if (!$directoryExists) { 54 | $this->makeDirectory($path); 55 | } 56 | 57 | if (!$this->files->exists($path = $this->getPath('Store'))) { 58 | $this->files->put($path, $this->compileStub()); 59 | 60 | $filename = pathinfo($path, PATHINFO_FILENAME); 61 | $this->line("Created Request: {$filename}"); 62 | } 63 | 64 | if (!$this->files->exists($path = $this->getPath('Update'))) { 65 | $this->files->put($path, $this->compileStub('update')); 66 | 67 | $filename = pathinfo($path, PATHINFO_FILENAME); 68 | $this->line("Created Request: {$filename}"); 69 | } 70 | 71 | $this->writeLog(); 72 | 73 | $this->composer->dumpAutoloads(); 74 | } 75 | 76 | /** 77 | * Get the path to where we should store the requests. 78 | * 79 | * @param array $args 80 | * 81 | * @return string 82 | */ 83 | protected function getPath(...$args): string 84 | { 85 | if ($this->hasPackage()) { 86 | $this->makeDirectory($path = $this->packagePath."/src/Http/Requests/{$this->modelName}{$args[0]}Request.php"); 87 | 88 | return $path; 89 | } 90 | 91 | return app_path("Http/Requests/{$this->modelName}{$args[0]}Request.php"); 92 | } 93 | 94 | /** 95 | * Compile the requests stub. 96 | * 97 | * @param string $type 98 | * 99 | * @throws FileNotFoundException 100 | * 101 | * @return string 102 | */ 103 | protected function compileStub(string $type = 'store'): string 104 | { 105 | $stub = $this->files->get($this->resolveStubPath('/app/Http/Requests/Request.stub')); 106 | 107 | $this 108 | ->replaceSchema($stub) 109 | ->replaceClassTypeName($stub, $type) 110 | ->replaceNamespace($stub) 111 | ->replaceModelName($stub); 112 | 113 | return $stub; 114 | } 115 | 116 | /** 117 | * Replace the class name in the stub. 118 | * 119 | * @param string $stub 120 | * @param string $type 121 | * 122 | * @return RequestsCommand 123 | */ 124 | protected function replaceClassTypeName(string &$stub, string $type = 'Store'): RequestsCommand 125 | { 126 | $stub = str_replace('{{type}}', ucwords($type), $stub); 127 | 128 | return $this; 129 | } 130 | 131 | /** 132 | * Replace the schema for the stub. 133 | * 134 | * @param string $stub 135 | * 136 | * @return RequestsCommand 137 | */ 138 | protected function replaceSchema(string &$stub): RequestsCommand 139 | { 140 | if ($schema = $this->option('schema')) { 141 | $schema = (new SchemaParser())->parse($schema); 142 | } 143 | 144 | $stub = (new RequestSyntaxBuilder())->create($schema); 145 | 146 | return $this; 147 | } 148 | } 149 | -------------------------------------------------------------------------------- /src/Commands/ResourcesCommand.php: -------------------------------------------------------------------------------- 1 | setNames(); 41 | 42 | if ($this->files->exists($path = $this->getPath('Resource'))) { 43 | $this->error('Resource already exists!'); 44 | } 45 | 46 | if ($this->files->exists($path = $this->getPath('Collection'))) { 47 | $this->error('Collection already exists!'); 48 | } 49 | 50 | $this->makeDirectory($path); 51 | 52 | if (!$this->files->exists($path = $this->getPath('Resource'))) { 53 | $this->files->put($path, $this->compileStub()); 54 | 55 | $filename = pathinfo($path, PATHINFO_FILENAME); 56 | $this->line("Created Resource: {$filename}"); 57 | } 58 | 59 | if (!$this->files->exists($path = $this->getPath('Collection'))) { 60 | $this->files->put($path, $this->compileStub('Collection')); 61 | 62 | $filename = pathinfo($path, PATHINFO_FILENAME); 63 | $this->line("Created Collection: {$filename}"); 64 | } 65 | 66 | $this->writeLog(); 67 | 68 | $this->composer->dumpAutoloads(); 69 | } 70 | 71 | /** 72 | * Get the path to where we should store the resources. 73 | * 74 | * @param array $args 75 | * 76 | * @return string 77 | */ 78 | protected function getPath(...$args): string 79 | { 80 | if ($this->hasPackage()) { 81 | $this->makeDirectory($path = $this->packagePath."/src/Http/Resources/{$this->modelName}{$args[0]}.php"); 82 | 83 | return $path; 84 | } 85 | 86 | return app_path("Http/Resources/{$this->modelName}{$args[0]}.php"); 87 | } 88 | 89 | /** 90 | * Compile the resources stub. 91 | * 92 | * @param string $sufix 93 | * 94 | * @throws FileNotFoundException 95 | * 96 | * @return string 97 | */ 98 | protected function compileStub(string $sufix = 'Resource'): string 99 | { 100 | $stub = $this->files->get($this->resolveStubPath("/app/Http/Resources/{$sufix}.stub")); 101 | 102 | $this 103 | ->replaceNamespace($stub) 104 | ->replaceModelName($stub); 105 | 106 | return $stub; 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /src/Commands/RouteCommand.php: -------------------------------------------------------------------------------- 1 | setNames(); 34 | 35 | if (!$this->files->exists($path = $this->getPath())) { 36 | $this->error('Route file not found'); 37 | 38 | return; 39 | } 40 | 41 | $this->files->append($path, $this->compileStub()); 42 | 43 | $filename = pathinfo($path, PATHINFO_FILENAME); 44 | $this->line("Inserted Route in: {$filename}"); 45 | 46 | $this->writeLog(); 47 | 48 | $this->composer->dumpAutoloads(); 49 | } 50 | 51 | /** 52 | * Get the path to where we should store the routes. 53 | * 54 | * @param array $args 55 | * 56 | * @return string 57 | */ 58 | protected function getPath(...$args): string 59 | { 60 | $name = $this->option('api') ? 'api' : 'web'; 61 | 62 | if ($this->hasPackage()) { 63 | $this->makeDirectory($path = $this->packagePath.'/'.config('ha-generator.packageRoutesFolder')."/$name.php"); 64 | 65 | return $path; 66 | } 67 | 68 | return base_path("routes/$name.php"); 69 | } 70 | 71 | /** 72 | * Compile the route stub. 73 | * 74 | * @return string 75 | */ 76 | protected function compileStub(): string 77 | { 78 | return $stub = $this->option('api') ? $this->compileApiStub() : $this->compileWebStub(); 79 | } 80 | 81 | /** 82 | * Compile the routes api stub. 83 | * 84 | * @return string 85 | */ 86 | protected function compileApiStub(): string 87 | { 88 | $stub = PHP_EOL."Route::apiResource('{{routeName}}', App\Http\Controllers\{{modelName}}Controller::class);".PHP_EOL; 89 | 90 | $this 91 | ->replaceModelName($stub) 92 | ->replaceRouteName($stub); 93 | 94 | return $stub; 95 | } 96 | 97 | /** 98 | * Compile the route web stub. 99 | * 100 | * @return string 101 | */ 102 | protected function compileWebStub(): string 103 | { 104 | $stub = PHP_EOL; 105 | 106 | $stub = $this->option('datatables') 107 | ? $stub."Route::get('/{{routeName}}/data', [App\Http\Controllers\{{modelName}}Controller::class, 'data'])->name('{{routeName}}.data');".PHP_EOL 108 | : ''; 109 | 110 | $stub = $stub. 111 | "Route::get('/{{routeName}}/{{{objectName}}}/delete', [App\Http\Controllers\{{modelName}}Controller::class, 'delete'])->name('{{routeName}}.delete');".PHP_EOL. 112 | "Route::resource('{{routeName}}', App\Http\Controllers\{{modelName}}Controller::class);".PHP_EOL; 113 | 114 | $this 115 | ->replaceModelName($stub) 116 | ->replaceRouteName($stub) 117 | ->replaceObjectName($stub); 118 | 119 | return $stub; 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /src/Commands/TestsCommand.php: -------------------------------------------------------------------------------- 1 | setNames(); 45 | 46 | if ($directoryExists = $this->files->exists($path = $this->getPath())) { 47 | $this->error('Test already exists!'); 48 | 49 | return; 50 | } 51 | 52 | if (!$directoryExists) { 53 | $this->makeDirectory($path); 54 | } 55 | 56 | $this->files->put($path, $this->compileStub()); 57 | 58 | $filename = pathinfo($path, PATHINFO_FILENAME); 59 | $this->line("Created Test: {$filename}"); 60 | 61 | $this->writeLog(); 62 | 63 | $this->composer->dumpAutoloads(); 64 | } 65 | 66 | /** 67 | * Get the path to where we should store the test. 68 | * 69 | * @param array $args 70 | * 71 | * @return string 72 | */ 73 | protected function getPath(...$args): string 74 | { 75 | if ($this->hasPackage()) { 76 | return $this->packagePath."/tests/Feature/{$this->modelName}Test.php"; 77 | } 78 | 79 | return base_path("tests/Feature/{$this->modelName}Test.php"); 80 | } 81 | 82 | /** 83 | * Compile the requests stub. 84 | * 85 | * @throws FileNotFoundException 86 | * 87 | * @return string 88 | */ 89 | protected function compileStub(): string 90 | { 91 | $stub = $this->option('api') 92 | ? $this->files->get($this->resolveStubPath('/tests/Feature/ApiTest.stub')) 93 | : $this->files->get($this->resolveStubPath('/tests/Feature/WebTest.stub')); 94 | 95 | $this->replaceSchema($stub) 96 | ->replacePackageRouteName($stub) 97 | ->replaceTestNamespace($stub) 98 | ->replaceModelNamespace($stub) 99 | ->replaceModelName($stub) 100 | ->replaceTableName($stub) 101 | ->replaceRouteName($stub) 102 | ->replaceObjectName($stub); 103 | 104 | return $stub; 105 | } 106 | 107 | /** 108 | * Replace the schema for the stub. 109 | * 110 | * @param string $stub 111 | * 112 | * @return TestsCommand 113 | */ 114 | protected function replaceSchema(string &$stub): TestsCommand 115 | { 116 | if ($schema = $this->option('schema')) { 117 | $schema = (new SchemaParser())->parse($schema); 118 | } 119 | 120 | $stub = (new TestSyntaxBuilder())->create($schema, $this->option('api')); 121 | 122 | return $this; 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /src/Commands/ViewCommand.php: -------------------------------------------------------------------------------- 1 | setNames(); 50 | 51 | $this->viewName = strtolower($this->argument('view')); 52 | 53 | if ($this->files->exists($path = $this->getPath())) { 54 | $this->error('View already exists!'); 55 | 56 | return; 57 | } 58 | 59 | $this->makeDirectory($path); 60 | 61 | $this->files->put($path, $this->compileStub()); 62 | 63 | $filename = pathinfo($path, PATHINFO_FILENAME); 64 | $this->line("Created View: {$filename}"); 65 | 66 | $this->writeLog(); 67 | 68 | $this->composer->dumpAutoloads(); 69 | } 70 | 71 | /** 72 | * Get the path to where we should store the view. 73 | * 74 | * @param array $args 75 | * 76 | * @return string 77 | */ 78 | protected function getPath(...$args): string 79 | { 80 | return resource_path("views/{$this->tableName}/{$this->viewName}.blade.php"); 81 | } 82 | 83 | /** 84 | * Compile the view stub. 85 | * 86 | * @throws FileNotFoundException 87 | * 88 | * @return string 89 | */ 90 | protected function compileStub(): string 91 | { 92 | $datatables = 'index' === $this->viewName && $this->option('datatables') ? 'Datatables' : ''; 93 | 94 | $stub = $this->files->get($this->resolveStubPath("/resources/views/{$this->viewName}{$datatables}.blade.stub")); 95 | 96 | if ($this->option('schema')) { 97 | $this->replaceSchema($stub); 98 | } 99 | 100 | $this->replaceTableName($stub) 101 | ->replaceRouteName($stub) 102 | ->replaceObjectName($stub); 103 | 104 | return $stub; 105 | } 106 | 107 | /** 108 | * Replace the schema for the stub. 109 | * 110 | * @param string $stub 111 | * 112 | * @return ViewCommand 113 | */ 114 | protected function replaceSchema(string &$stub): ViewCommand 115 | { 116 | if ($schema = $this->option('schema')) { 117 | $schema = (new SchemaParser())->parse($schema); 118 | } 119 | 120 | $stub = (new ViewSyntaxBuilder($this->viewName, $this->files))->create($schema); 121 | 122 | return $this; 123 | } 124 | 125 | /** 126 | * @param $content 127 | * @param string $tab 128 | * 129 | * @return string 130 | * @See https://stackoverflow.com/questions/7838929/keeping-line-breaks-when-using-phps-domdocument-appendchild 131 | */ 132 | protected function indentContent($content, $tab = "\t") 133 | { 134 | $indent = 0; 135 | 136 | // add marker linefeeds to aid the pretty-tokeniser (adds a linefeed between all tag-end boundaries) 137 | $content = preg_replace('/(>)(<)(\/*)/', "$1\n$2$3", $content); 138 | 139 | // now indent the tags 140 | $token = strtok($content, "\n"); 141 | $result = ''; // holds formatted version as it is built 142 | $pad = 0; // initial indent 143 | $matches = []; // returns from preg_matches() 144 | 145 | // scan each line and adjust indent based on opening/closing tags 146 | while ($token !== false) { 147 | $token = trim($token); 148 | // test for the various tag states 149 | 150 | // 1. open and closing tags on same line - no change 151 | if (preg_match('/.+<\/\w[^>]*>$/', $token, $matches)) { 152 | $indent = 0; 153 | } 154 | // 2. closing tag - outdent now 155 | elseif (preg_match('/^<\/\w/', $token, $matches)) { 156 | $pad--; 157 | if ($indent > 0) { 158 | $indent = 0; 159 | } 160 | } 161 | // 3. opening tag - don't pad this one, only subsequent tags 162 | elseif (preg_match('/^<\w[^>]*[^\/]>.*$/', $token, $matches)) { 163 | $indent = 1; 164 | } 165 | // 4. no indentation needed 166 | else { 167 | $indent = 0; 168 | } 169 | 170 | // pad the line with the required number of leading spaces 171 | $line = str_pad($token, strlen($token) + $pad, $tab, STR_PAD_LEFT); 172 | $result .= $line."\n"; // add to the cumulative result, with linefeed 173 | $token = strtok("\n"); // get the next token 174 | $pad += $indent; // update the pad size for subsequent lines 175 | } 176 | 177 | return $result; 178 | } 179 | } 180 | -------------------------------------------------------------------------------- /src/Config/ha-generator.php: -------------------------------------------------------------------------------- 1 | true, 6 | 7 | 'stringTypes' => [ 8 | 'char', 'string', 'text', 'mediumText', 'longText', 'json', 'jsonb', 9 | ], 10 | 11 | 'integerTypes' => [ 12 | 'increments', 'integerIncrements', 'tinyIncrements', 'smallIncrements', 'mediumIncrements', 'bigIncrements', 13 | 'integer', 'tinyInteger', 'smallInteger', 'mediumInteger', 'bigInteger', 14 | 'unsignedInteger', 'unsignedTinyInteger', 'unsignedSmallInteger', 'unsignedMediumInteger', 'unsignedBigInteger', 15 | ], 16 | 17 | 'floatTypes' => [ 18 | 'float', 'double', 'decimal', 'unsignedDecimal', 19 | ], 20 | 21 | 'dateTypes' => [ 22 | 'date', 'dateTime', 'dateTimeTz', 23 | 'time', 'timeTz', 'timestamp', 'timestampTz', 'timestamps', 24 | 'timestamps', 'timestampsTz', 'softDeletes', 'softDeletesTz', 25 | 'year', 26 | ], 27 | 28 | 'bluePrintTypes' => [ 29 | 'increments', 'integerIncrements', 'tinyIncrements', 'smallIncrements', 'mediumIncrements', 'bigIncrements', 30 | 'char', 'string', 'text', 'mediumText', 'longText', 31 | 'integer', 'tinyInteger', 'smallInteger', 'mediumInteger', 'bigInteger', 32 | 'unsignedInteger', 'unsignedTinyInteger', 'unsignedSmallInteger', 'unsignedMediumInteger', 'unsignedBigInteger', 33 | 'float', 'double', 'decimal', 'unsignedDecimal', 34 | 'boolean', 35 | 'enum', 'set', 36 | 'json', 'jsonb', 37 | 'date', 'dateTime', 'dateTimeTz', 38 | 'time', 'timeTz', 'timestamp', 'timestampTz', 'timestamps', 39 | 'timestamps', 'timestampsTz', 'softDeletes', 'softDeletesTz', 40 | 'year', 41 | 'binary', 42 | 'uuid', 43 | 'ipAddress', 44 | 'macAddress', 45 | 'geometry', 'point', 'lineString', 'polygon', 'geometryCollection', 'multiPoint', 'multiLineString', 'multiPolygon', 'multiPolygonZ', 46 | 'computed', 47 | 'morphs', 'nullableMorphs', 'uuidMorphs', 'nullableUuidMorphs', 48 | 'rememberToken', 49 | 'foreign', 50 | ], 51 | 52 | 'modelNamespace' => 'App\Models', 53 | 54 | 'logFile' => 'ha-generator', 55 | 56 | 'packagesFolder' => 'packages', 57 | 58 | 'packagesNamespace' => 'Laravelha', 59 | 60 | 'packagesVendor' => 'laravelha/', 61 | 62 | 'packageConfigsFolder' => 'config', 63 | 64 | 'packageMigrationsFolder' => 'database/migrations', 65 | 'packageFactoriesFolder' => 'database/factories', 66 | 67 | 'packageLangsFolder' => 'resource/lang', 68 | 'packageViewsFolder' => 'resource/view', 69 | 70 | 'packageRoutesFolder' => 'routes', 71 | ]; 72 | -------------------------------------------------------------------------------- /src/GeneratorException.php: -------------------------------------------------------------------------------- 1 | publishes([ 34 | __DIR__.'/Config/ha-generator.php' => config_path('ha-generator.php'), 35 | ], 'ha-generator'); 36 | 37 | $this->publishes([ 38 | __DIR__.'/stubs' => base_path('stubs/ha-generator'), 39 | ], 'ha-generator'); 40 | } 41 | 42 | /** 43 | * Register the package services. 44 | * 45 | * @return void 46 | */ 47 | public function register() 48 | { 49 | $this->mergeConfigFrom( 50 | __DIR__.'/Config/ha-generator.php', 51 | 'ha-generator' 52 | ); 53 | 54 | if ($this->app->runningInConsole()) { 55 | $this->commands([ 56 | BreadcrumbCommand::class, 57 | ControllerCommand::class, 58 | CrudGenerator::class, 59 | FactoryCommand::class, 60 | FromExistingCommand::class, 61 | LangCommand::class, 62 | MigrationCommand::class, 63 | MigrationPivotCommand::class, 64 | ModelCommand::class, 65 | NavCommand::class, 66 | PackageCommand::class, 67 | RequestsCommand::class, 68 | ResourcesCommand::class, 69 | RouteCommand::class, 70 | TestsCommand::class, 71 | ViewCommand::class, 72 | ]); 73 | } 74 | } 75 | 76 | /** 77 | * Get the services provided by the provider. 78 | * 79 | * @return array 80 | */ 81 | public function provides() 82 | { 83 | return [ 84 | BreadcrumbCommand::class, 85 | ControllerCommand::class, 86 | CrudGenerator::class, 87 | FactoryCommand::class, 88 | FromExistingCommand::class, 89 | LangCommand::class, 90 | MigrationCommand::class, 91 | MigrationPivotCommand::class, 92 | ModelCommand::class, 93 | NavCommand::class, 94 | PackageCommand::class, 95 | RequestsCommand::class, 96 | ResourcesCommand::class, 97 | RouteCommand::class, 98 | TestsCommand::class, 99 | ViewCommand::class, 100 | ]; 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /src/Parsers/MigrationNameParser.php: -------------------------------------------------------------------------------- 1 | $this->getAction($segments), 24 | 'table' => $this->getTableName($segments), 25 | ]; 26 | } 27 | 28 | /** 29 | * Calculate the table name. 30 | * 31 | * @param array $segments 32 | * 33 | * @return string 34 | */ 35 | private function getTableName(array $segments): string 36 | { 37 | $tableName = []; 38 | 39 | foreach ($segments as $segment) { 40 | if ($this->isConnectingWord($segment)) { 41 | break; 42 | } 43 | 44 | $tableName[] = $segment; 45 | } 46 | 47 | return implode('_', array_reverse($tableName)); 48 | } 49 | 50 | /** 51 | * Determine the user's desired action for the migration. 52 | * 53 | * @param array $segments 54 | * 55 | * @return string 56 | */ 57 | private function getAction(array &$segments): string 58 | { 59 | return $this->normalizeActionName(array_pop($segments)); 60 | } 61 | 62 | /** 63 | * Normalize the user's chosen action to name to 64 | * something that we recognize. 65 | * 66 | * @param string $action 67 | * 68 | * @return string 69 | */ 70 | private function normalizeActionName($action): string 71 | { 72 | switch ($action) { 73 | case 'create': 74 | case 'make': 75 | return 'create'; 76 | case 'delete': 77 | case 'destroy': 78 | case 'drop': 79 | return 'remove'; 80 | case 'add': 81 | case 'append': 82 | case 'update': 83 | case 'insert': 84 | return 'add'; 85 | default: 86 | return $action; 87 | } 88 | } 89 | 90 | /** 91 | * Determine if the current segment is a connecting word. 92 | * 93 | * @param string $segment 94 | * 95 | * @return bool 96 | */ 97 | private function isConnectingWord(string $segment): bool 98 | { 99 | $connectors = ['to', 'from', 'and', 'with', 'for', 'in', 'of', 'on']; 100 | 101 | return in_array($segment, $connectors); 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /src/Parsers/SchemaParser.php: -------------------------------------------------------------------------------- 1 | splitIntoFields($schema); 27 | 28 | foreach ($fields as $field) { 29 | $segments = $this->parseSegments($field); 30 | 31 | if ($this->fieldNeedsForeignConstraint($segments)) { 32 | unset($segments['options']['foreign']); 33 | 34 | // If the user wants a foreign constraint, then 35 | // we'll first add the regular field. 36 | $this->addField($segments); 37 | 38 | // And then add another field for the constraint. 39 | $this->addForeignConstraint($segments); 40 | 41 | continue; 42 | } 43 | 44 | $this->addField($segments); 45 | } 46 | 47 | return $this->schema; 48 | } 49 | 50 | /** 51 | * Add a field to the schema array. 52 | * 53 | * @param array $field 54 | * 55 | * @return SchemaParser 56 | */ 57 | private function addField(array $field): SchemaParser 58 | { 59 | $this->schema[] = $field; 60 | 61 | return $this; 62 | } 63 | 64 | /** 65 | * Get an array of fields from the given schema. 66 | * 67 | * @param string $schema 68 | * 69 | * @return array 70 | */ 71 | private function splitIntoFields(string $schema): array 72 | { 73 | return preg_split('/,\s?(?![^()]*\))/', $schema); 74 | } 75 | 76 | /** 77 | * Get the segments of the schema field. 78 | * 79 | * @param string $field 80 | * 81 | * @return array 82 | */ 83 | private function parseSegments(string $field): array 84 | { 85 | $segments = explode(':', $field); 86 | 87 | $name = array_shift($segments); 88 | $type = array_shift($segments); 89 | 90 | $arguments = []; 91 | $options = $this->parseOptions($segments); 92 | 93 | // Do we have arguments being used here? 94 | // Like: string(100) 95 | if (preg_match('/(.+?)\(([^)]+)\)/', $type, $matches)) { 96 | $type = $matches[1]; 97 | $arguments = explode(',', $matches[2]); 98 | } 99 | 100 | if (!in_array($type, config('ha-generator.bluePrintTypes'))) { 101 | throw new \InvalidArgumentException("Column Type: $type not available"); 102 | } 103 | 104 | return compact('name', 'type', 'arguments', 'options'); 105 | } 106 | 107 | /** 108 | * Parse any given options into something usable. 109 | * 110 | * @param array $options 111 | * 112 | * @return array 113 | */ 114 | private function parseOptions(array $options): array 115 | { 116 | if (empty($options)) { 117 | return []; 118 | } 119 | 120 | foreach ($options as $option) { 121 | if (Str::contains($option, '(')) { 122 | preg_match('/([a-z]+)\(([^\)]+)\)/i', $option, $matches); 123 | 124 | $results[$matches[1]] = $matches[2]; 125 | } else { 126 | $results[$option] = true; 127 | } 128 | } 129 | 130 | return $results; 131 | } 132 | 133 | /** 134 | * Add a foreign constraint field to the schema. 135 | * 136 | * @param array $segments 137 | */ 138 | private function addForeignConstraint(array $segments): void 139 | { 140 | $string = sprintf( 141 | "%s:foreign:references('id'):on('%s')", 142 | $segments['name'], 143 | $this->getTableNameFromForeignKey($segments['name']) 144 | ); 145 | 146 | $this->addField($this->parseSegments($string)); 147 | } 148 | 149 | /** 150 | * Try to figure out the name of a table from a foreign key. 151 | * Ex: user_id => users. 152 | * 153 | * @param string $key 154 | * 155 | * @return string 156 | */ 157 | private function getTableNameFromForeignKey(string $key): string 158 | { 159 | return Str::plural(str_replace('_id', '', $key)); 160 | } 161 | 162 | /** 163 | * Determine if the user wants a foreign constraint for the field. 164 | * 165 | * @param array $segments 166 | * 167 | * @return bool 168 | */ 169 | private function fieldNeedsForeignConstraint(array $segments): bool 170 | { 171 | return array_key_exists('foreign', $segments['options']); 172 | } 173 | } 174 | -------------------------------------------------------------------------------- /src/SyntaxBuilders/AbstractSintaxBuilder.php: -------------------------------------------------------------------------------- 1 | stringTypes = config('ha-generator.stringTypes'); 60 | $this->integerTypes = config('ha-generator.integerTypes'); 61 | $this->floatTypes = config('ha-generator.floatTypes'); 62 | $this->dateTypes = config('ha-generator.dateTypes'); 63 | $this->bluePrintTypes = config('ha-generator.bluePrintTypes'); 64 | } 65 | 66 | /** 67 | * Determine if field has foreign constraint. 68 | * 69 | * @param array $field 70 | * 71 | * @return bool 72 | */ 73 | protected function hasForeignConstraint(array $field): bool 74 | { 75 | return 'foreign' === $field['type']; 76 | } 77 | 78 | /** 79 | * Remove empty fields. 80 | * 81 | * @param array $fields 82 | * 83 | * @return array 84 | */ 85 | protected function removeEmpty(array $fields): array 86 | { 87 | foreach ($fields as $key => $field) { 88 | if ($field == '') { 89 | unset($fields[$key]); 90 | } 91 | } 92 | 93 | return $fields; 94 | } 95 | 96 | /** 97 | * @return mixed 98 | */ 99 | protected function resolveStubPath($stub) 100 | { 101 | return file_exists($customPath = base_path('stubs/ha-generator/'.trim($stub, '/'))) && config('ha-generator.customStubs') 102 | ? $customPath 103 | : static::STUB_DIR.$stub; 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /src/SyntaxBuilders/ControllerApiSyntaxBuilder.php: -------------------------------------------------------------------------------- 1 | createSchema($schema); 17 | } 18 | 19 | /** 20 | * Create the schema. 21 | * 22 | * @param array $schema 23 | * 24 | * @return string 25 | */ 26 | private function createSchema(array $schema): string 27 | { 28 | $fields = $this->constructSchema($schema); 29 | 30 | return $this->insert($fields)->into($this->getSchemaWrapper()); 31 | } 32 | 33 | /** 34 | * Store the given template, to be inserted somewhere. 35 | * 36 | * @param string $template 37 | * 38 | * @return ControllerApiSyntaxBuilder 39 | */ 40 | private function insert(string $template): ControllerApiSyntaxBuilder 41 | { 42 | $this->template = $template; 43 | 44 | return $this; 45 | } 46 | 47 | /** 48 | * Get the stored template, and insert into the given wrapper. 49 | * 50 | * @param string $wrapper 51 | * @param string $placeholder 52 | * 53 | * @return string 54 | */ 55 | private function into(string $wrapper, string $placeholder = 'column'): string 56 | { 57 | return str_replace('{{'.$placeholder.'}}', $this->template, $wrapper); 58 | } 59 | 60 | /** 61 | * Get the wrapper template. 62 | * 63 | * @return string 64 | */ 65 | private function getSchemaWrapper(): string 66 | { 67 | return file_get_contents($this->resolveStubPath('/app/Http/Controllers/ApiController.stub')); 68 | } 69 | 70 | /** 71 | * Construct the schema fields. 72 | * 73 | * @param array $schema 74 | * 75 | * @return string|array 76 | */ 77 | private function constructSchema(array $schema) 78 | { 79 | if (!$schema) { 80 | return ''; 81 | } 82 | 83 | $fields = array_map(function ($field) { 84 | return $this->addColumn($field); 85 | }, $schema); 86 | 87 | return implode("\n".str_repeat(' ', 5).'*'.str_repeat(' ', 13), $this->removeEmpty($fields)); 88 | } 89 | 90 | /** 91 | * Construct the syntax to add a column. 92 | * 93 | * @param array $field 94 | * 95 | * @return string 96 | */ 97 | private function addColumn(array $field): string 98 | { 99 | if ($this->hasForeignConstraint($field)) { 100 | return ''; 101 | } 102 | 103 | return sprintf("@OA\Property(property=\"%s\", type=\"%s\"),", $field['name'], $this->getDataType($field['type'])); 104 | } 105 | 106 | /** 107 | * @param string $fieldType 108 | * 109 | * @return string 110 | */ 111 | private function getDataType(string $fieldType): string 112 | { 113 | if (in_array($fieldType, $this->integerTypes)) { 114 | return 'integer'; 115 | } 116 | 117 | if (in_array($fieldType, $this->stringTypes)) { 118 | return 'string'; 119 | } 120 | 121 | if (in_array($fieldType, $this->floatTypes)) { 122 | return 'number'; 123 | } 124 | 125 | if (in_array($fieldType, ['boolean'])) { 126 | return 'boolean'; 127 | } 128 | 129 | return 'string'; 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /src/SyntaxBuilders/FactorySintaxeBuilder.php: -------------------------------------------------------------------------------- 1 | createSchema($schema); 19 | } 20 | 21 | /** 22 | * Create the schema for the data faker. 23 | * 24 | * @param array $schema 25 | * 26 | * @return string 27 | */ 28 | private function createSchema(array $schema): string 29 | { 30 | $fields = $this->constructSchema($schema); 31 | 32 | return $this->insert($fields)->into($this->getSchemaWrapper()); 33 | } 34 | 35 | /** 36 | * Store the given template, to be inserted somewhere. 37 | * 38 | * @param string $template 39 | * 40 | * @return FactorySintaxeBuilder 41 | */ 42 | private function insert(string $template): FactorySintaxeBuilder 43 | { 44 | $this->template = $template; 45 | 46 | return $this; 47 | } 48 | 49 | /** 50 | * Get the stored template, and insert into the given wrapper. 51 | * 52 | * @param string $wrapper 53 | * @param string $placeholder 54 | * 55 | * @return string 56 | */ 57 | private function into(string $wrapper, string $placeholder = 'column'): string 58 | { 59 | return str_replace('{{'.$placeholder.'}}', $this->template, $wrapper); 60 | } 61 | 62 | /** 63 | * Get the wrapper template. 64 | * 65 | * @return string 66 | */ 67 | private function getSchemaWrapper(): string 68 | { 69 | return file_get_contents($this->resolveStubPath('/database/factories/factory.stub')); 70 | } 71 | 72 | /** 73 | * Construct the schema fields. 74 | * 75 | * @param array $schema 76 | * 77 | * @return string|array 78 | */ 79 | private function constructSchema(array $schema) 80 | { 81 | if (!$schema) { 82 | return ''; 83 | } 84 | 85 | $fields = array_map(function ($field) { 86 | return $this->addColumn($field); 87 | }, $schema); 88 | 89 | return implode("\n".str_repeat(' ', 8), $this->removeEmpty($fields)); 90 | } 91 | 92 | /** 93 | * Construct the syntax to add a column. 94 | * 95 | * @param array $field 96 | * 97 | * @return string 98 | */ 99 | private function addColumn(array $field): string 100 | { 101 | if ($this->hasForeignConstraint($field)) { 102 | $foreignName = Str::studly(str_replace('_id', '', $field['name'])); 103 | 104 | return sprintf("'%s' => {{modelNamespace}}\\$foreignName::factory()->create(),", $field['name']); 105 | } 106 | 107 | if (strpos($field['name'], '_id')) { 108 | return ''; 109 | } 110 | 111 | return sprintf("'%s' => \$this->faker->%s,", $field['name'], $this->getFakerType($field)); 112 | } 113 | 114 | /** 115 | * @param array $field 116 | * 117 | * @return string 118 | */ 119 | private function getFakerType(array $field): string 120 | { 121 | $type = 'text('; 122 | 123 | if (in_array($field['type'], $this->stringTypes)) { 124 | $type = 'text('; 125 | } 126 | 127 | if (in_array($field['type'], $this->integerTypes)) { 128 | $type = 'randomNumber('; 129 | } 130 | 131 | if (in_array($field['type'], $this->floatTypes)) { 132 | $type = 'randomFloat('; 133 | 134 | if ($field['arguments']) { 135 | $type .= $field['arguments'][1].', 0, '.str_repeat('9', $field['arguments'][0] - $field['arguments'][1]).'.'.str_repeat('9', $field['arguments'][1]); 136 | } 137 | } 138 | 139 | if (in_array($field['type'], $this->dateTypes)) { 140 | $type = 'date('; 141 | } 142 | 143 | if (!in_array($field['type'], $this->floatTypes) && $field['arguments']) { 144 | $type .= implode(', ', $field['arguments']); 145 | } 146 | 147 | return $type.')'; 148 | } 149 | } 150 | -------------------------------------------------------------------------------- /src/SyntaxBuilders/LangSyntaxBuilder.php: -------------------------------------------------------------------------------- 1 | createSchema($schema); 17 | } 18 | 19 | /** 20 | * Create the schema for the lang array. 21 | * 22 | * @param array $schema 23 | * 24 | * @return string 25 | */ 26 | private function createSchema(array $schema): string 27 | { 28 | $fields = $this->constructSchema($schema); 29 | 30 | return $this->insert($fields)->into($this->getSchemaWrapper()); 31 | } 32 | 33 | /** 34 | * Store the given template, to be inserted somewhere. 35 | * 36 | * @param string $template 37 | * 38 | * @return LangSyntaxBuilder 39 | */ 40 | private function insert(string $template): LangSyntaxBuilder 41 | { 42 | $this->template = $template; 43 | 44 | return $this; 45 | } 46 | 47 | /** 48 | * Get the stored template, and insert into the given wrapper. 49 | * 50 | * @param string $wrapper 51 | * @param string $placeholder 52 | * 53 | * @return string 54 | */ 55 | private function into(string $wrapper, string $placeholder = 'column'): string 56 | { 57 | return str_replace('{{'.$placeholder.'}}', $this->template, $wrapper); 58 | } 59 | 60 | /** 61 | * Get the wrapper template. 62 | * 63 | * @return string 64 | */ 65 | private function getSchemaWrapper(): string 66 | { 67 | return file_get_contents($this->resolveStubPath('/resources/lang/lang.stub')); 68 | } 69 | 70 | /** 71 | * Construct the schema fields. 72 | * 73 | * @param array $schema 74 | * 75 | * @return string|array 76 | */ 77 | private function constructSchema(array $schema) 78 | { 79 | if (!$schema) { 80 | return ''; 81 | } 82 | 83 | $fields = array_map(function ($field) { 84 | return $this->addColumn($field); 85 | }, $schema); 86 | 87 | return implode("\n".str_repeat(' ', 4), $this->removeEmpty($fields)); 88 | } 89 | 90 | /** 91 | * Construct the syntax to add a column. 92 | * 93 | * @param array $field 94 | * 95 | * @return string 96 | */ 97 | private function addColumn(array $field): string 98 | { 99 | if ($this->hasForeignConstraint($field)) { 100 | return ''; 101 | } 102 | 103 | return sprintf("'%s' => '%s',", $field['name'], $field['name']); 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /src/SyntaxBuilders/MigrationSyntaxBuilder.php: -------------------------------------------------------------------------------- 1 | createSchemaForUpMethod($schema, $meta); 22 | $down = $this->createSchemaForDownMethod($schema, $meta); 23 | 24 | return compact('up', 'down'); 25 | } 26 | 27 | /** 28 | * Create the schema for the "up" method. 29 | * 30 | * @param array $schema 31 | * @param array $meta 32 | * 33 | * @throws GeneratorException 34 | * 35 | * @return string 36 | */ 37 | private function createSchemaForUpMethod(array $schema, array $meta): string 38 | { 39 | $fields = $this->constructSchema($schema); 40 | 41 | if ($meta['action'] == 'create') { 42 | return $this->insert($fields)->into($this->getCreateSchemaWrapper()); 43 | } 44 | 45 | if ($meta['action'] == 'add') { 46 | return $this->insert($fields)->into($this->getChangeSchemaWrapper()); 47 | } 48 | 49 | if ($meta['action'] == 'remove') { 50 | $fields = $this->constructSchema($schema, 'Drop'); 51 | 52 | return $this->insert($fields)->into($this->getChangeSchemaWrapper()); 53 | } 54 | 55 | // Otherwise, we have no idea how to proceed. 56 | throw new GeneratorException(); 57 | } 58 | 59 | /** 60 | * Construct the syntax for a down field. 61 | * 62 | * @param array $schema 63 | * @param array $meta 64 | * 65 | * @throws GeneratorException 66 | * 67 | * @return string 68 | */ 69 | private function createSchemaForDownMethod(array $schema, array $meta): string 70 | { 71 | // If the user created a table, then for the down 72 | // method, we should drop it. 73 | if ($meta['action'] == 'create') { 74 | return sprintf("Schema::dropIfExists('%s');", $meta['table']); 75 | } 76 | 77 | // If the user added columns to a table, then for 78 | // the down method, we should remove them. 79 | if ($meta['action'] == 'add') { 80 | $fields = $this->constructSchema($schema, 'Drop'); 81 | 82 | return $this->insert($fields)->into($this->getChangeSchemaWrapper()); 83 | } 84 | 85 | // If the user removed columns from a table, then for 86 | // the down method, we should add them back on. 87 | if ($meta['action'] == 'remove') { 88 | $fields = $this->constructSchema($schema); 89 | 90 | return $this->insert($fields)->into($this->getChangeSchemaWrapper()); 91 | } 92 | 93 | // Otherwise, we have no idea how to proceed. 94 | throw new GeneratorException(); 95 | } 96 | 97 | /** 98 | * Store the given template, to be inserted somewhere. 99 | * 100 | * @param string $template 101 | * 102 | * @return MigrationSyntaxBuilder 103 | */ 104 | private function insert(string $template): MigrationSyntaxBuilder 105 | { 106 | $this->template = $template; 107 | 108 | return $this; 109 | } 110 | 111 | /** 112 | * Get the stored template, and insert into the given wrapper. 113 | * 114 | * @param string $wrapper 115 | * @param string $placeholder 116 | * 117 | * @return string 118 | */ 119 | private function into(string $wrapper, string $placeholder = 'schema_up'): string 120 | { 121 | return str_replace('{{'.$placeholder.'}}', $this->template, $wrapper); 122 | } 123 | 124 | /** 125 | * Get the wrapper template for a "create" action. 126 | * 127 | * @return string 128 | */ 129 | private function getCreateSchemaWrapper(): string 130 | { 131 | return file_get_contents($this->resolveStubPath('/database/migrations/schema-create.stub')); 132 | } 133 | 134 | /** 135 | * Get the wrapper template for an "add" action. 136 | * 137 | * @return string 138 | */ 139 | private function getChangeSchemaWrapper(): string 140 | { 141 | return file_get_contents($this->resolveStubPath('/database/migrations/schema-change.stub')); 142 | } 143 | 144 | /** 145 | * Construct the schema fields. 146 | * 147 | * @param array $schema 148 | * @param string $direction 149 | * 150 | * @return string|array 151 | */ 152 | private function constructSchema(array $schema, string $direction = 'Add') 153 | { 154 | if (!$schema) { 155 | return ''; 156 | } 157 | 158 | $fields = array_map(function ($field) use ($direction) { 159 | $method = "{$direction}Column"; 160 | 161 | return $this->$method($field); 162 | }, $schema); 163 | 164 | return implode("\n".str_repeat(' ', 12), $fields); 165 | } 166 | 167 | /** 168 | * Construct the syntax to add a column. 169 | * 170 | * @param array $field 171 | * 172 | * @return string 173 | */ 174 | private function addColumn(array $field): string 175 | { 176 | $syntax = sprintf("\$table->%s('%s')", $field['type'], $field['name']); 177 | 178 | // If there are arguments for the schema type, like decimal('amount', 5, 2) 179 | // then we have to remember to work those in. 180 | if ($field['arguments']) { 181 | $syntax = substr($syntax, 0, -1).', '; 182 | 183 | $syntax .= implode(', ', $field['arguments']).')'; 184 | } 185 | 186 | foreach ($field['options'] as $method => $value) { 187 | $syntax .= sprintf('->%s(%s)', $method, $value === true ? '' : $value); 188 | } 189 | 190 | return $syntax .= ';'; 191 | } 192 | 193 | /** 194 | * Construct the syntax to drop a column. 195 | * 196 | * @param string $field 197 | * 198 | * @return string 199 | */ 200 | private function dropColumn($field) 201 | { 202 | return sprintf("\$table->dropColumn('%s');", $field['name']); 203 | } 204 | } 205 | -------------------------------------------------------------------------------- /src/SyntaxBuilders/ModelSyntaxBuilder.php: -------------------------------------------------------------------------------- 1 | createSchemaForColumn($schema); 19 | $foreign = $this->createSchemaForForeign($schema); 20 | $searchable = $this->createSchemaForSearchable($schema); 21 | 22 | return compact('column', 'foreign', 'searchable'); 23 | } 24 | 25 | /** 26 | * Create the schema for the columns in getColumns. 27 | * 28 | * @param array $schema 29 | * 30 | * @return string 31 | */ 32 | private function createSchemaForColumn(array $schema): string 33 | { 34 | $fields = $this->constructSchema($schema); 35 | 36 | return $this->insert($fields)->into($this->getSchemaWrapper()); 37 | } 38 | 39 | /** 40 | * Create the schema for the foreign methods. 41 | * 42 | * @param array $schema 43 | * 44 | * @return string 45 | */ 46 | private function createSchemaForForeign(array $schema): string 47 | { 48 | return $this->constructSchema($schema, 'addForeign'); 49 | } 50 | 51 | /** 52 | * Create the schema for the api searchable method. 53 | * 54 | * @param array $schema 55 | * 56 | * @return string 57 | */ 58 | private function createSchemaForSearchable(array $schema): string 59 | { 60 | $fields = $this->constructSchema($schema, 'addSearchable'); 61 | 62 | return $this->insert($fields)->into($this->getSchemaWrapper('Searchables'), 'searchable'); 63 | } 64 | 65 | /** 66 | * Store the given template, to be inserted somewhere. 67 | * 68 | * @param string $template 69 | * 70 | * @return ModelSyntaxBuilder 71 | */ 72 | private function insert(string $template): ModelSyntaxBuilder 73 | { 74 | $this->template = $template; 75 | 76 | return $this; 77 | } 78 | 79 | /** 80 | * Get the stored template, and insert into the given wrapper. 81 | * 82 | * @param string $wrapper 83 | * @param string $placeholder 84 | * 85 | * @return string 86 | */ 87 | private function into(string $wrapper, string $placeholder = 'column'): string 88 | { 89 | return str_replace('{{'.$placeholder.'}}', $this->template, $wrapper); 90 | } 91 | 92 | /** 93 | * Get the wrapper template. 94 | * 95 | * @param string $type 96 | * 97 | * @return string 98 | */ 99 | private function getSchemaWrapper(string $type = 'Column'): string 100 | { 101 | return file_get_contents($this->resolveStubPath("/app/{$type}Model.stub")); 102 | } 103 | 104 | /** 105 | * Construct the schema fields. 106 | * 107 | * @param array $schema 108 | * @param string $method 109 | * 110 | * @return string|array 111 | */ 112 | private function constructSchema(array $schema, string $method = 'addColumn') 113 | { 114 | if (!$schema) { 115 | return ''; 116 | } 117 | 118 | $fields = array_map(function ($field) use ($method) { 119 | return $this->$method($field); 120 | }, $schema); 121 | 122 | return implode("\n".str_repeat(' ', 12), $this->removeEmpty($fields)); 123 | } 124 | 125 | /** 126 | * Construct the syntax to add a column. 127 | * 128 | * @param array $field 129 | * 130 | * @return string 131 | */ 132 | private function addColumn(array $field): string 133 | { 134 | if (array_key_exists('nullable', $field['options'])) { 135 | return ''; 136 | } 137 | 138 | if ($this->hasForeignConstraint($field)) { 139 | return ''; 140 | } 141 | 142 | return sprintf("['data' => '%s'],", $field['name']); 143 | } 144 | 145 | /** 146 | * Construct the syntax to add a foreign. 147 | * 148 | * @param array $field 149 | * 150 | * @return string 151 | */ 152 | private function addForeign(array $field): string 153 | { 154 | if (!$this->hasForeignConstraint($field)) { 155 | return ''; 156 | } 157 | 158 | $objectForeign = Str::singular(str_replace("'", '', $field['options']['on'])); 159 | 160 | return str_replace(['{{objectForeigntName}}', '{{ModelForeigntName}}'], [$objectForeign, ucwords($objectForeign)], $this->getSchemaWrapper('Foreign')); 161 | } 162 | 163 | /** 164 | * Construct the syntax to add a searchable. 165 | * 166 | * @param array $field 167 | * 168 | * @return string 169 | */ 170 | private function addSearchable(array $field): string 171 | { 172 | if (array_key_exists('nullable', $field['options'])) { 173 | return ''; 174 | } 175 | 176 | if ($this->hasForeignConstraint($field)) { 177 | return ''; 178 | } 179 | 180 | return sprintf("'%s' => 'like',", $field['name']); 181 | } 182 | 183 | /** 184 | * @param array $schema 185 | * 186 | * @return array 187 | */ 188 | private function getRequiredFields(array $schema): array 189 | { 190 | $fields = []; 191 | foreach ($schema as $field) { 192 | if (!array_key_exists('nullable', $field['options'])) { 193 | $fields[] = $field; 194 | } 195 | } 196 | 197 | return $fields; 198 | } 199 | } 200 | -------------------------------------------------------------------------------- /src/SyntaxBuilders/RequestSyntaxBuilder.php: -------------------------------------------------------------------------------- 1 | createSchema($schema); 17 | } 18 | 19 | /** 20 | * Create the schema for the array on rules method. 21 | * 22 | * @param array $schema 23 | * 24 | * @return string 25 | */ 26 | private function createSchema(array $schema): string 27 | { 28 | $fields = $this->constructSchema($schema); 29 | 30 | return $this->insert($fields)->into($this->getSchemaWrapper()); 31 | } 32 | 33 | /** 34 | * Store the given template, to be inserted somewhere. 35 | * 36 | * @param string $template 37 | * 38 | * @return RequestSyntaxBuilder 39 | */ 40 | private function insert(string $template): RequestSyntaxBuilder 41 | { 42 | $this->template = $template; 43 | 44 | return $this; 45 | } 46 | 47 | /** 48 | * Get the stored template, and insert into the given wrapper. 49 | * 50 | * @param string $wrapper 51 | * @param string $placeholder 52 | * 53 | * @return string 54 | */ 55 | private function into(string $wrapper, string $placeholder = 'rule'): string 56 | { 57 | return str_replace('{{'.$placeholder.'}}', $this->template, $wrapper); 58 | } 59 | 60 | /** 61 | * Get the wrapper template. 62 | * 63 | * @return string 64 | */ 65 | private function getSchemaWrapper(): string 66 | { 67 | return file_get_contents($this->resolveStubPath('/app/Http/Requests/Request.stub')); 68 | } 69 | 70 | /** 71 | * Construct the schema fields. 72 | * 73 | * @param array $schema 74 | * 75 | * @return string|array 76 | */ 77 | private function constructSchema(array $schema) 78 | { 79 | if (!$schema) { 80 | return ''; 81 | } 82 | 83 | $fields = array_map(function ($field) { 84 | return $this->addRule($field); 85 | }, $schema); 86 | 87 | return implode("\n".str_repeat(' ', 12), $this->removeEmpty($fields)); 88 | } 89 | 90 | /** 91 | * Construct the syntax to add a rule. 92 | * 93 | * @param array $field 94 | * 95 | * @return string 96 | */ 97 | private function addRule(array $field): string 98 | { 99 | if ($this->hasForeignConstraint($field)) { 100 | return ''; 101 | } 102 | 103 | return sprintf("'%s' => '%s',", $field['name'], $this->getRule($field)); 104 | } 105 | 106 | /** 107 | * @param array $field 108 | * 109 | * @return string 110 | */ 111 | private function getRule(array $field): string 112 | { 113 | $type = ''; 114 | 115 | if (array_key_exists('nullable', $field['options'])) { 116 | $type .= 'nullable|'; 117 | } 118 | 119 | if (!array_key_exists('nullable', $field['options'])) { 120 | $type .= 'required|'; 121 | } 122 | 123 | if (in_array($field['type'], $this->stringTypes)) { 124 | $type .= 'string|'; 125 | 126 | if ($field['arguments']) { 127 | $type .= 'max:'.$field['arguments'][0].'|'; 128 | } 129 | } 130 | 131 | if (in_array($field['type'], $this->integerTypes)) { 132 | $type .= 'integer|'; 133 | } 134 | 135 | if (in_array($field['type'], $this->floatTypes)) { 136 | $type .= 'numeric|'; 137 | } 138 | 139 | return rtrim($type, '|'); 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /src/SyntaxBuilders/TestSyntaxBuilder.php: -------------------------------------------------------------------------------- 1 | createSchema($schema, $api); 18 | } 19 | 20 | /** 21 | * Create the schema for the assert method. 22 | * 23 | * @param array $schema 24 | * @param bool $api 25 | * 26 | * @return string 27 | */ 28 | private function createSchema(array $schema, bool $api): string 29 | { 30 | $wrapperWithColumns = $this->createSchemaForColumn($schema, $api); 31 | 32 | $fields = $this->constructSchemaForRequired($schema, $api); 33 | 34 | return $this->insert($fields)->into($wrapperWithColumns, 'required'); 35 | } 36 | 37 | /** 38 | * Create the schema for the assert method. 39 | * 40 | * @param array $schema 41 | * @param bool $api 42 | * 43 | * @return string 44 | */ 45 | private function createSchemaForColumn(array $schema, bool $api): string 46 | { 47 | $fields = $this->constructSchemaForColumn($schema); 48 | 49 | return $this->insert($fields)->into($this->getSchemaWrapper($api)); 50 | } 51 | 52 | /** 53 | * Store the given template, to be inserted somewhere. 54 | * 55 | * @param string $template 56 | * 57 | * @return TestSyntaxBuilder 58 | */ 59 | private function insert(string $template): TestSyntaxBuilder 60 | { 61 | $this->template = $template; 62 | 63 | return $this; 64 | } 65 | 66 | /** 67 | * Get the stored template, and insert into the given wrapper. 68 | * 69 | * @param string $wrapper 70 | * @param string $placeholder 71 | * 72 | * @return string 73 | */ 74 | private function into(string $wrapper, string $placeholder = 'column'): string 75 | { 76 | return str_replace('{{'.$placeholder.'}}', $this->template, $wrapper); 77 | } 78 | 79 | /** 80 | * Get the wrapper template. 81 | * 82 | * @return string 83 | */ 84 | private function getSchemaWrapper(bool $api): string 85 | { 86 | $type = $api ? 'Api' : 'Web'; 87 | 88 | return file_get_contents(__DIR__."/../stubs//tests/Feature/{$type}Test.stub"); 89 | } 90 | 91 | /** 92 | * Construct the schema fields. 93 | * 94 | * @param array $schema 95 | * 96 | * @return string|array 97 | */ 98 | private function constructSchemaForColumn(array $schema): string 99 | { 100 | if (!$schema) { 101 | return ''; 102 | } 103 | 104 | $fields = array_map(function ($field) { 105 | return $this->addColumn($field); 106 | }, $schema); 107 | 108 | return implode("\n".str_repeat(' ', 20), $this->removeEmpty($fields)); 109 | } 110 | 111 | /** 112 | * Construct the schema fields. 113 | * 114 | * @param array $schema 115 | * @param bool $api 116 | * 117 | * @return string|array 118 | */ 119 | private function constructSchemaForRequired(array $schema, bool $api): string 120 | { 121 | if (!$schema) { 122 | return ''; 123 | } 124 | 125 | $fields = array_map(function ($field) use ($api) { 126 | return $this->addRequired($field, $api); 127 | }, $schema); 128 | 129 | return implode("\n".str_repeat(' ', 8), $this->removeEmpty($fields)); 130 | } 131 | 132 | /** 133 | * Construct the syntax to add a rule. 134 | * 135 | * @param array $field 136 | * 137 | * @return string 138 | */ 139 | private function addColumn(array $field): string 140 | { 141 | if ($this->hasForeignConstraint($field)) { 142 | return ''; 143 | } 144 | 145 | return sprintf("'%s',", $field['name']); 146 | } 147 | 148 | /** 149 | * Construct the syntax to add a required. 150 | * 151 | * @param array $field 152 | * @param bool $api 153 | * 154 | * @return string 155 | */ 156 | private function addRequired(array $field, bool $api): string 157 | { 158 | if ($this->hasForeignConstraint($field)) { 159 | return ''; 160 | } 161 | 162 | if (array_key_exists('nullable', $field['options'])) { 163 | return ''; 164 | } 165 | 166 | $assert = $api ? 'assertJsonValidationErrors' : 'assertSessionHasErrors'; 167 | 168 | return sprintf("\$response->{$assert}('%s');", $field['name']); 169 | } 170 | } 171 | -------------------------------------------------------------------------------- /src/SyntaxBuilders/ViewSyntaxBuilder.php: -------------------------------------------------------------------------------- 1 | viewName = $viewName; 35 | $this->files = $files; 36 | } 37 | 38 | /** 39 | * Create the PHP syntax for the given schema. 40 | * 41 | * @param array $schema 42 | * 43 | * @return string 44 | */ 45 | public function create(array $schema): string 46 | { 47 | return $this->createSchema($schema); 48 | } 49 | 50 | /** 51 | * Create the schema for the blade views. 52 | * 53 | * @param array $schema 54 | * 55 | * @return string 56 | */ 57 | private function createSchema(array $schema): string 58 | { 59 | $fields = $this->constructSchema($schema); 60 | 61 | return $this->insert($fields)->into($this->getSchemaWrapper()); 62 | } 63 | 64 | /** 65 | * Store the given template, to be inserted somewhere. 66 | * 67 | * @param string $template 68 | * 69 | * @return ViewSyntaxBuilder 70 | */ 71 | private function insert(string $template): ViewSyntaxBuilder 72 | { 73 | $this->template = $template; 74 | 75 | return $this; 76 | } 77 | 78 | /** 79 | * Get the stored template, and insert into the given wrapper. 80 | * 81 | * @param string $wrapper 82 | * @param string $placeholder 83 | * 84 | * @return string 85 | */ 86 | private function into(string $wrapper, string $placeholder = 'column'): string 87 | { 88 | return str_replace('{{'.$placeholder.'}}', $this->template, $wrapper); 89 | } 90 | 91 | /** 92 | * Get the wrapper template. 93 | * 94 | * @return string 95 | */ 96 | private function getSchemaWrapper(): string 97 | { 98 | return file_get_contents($this->resolveStubPath("/resources/views/{$this->viewName}.blade.stub")); 99 | } 100 | 101 | /** 102 | * Construct the schema fields. 103 | * 104 | * @param array $schema 105 | * 106 | * @return string|array 107 | */ 108 | private function constructSchema(array $schema) 109 | { 110 | if (!$schema) { 111 | return ''; 112 | } 113 | 114 | $fields = array_map(function ($field) { 115 | return $this->addColumn($field); 116 | }, $schema); 117 | 118 | return implode("\n".str_repeat(' ', 8), $this->removeEmpty($fields)); 119 | } 120 | 121 | /** 122 | * Construct the syntax to add a column. 123 | * 124 | * @param array $field 125 | * 126 | * @throws FileNotFoundException 127 | * 128 | * @return string 129 | */ 130 | private function addColumn(array $field): string 131 | { 132 | if ($this->hasForeignConstraint($field)) { 133 | return ''; 134 | } 135 | 136 | return str_replace('{{column}}', $field['name'], $this->getElementStub($field)); 137 | } 138 | 139 | /** 140 | * @param array $field 141 | * 142 | * @throws FileNotFoundException 143 | * 144 | * @return string 145 | */ 146 | private function getElementStub(array $field): string 147 | { 148 | $element = 'text'; 149 | 150 | if (in_array($field['type'], $this->stringTypes)) { 151 | $element = 'text'; 152 | 153 | if ($field['type'] == 'text' || ($field['arguments'] && $field['arguments'][0] > 100)) { 154 | $element = 'textarea'; 155 | } 156 | } 157 | 158 | if (in_array($field['type'], $this->integerTypes)) { 159 | $element = 'number'; 160 | } 161 | 162 | if (in_array($field['type'], $this->floatTypes)) { 163 | $element = 'decimal'; 164 | } 165 | 166 | if (in_array($field['type'], $this->dateTypes)) { 167 | $element = 'date'; 168 | } 169 | 170 | $strSub = $this->files->get($this->resolveStubPath("/resources/views/elements/{$element}.blade.stub")); 171 | 172 | $strSub = str_replace('{{required}}', !array_key_exists('nullable', $field['options']) ? 'required' : '', $strSub); 173 | 174 | if (in_array($field['type'], $this->stringTypes)) { 175 | if ($field['arguments'] && $field['arguments'][0]) { 176 | $strSub = str_replace('{{maxlength}}', $field['arguments'][0], $strSub); 177 | } else { 178 | $strSub = str_replace('{{maxlength}}', '255', $strSub); 179 | } 180 | } 181 | 182 | if (in_array($field['type'], $this->integerTypes)) { 183 | if ($field['arguments'] && $field['arguments'][0]) { 184 | $strSub = str_replace('{{min}}', 'min="0"', $strSub); 185 | $strSub = str_replace('{{max}}', 'max="'.str_repeat('9', $field['arguments'][0]).'"', $strSub); 186 | } else { 187 | $strSub = str_replace('{{min}}', '', $strSub); 188 | $strSub = str_replace('{{max}}', '', $strSub); 189 | } 190 | } 191 | 192 | if (in_array($field['type'], $this->floatTypes)) { 193 | if ($field['arguments'] && $field['arguments'][0] && $field['arguments'][1]) { 194 | $strSub = str_replace('{{digits}}', str_repeat('0', $field['arguments'][0] - $field['arguments'][1]), $strSub); 195 | $strSub = str_replace('{{precision}}', str_repeat('0', $field['arguments'][1]), $strSub); 196 | } else { 197 | $strSub = str_replace('{{digits}}', str_repeat('0', 8), $strSub); 198 | $strSub = str_replace('{{precision}}', str_repeat('0', 2), $strSub); 199 | } 200 | } 201 | 202 | return $strSub; 203 | } 204 | } 205 | -------------------------------------------------------------------------------- /src/stubs/app/ApiModel.stub: -------------------------------------------------------------------------------- 1 | 'columnName', 'searchable' => true, 'orderable' => true, 'linkable' => false] 3 | * 4 | * searchable and orderable is true by default 5 | * linkable is false by default 6 | * 7 | * @return array[] 8 | */ 9 | public static function getColumns(): array 10 | { 11 | return [ 12 | ['data' => 'id', 'linkable' => true], 13 | {{column}} 14 | ]; 15 | } 16 | -------------------------------------------------------------------------------- /src/stubs/app/ForeignModel.stub: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * Get the {{objectForeigntName}} of {{modelName}}. 4 | */ 5 | public function {{objectForeigntName}}() 6 | { 7 | return $this->belongsTo({{ModelForeigntName}}::class); 8 | } 9 | -------------------------------------------------------------------------------- /src/stubs/app/Http/Controllers/ApiController.stub: -------------------------------------------------------------------------------- 1 | has('limit') ? request()->get('limit') : null; 87 | return new {{modelName}}Collection({{modelName}}::paginate($limit)); 88 | } 89 | 90 | /** 91 | * @OA\Post( 92 | * tags={"{{packageRouteName}}/{{routeName}}"}, 93 | * summary="Store a newly created resource in storage.", 94 | * description="store a new {{objectName}} on database", 95 | * path="{{packageRouteName}}/{{routeName}}", 96 | * @OA\RequestBody( 97 | * required=true, 98 | * @OA\JsonContent( 99 | * type="object", 100 | * {{column}} 101 | * ) 102 | * ), 103 | * @OA\Response( 104 | * response="201", description="New {{objectName}} created" 105 | * ) 106 | * ) 107 | * 108 | * @param {{modelName}}StoreRequest $request 109 | * @return {{modelName}}Resource 110 | */ 111 | public function store({{modelName}}StoreRequest $request) 112 | { 113 | return new {{modelName}}Resource({{modelName}}::create($request->validated())); 114 | } 115 | 116 | /** 117 | * @OA\Get( 118 | * tags={"{{packageRouteName}}/{{routeName}}"}, 119 | * summary="Display the specified resource.", 120 | * description="show {{objectName}}", 121 | * path="{{packageRouteName}}/{{routeName}}/{{{objectName}}}", 122 | * @OA\Parameter( 123 | * description="{{modelName}} id", 124 | * in="path", 125 | * name="{{objectName}}", 126 | * required=true, 127 | * @OA\Schema( 128 | * type="integer", 129 | * format="int64" 130 | * ) 131 | * ), 132 | * @OA\Parameter( 133 | * name="only", 134 | * in="query", 135 | * description="filter results using field1;field2;field3...", 136 | * @OA\Schema(type="string"), 137 | * style="form" 138 | * ), 139 | * @OA\Parameter( 140 | * name="with", 141 | * in="query", 142 | * description="get relations using relation1;relation2;relation3...", 143 | * @OA\Schema(type="string"), 144 | * style="form" 145 | * ), 146 | * @OA\Response( 147 | * response="200", description="Show {{objectName}}" 148 | * ) 149 | * ) 150 | * 151 | * @param {{modelName}} ${{objectName}} 152 | * @return {{modelName}}Resource 153 | */ 154 | public function show({{modelName}} ${{objectName}}) 155 | { 156 | return new {{modelName}}Resource(${{objectName}}); 157 | } 158 | 159 | /** 160 | * @OA\Put( 161 | * tags={"{{packageRouteName}}/{{routeName}}"}, 162 | * summary="Update the specified resource in storage", 163 | * description="update a {{objectName}} on database", 164 | * path="{{packageRouteName}}/{{routeName}}/{{{objectName}}}", 165 | * @OA\Parameter( 166 | * description="{{modelName}} id", 167 | * in="path", 168 | * name="{{objectName}}", 169 | * required=true, 170 | * @OA\Schema( 171 | * type="integer", 172 | * format="int64" 173 | * ) 174 | * ), 175 | * @OA\RequestBody( 176 | * required=true, 177 | * @OA\JsonContent( 178 | * type="object", 179 | * {{column}} 180 | * ) 181 | * ), 182 | * @OA\Response( 183 | * response="201", description="{{modelName}} updated" 184 | * ) 185 | * ) 186 | * 187 | * @param {{modelName}}UpdateRequest $request 188 | * @param {{modelName}} ${{objectName}} 189 | * 190 | * @return {{modelName}}Resource 191 | */ 192 | public function update({{modelName}}UpdateRequest $request, {{modelName}} ${{objectName}}) 193 | { 194 | ${{objectName}}->update($request->validated()); 195 | 196 | return new {{modelName}}Resource(${{objectName}}); 197 | } 198 | 199 | /** 200 | * @OA\Delete( 201 | * tags={"{{packageRouteName}}/{{routeName}}"}, 202 | * summary="Remove the specified resource from storage.", 203 | * description="remove a {{objectName}} on database", 204 | * path="{{packageRouteName}}/{{routeName}}/{{{objectName}}}", 205 | * @OA\Parameter( 206 | * description="{{modelName}} id", 207 | * in="path", 208 | * name="{{objectName}}", 209 | * required=true, 210 | * @OA\Schema( 211 | * type="integer", 212 | * format="int64" 213 | * ) 214 | * ), 215 | * @OA\Response( 216 | * response="200", description="{{modelName}} deleted" 217 | * ) 218 | * ) 219 | * 220 | * @param {{modelName}} ${{objectName}} 221 | * 222 | * @return JsonResponse 223 | * @throws Exception 224 | */ 225 | public function destroy({{modelName}} ${{objectName}}) 226 | { 227 | ${{objectName}}->delete(); 228 | 229 | return response()->json(null, 204); 230 | } 231 | } 232 | -------------------------------------------------------------------------------- /src/stubs/app/Http/Controllers/WebController.stub: -------------------------------------------------------------------------------- 1 | validated()); 57 | return redirect() 58 | ->route('{{routeName}}.show', ${{objectName}}) 59 | ->with([ 60 | 'type' => 'success', 61 | 'message' => __('messages.success.store', [ 62 | 'item' => __('{{routeName}}.show', ['{{objectName}}' => ${{objectName}}->id]), 63 | ]), 64 | ]); 65 | } catch (\Exception $exception) { 66 | return redirect() 67 | ->back() 68 | ->with([ 69 | 'type' => 'danger', 70 | 'message' => __('messages.danger.store', [ 71 | 'item' => __('{{routeName}}.model'), 72 | ]), 73 | 'error' => $exception->getMessage(), 74 | ]) 75 | ->withInput(); 76 | } 77 | } 78 | 79 | /** 80 | * Display the specified resource. 81 | * 82 | * @param {{modelName}} ${{objectName}} 83 | * 84 | * @return Factory|View 85 | */ 86 | public function show({{modelName}} ${{objectName}}) 87 | { 88 | return view('{{routeName}}.show', compact('{{objectName}}')); 89 | } 90 | 91 | /** 92 | * Show the form for editing the specified resource. 93 | * 94 | * @param {{modelName}} ${{objectName}} 95 | * 96 | * @return Factory|View 97 | */ 98 | public function edit({{modelName}} ${{objectName}}) 99 | { 100 | return view('{{routeName}}.edit', compact('{{objectName}}')); 101 | } 102 | 103 | /** 104 | * Update the specified resource in storage 105 | * 106 | * @param {{modelName}}UpdateRequest $request 107 | * @param {{modelName}} ${{objectName}} 108 | * 109 | * @return RedirectResponse 110 | */ 111 | public function update({{modelName}}UpdateRequest $request, {{modelName}} ${{objectName}}) 112 | { 113 | try { 114 | ${{objectName}}->update($request->validated()); 115 | return redirect() 116 | ->route('{{routeName}}.show', ${{objectName}}) 117 | ->with([ 118 | 'type' => 'success', 119 | 'message' => __('messages.success.update', [ 120 | 'item' => __('{{routeName}}.show', ['{{objectName}}' => ${{objectName}}->id]), 121 | ]), 122 | ]); 123 | } catch (\Exception $exception) { 124 | return redirect() 125 | ->back() 126 | ->with([ 127 | 'type' => 'danger', 128 | 'message' => __('messages.danger.update', [ 129 | 'item' => __('{{routeName}}.show', ['{{objectName}}' => ${{objectName}}->id]), 130 | ]), 131 | 'error' => $exception->getMessage(), 132 | ]) 133 | ->withInput(); 134 | } 135 | } 136 | 137 | /** 138 | * Show the form for deleting the specified resource. 139 | * 140 | * @param {{modelName}} ${{objectName}} 141 | * 142 | * @return Factory|View 143 | */ 144 | public function delete({{modelName}} ${{objectName}}) 145 | { 146 | return view('{{routeName}}.delete', compact('{{objectName}}')); 147 | } 148 | 149 | /** 150 | * Remove the specified resource from storage. 151 | * 152 | * @param {{modelName}} ${{objectName}} 153 | * 154 | * @return RedirectResponse 155 | */ 156 | public function destroy({{modelName}} ${{objectName}}) 157 | { 158 | try { 159 | ${{objectName}}->delete(); 160 | return redirect() 161 | ->route('{{routeName}}.index') 162 | ->with([ 163 | 'type' => 'success', 164 | 'message' => __('messages.success.destroy', [ 165 | 'item' => __('{{routeName}}.show', ['{{objectName}}' => ${{objectName}}->id]), 166 | ]), 167 | ]); 168 | } catch (\Exception $exception) { 169 | return redirect() 170 | ->back() 171 | ->with([ 172 | 'type' => 'danger', 173 | 'message' => __('messages.danger.destroy', [ 174 | 'item' => __('{{routeName}}.show', ['{{objectName}}' => ${{objectName}}->id]), 175 | ]), 176 | 'error' => $exception->getMessage(), 177 | ]) 178 | ->withInput(); 179 | } 180 | } 181 | } 182 | -------------------------------------------------------------------------------- /src/stubs/app/Http/Controllers/WebControllerDatatables.stub: -------------------------------------------------------------------------------- 1 | validated()); 67 | return redirect() 68 | ->route('{{routeName}}.show', ${{objectName}}) 69 | ->with([ 70 | 'type' => 'success', 71 | 'message' => __('messages.success.store', [ 72 | 'item' => __('{{routeName}}.show', ['{{objectName}}' => ${{objectName}}->id]), 73 | ]), 74 | ]); 75 | } catch (\Exception $exception) { 76 | return redirect() 77 | ->back() 78 | ->with([ 79 | 'type' => 'danger', 80 | 'message' => __('messages.danger.store', [ 81 | 'item' => __('{{routeName}}.model'), 82 | ]), 83 | 'error' => $exception->getMessage(), 84 | ]) 85 | ->withInput(); 86 | } 87 | } 88 | 89 | /** 90 | * Display the specified resource. 91 | * 92 | * @param {{modelName}} ${{objectName}} 93 | * 94 | * @return Factory|View 95 | */ 96 | public function show({{modelName}} ${{objectName}}) 97 | { 98 | return view('{{routeName}}.show', compact('{{objectName}}')); 99 | } 100 | 101 | /** 102 | * Show the form for editing the specified resource. 103 | * 104 | * @param {{modelName}} ${{objectName}} 105 | * 106 | * @return Factory|View 107 | */ 108 | public function edit({{modelName}} ${{objectName}}) 109 | { 110 | return view('{{routeName}}.edit', compact('{{objectName}}')); 111 | } 112 | 113 | /** 114 | * Update the specified resource in storage 115 | * 116 | * @param {{modelName}}UpdateRequest $request 117 | * @param {{modelName}} ${{objectName}} 118 | * 119 | * @return RedirectResponse 120 | */ 121 | public function update({{modelName}}UpdateRequest $request, {{modelName}} ${{objectName}}) 122 | { 123 | try { 124 | ${{objectName}}->update($request->validated()); 125 | return redirect() 126 | ->route('{{routeName}}.show', ${{objectName}}) 127 | ->with([ 128 | 'type' => 'success', 129 | 'message' => __('messages.success.update', [ 130 | 'item' => __('{{routeName}}.show', ['{{objectName}}' => ${{objectName}}->id]), 131 | ]), 132 | ]); 133 | } catch (\Exception $exception) { 134 | return redirect() 135 | ->back() 136 | ->with([ 137 | 'type' => 'danger', 138 | 'message' => __('messages.danger.update', [ 139 | 'item' => __('{{routeName}}.show', ['{{objectName}}' => ${{objectName}}->id]), 140 | ]), 141 | 'error' => $exception->getMessage(), 142 | ]) 143 | ->withInput(); 144 | } 145 | } 146 | 147 | /** 148 | * Show the form for deleting the specified resource. 149 | * 150 | * @param {{modelName}} ${{objectName}} 151 | * 152 | * @return Factory|View 153 | */ 154 | public function delete({{modelName}} ${{objectName}}) 155 | { 156 | return view('{{routeName}}.delete', compact('{{objectName}}')); 157 | } 158 | 159 | /** 160 | * Remove the specified resource from storage. 161 | * 162 | * @param {{modelName}} ${{objectName}} 163 | * 164 | * @return RedirectResponse 165 | */ 166 | public function destroy({{modelName}} ${{objectName}}) 167 | { 168 | try { 169 | ${{objectName}}->delete(); 170 | return redirect() 171 | ->route('{{routeName}}.index') 172 | ->with([ 173 | 'type' => 'success', 174 | 'message' => __('messages.success.destroy', [ 175 | 'item' => __('{{routeName}}.show', ['{{objectName}}' => ${{objectName}}->id]), 176 | ]), 177 | ]); 178 | } catch (\Exception $exception) { 179 | return redirect() 180 | ->back() 181 | ->with([ 182 | 'type' => 'danger', 183 | 'message' => __('messages.danger.destroy', [ 184 | 'item' => __('{{routeName}}.show', ['{{objectName}}' => ${{objectName}}->id]), 185 | ]), 186 | 'error' => $exception->getMessage(), 187 | ]) 188 | ->withInput(); 189 | } 190 | } 191 | } 192 | -------------------------------------------------------------------------------- /src/stubs/app/Http/Requests/Request.stub: -------------------------------------------------------------------------------- 1 | '=', 8 | {{searchable}} 9 | ]; 10 | } 11 | -------------------------------------------------------------------------------- /src/stubs/app/WebModel.stub: -------------------------------------------------------------------------------- 1 | bigInteger('{{columnOne}}_id')->unsigned()->index(); 17 | $table->foreign('{{columnOne}}_id')->references('id')->on('{{tableOne}}')->onDelete('cascade'); 18 | $table->bigInteger('{{columnTwo}}_id')->unsigned()->index(); 19 | $table->foreign('{{columnTwo}}_id')->references('id')->on('{{tableTwo}}')->onDelete('cascade'); 20 | $table->primary(['{{columnOne}}_id', '{{columnTwo}}_id']); 21 | }); 22 | } 23 | 24 | /** 25 | * Reverse the migrations. 26 | * 27 | * @return void 28 | */ 29 | public function down() 30 | { 31 | Schema::dropIfExists('{{pivotTableName}}'); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/stubs/database/migrations/schema-change.stub: -------------------------------------------------------------------------------- 1 | Schema::table('{{table}}', function (Blueprint $table) { 2 | {{schema_up}} 3 | }); -------------------------------------------------------------------------------- /src/stubs/database/migrations/schema-create.stub: -------------------------------------------------------------------------------- 1 | Schema::create('{{table}}', function (Blueprint $table) { 2 | $table->bigIncrements('id'); 3 | {{schema_up}} 4 | $table->timestamps(); 5 | }); 6 | -------------------------------------------------------------------------------- /src/stubs/packages/README.stub: -------------------------------------------------------------------------------- 1 | # Laravelha {{packageName}} 2 | Laravelha {{packageName}}. 3 | 4 | -------------------------------------------------------------------------------- /src/stubs/packages/composer.stub: -------------------------------------------------------------------------------- 1 | { 2 | "name": "{{packagesVendor}}{{folderName}}", 3 | "description": "Laravelha {{packageName}}", 4 | "keywords": ["Laravelha", "Laravel", "{{packageName}}"], 5 | "require": { 6 | "php": "^7.2", 7 | "illuminate/support": "^6.0", 8 | "laravelha/support": "^1.0.0" 9 | }, 10 | "require-dev": { 11 | "orchestra/testbench": "^4.0", 12 | "phpunit/phpunit": "^8.5" 13 | }, 14 | "autoload": { 15 | "psr-4": { 16 | "{{packagesNamespace}}\\": "src/" 17 | } 18 | }, 19 | "autoload-dev": { 20 | "psr-4": { 21 | "{{packagesNamespace}}\\Tests\\": "tests/" 22 | } 23 | }, 24 | "extra": { 25 | "laravel": { 26 | "providers": [ 27 | "{{packagesNamespace}}\\Providers\\{{packageName}}ServiceProvider", 28 | "{{packagesNamespace}}\\Providers\\RouteServiceProvider" 29 | ] 30 | } 31 | }, 32 | "minimum-stability": "dev", 33 | "prefer-stable": true 34 | } 35 | -------------------------------------------------------------------------------- /src/stubs/packages/gitignore.stub: -------------------------------------------------------------------------------- 1 | /vendor 2 | .phpunit.result.cache 3 | composer.lock 4 | -------------------------------------------------------------------------------- /src/stubs/packages/phpunit.stub: -------------------------------------------------------------------------------- 1 | 2 | 13 | 14 | 15 | tests/ 16 | 17 | 18 | 19 | 20 | src/ 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /src/stubs/packages/src/Config/config.stub: -------------------------------------------------------------------------------- 1 | mapApiRoutes(); 41 | 42 | $this->mapWebRoutes(); 43 | 44 | // 45 | } 46 | 47 | /** 48 | * Define the "web" routes for the application. 49 | * 50 | * These routes all receive session state, CSRF protection, etc. 51 | * 52 | * @return void 53 | */ 54 | protected function mapWebRoutes() 55 | { 56 | Route::prefix('{{routeName}}') 57 | ->as('{{routeName}}.') 58 | ->middleware('web') 59 | ->namespace($this->namespace) 60 | ->group(self::SRC_PATH.'/../{{packageRoutesFolder}}/web.php'); 61 | } 62 | 63 | /** 64 | * Define the "api" routes for the application. 65 | * 66 | * These routes are typically stateless. 67 | * 68 | * @return void 69 | */ 70 | protected function mapApiRoutes() 71 | { 72 | Route::prefix('api/{{routeName}}') 73 | ->as('api.{{routeName}}.') 74 | ->middleware('api') 75 | ->namespace($this->namespace) 76 | ->group(self::SRC_PATH.'/../{{packageRoutesFolder}}/api.php'); 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /src/stubs/packages/src/Providers/ServiceProvider.stub: -------------------------------------------------------------------------------- 1 | loadViewsFrom(self::SRC_PATH.'/../{{packageViewsFolder}}', '{{folderName}}'); 22 | $this->loadTranslationsFrom(self::SRC_PATH.'/../{{packageLangsFolder}}', '{{folderName}}'); 23 | $this->loadMigrationsFrom(self::SRC_PATH.'/../{{packageMigrationsFolder}}'); 24 | $this->app->make(Factory::class)->load(self::SRC_PATH.'/../{{packageFactoriesFolder}}'); 25 | 26 | $this->publishes([ 27 | self::SRC_PATH.'/../{{packageViewsFolder}}' => resource_path('views/vendor/{{folderName}}'), 28 | ], '{{folderName}}-views'); 29 | 30 | $this->publishes([ 31 | self::SRC_PATH.'/../{{packageLangsFolder}}' => resource_path('lang/vendor/{{folderName}}'), 32 | ], '{{folderName}}-lang'); 33 | 34 | $this->publishes([ 35 | self::SRC_PATH.'/../{{packageConfigsFolder}}/{{folderName}}.php' => config_path('{{folderName}}.php'), 36 | ], '{{folderName}}-config'); 37 | } 38 | 39 | /** 40 | * Register the package services. 41 | * 42 | * @return void 43 | */ 44 | public function register() 45 | { 46 | $this->mergeConfigFrom( 47 | self::SRC_PATH.'/../{{packageConfigsFolder}}/{{folderName}}.php', 48 | '{{folderName}}' 49 | ); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/stubs/packages/src/Routes/api.stub: -------------------------------------------------------------------------------- 1 | set('database.default', 'testing'); 43 | $app['config']->set('database.connections.testing', [ 44 | 'driver' => 'sqlite', 45 | 'database' => ':memory:', 46 | 'prefix' => '', 47 | ]); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/stubs/resources/lang/lang.stub: -------------------------------------------------------------------------------- 1 | '{{modelName}}', 5 | 'index' => '{{modelNamePlural}}', 6 | 'create' => 'Create', 7 | 'show' => '{{modelName}} :{{objectName}}', 8 | 'edit' => 'Edit {{modelName}}', 9 | 'delete' => 'Remove {{modelName}}', 10 | 'id' => 'id', 11 | 'created_at' => 'Created at', 12 | 'updated_at' => 'Update at', 13 | {{column}} 14 | ]; 15 | -------------------------------------------------------------------------------- /src/stubs/resources/views/create.blade.stub: -------------------------------------------------------------------------------- 1 | @extends('layouts.app') 2 | 3 | @section('breadcrumbs', Breadcrumbs::render('{{routeName}}.create')) 4 | 5 | @section('content') 6 |
7 |
8 |
9 |
10 |

@lang('app.create.title', ['model' => __('{{tableName}}.model')])

11 |
12 |
13 |
14 | @csrf 15 | {{column}} 16 |
17 | 18 | 19 |
20 |
21 |
22 |
23 |
24 |
25 | @endsection 26 | -------------------------------------------------------------------------------- /src/stubs/resources/views/delete.blade.stub: -------------------------------------------------------------------------------- 1 | @extends('layouts.app') 2 | 3 | @section('breadcrumbs', Breadcrumbs::render('{{routeName}}.delete', ${{objectName}})) 4 | 5 | @section('content') 6 |
7 |
8 |
9 |
10 |

@lang('app.delete.title', ['id' => ${{objectName}}->id])

11 |

@lang('app.delete.question', ['item' => __('{{routeName}}.show', ['{{objectName}}' => ${{objectName}}->id ])])

12 |
13 |
14 |
15 |
16 | @foreach(${{objectName}}->getAttributes() as $key => $value) 17 |
@lang('{{tableName}}.'.$key)
18 |
{{ $value }}
19 | @endforeach 20 |
21 |
22 |
23 |
24 | @method('delete') 25 | @csrf 26 | 27 | 28 |
29 |
30 |
31 |
32 |
33 |
34 | @endsection 35 | 36 | -------------------------------------------------------------------------------- /src/stubs/resources/views/edit.blade.stub: -------------------------------------------------------------------------------- 1 | @extends('layouts.app') 2 | 3 | @section('breadcrumbs', Breadcrumbs::render('{{routeName}}.edit', ${{objectName}})) 4 | 5 | @section('content') 6 |
7 |
8 |
9 |
10 |

@lang('app.edit.title', ['id' => ${{objectName}}->id])

11 |
12 |
13 |
14 | @method('patch') 15 | @csrf 16 | {{column}} 17 |
18 | 19 | 20 |
21 |
22 |
23 |
24 |
25 |
26 | @endsection 27 | -------------------------------------------------------------------------------- /src/stubs/resources/views/elements/check.blade.stub: -------------------------------------------------------------------------------- 1 |
2 | 3 | 4 | @if ($errors->has('{{column}}')) 5 | 6 | {{ $errors->first('{{column}}') }} 7 | 8 | @endif 9 |
10 | -------------------------------------------------------------------------------- /src/stubs/resources/views/elements/date.blade.stub: -------------------------------------------------------------------------------- 1 |
2 | 3 | 4 | @if ($errors->has('{{column}}')) 5 | 6 | {{ $errors->first('{{column}}') }} 7 | 8 | @endif 9 |
10 | @push('scripts') 11 | 19 | @endpush 20 | -------------------------------------------------------------------------------- /src/stubs/resources/views/elements/decimal.blade.stub: -------------------------------------------------------------------------------- 1 |
2 | 3 | 4 | @if ($errors->has('{{column}}')) 5 | 6 | {{ $errors->first('{{column}}') }} 7 | 8 | @endif 9 |
10 | @push('scripts') 11 | 16 | @endpush 17 | -------------------------------------------------------------------------------- /src/stubs/resources/views/elements/number.blade.stub: -------------------------------------------------------------------------------- 1 |
2 | 3 | 4 | @if ($errors->has('{{column}}')) 5 | 6 | {{ $errors->first('{{column}}') }} 7 | 8 | @endif 9 |
10 | -------------------------------------------------------------------------------- /src/stubs/resources/views/elements/radio.blade.stub: -------------------------------------------------------------------------------- 1 |
2 | 3 | 4 | @if ($errors->has('{{column}}')) 5 | 6 | {{ $errors->first('{{column}}') }} 7 | 8 | @endif 9 |
10 | -------------------------------------------------------------------------------- /src/stubs/resources/views/elements/select.blade.stub: -------------------------------------------------------------------------------- 1 |
2 | 3 | 9 | @if ($errors->has('{{column}}')) 10 | 11 | {{ $errors->first('{{column}}') }} 12 | 13 |
14 | 15 | @push('scripts') 16 | 28 | @endpush 29 | -------------------------------------------------------------------------------- /src/stubs/resources/views/elements/text.blade.stub: -------------------------------------------------------------------------------- 1 |
2 | 3 | 4 | @if ($errors->has('{{column}}')) 5 | 6 | {{ $errors->first('{{column}}') }} 7 | 8 | @endif 9 |
10 | -------------------------------------------------------------------------------- /src/stubs/resources/views/elements/textarea.blade.stub: -------------------------------------------------------------------------------- 1 |
2 | 3 | 4 | @if ($errors->has('{{column}}')) 5 | 6 | {{ $errors->first('{{column}}') }} 7 | 8 | @endif 9 |
10 | -------------------------------------------------------------------------------- /src/stubs/resources/views/index.blade.stub: -------------------------------------------------------------------------------- 1 | @extends('layouts.app') 2 | 3 | @section('breadcrumbs', Breadcrumbs::render('{{routeName}}.index')) 4 | 5 | @section('content') 6 |
7 |
8 |
9 | 14 | 15 |
16 |
17 | 18 | 19 | 20 | @foreach(Schema::getColumnListing('{{tableName}}') as $column) 21 | 22 | @endforeach 23 | 24 | 25 | 26 | @foreach(${{routeName}} as ${{objectName}}) 27 | 28 | @foreach(${{objectName}}->toArray() as $key => $value) 29 | 40 | @endforeach 41 | 42 | @endforeach 43 | 44 |
@lang('{{tableName}}.'.$column)
30 | @if('id' === $key) 31 | 34 | {{ $value }} 35 | 36 | @else 37 | {{ $value }} 38 | @endif 39 |
45 |
46 | {{ ${{routeName}}->links() }} 47 |
48 |
49 |
50 |
51 | @endsection 52 | -------------------------------------------------------------------------------- /src/stubs/resources/views/indexDatatables.blade.stub: -------------------------------------------------------------------------------- 1 | @extends('layouts.app') 2 | 3 | @section('breadcrumbs', Breadcrumbs::render('{{routeName}}.index')) 4 | 5 | @section('content') 6 |
7 |
8 |
9 | 14 | 15 |
16 |
17 | 18 | 19 | 20 | @foreach($columns as $column) 21 | 22 | @endforeach 23 | 24 | 25 | 26 |
@lang('{{tableName}}.'.$column['data'])
27 |
28 |
29 |
30 |
31 |
32 | @endsection 33 | 34 | 35 | @push('scripts') 36 | 51 | @endpush 52 | -------------------------------------------------------------------------------- /src/stubs/resources/views/layouts/nav.stub: -------------------------------------------------------------------------------- 1 | 4 | -------------------------------------------------------------------------------- /src/stubs/resources/views/show.blade.stub: -------------------------------------------------------------------------------- 1 | @extends('layouts.app') 2 | 3 | @section('breadcrumbs', Breadcrumbs::render('{{routeName}}.show', ${{objectName}})) 4 | 5 | @section('content') 6 |
7 |
8 |
9 |
10 |

@lang('app.show.title', ['item' => __('{{routeName}}.show', ['{{objectName}}' => ${{objectName}}->id])])

11 |
12 |
13 |
14 | 19 | 24 |
25 |
26 |
27 | @foreach(${{objectName}}->toArray() as $key => $value) 28 |
@lang('{{tableName}}.'.$key)
29 |
{{ $value }}
30 | @endforeach 31 |
32 |
33 |
34 |
35 |
36 |
37 | @endsection 38 | -------------------------------------------------------------------------------- /src/stubs/tests/Feature/ApiTest.stub: -------------------------------------------------------------------------------- 1 | count(30)->create(); 22 | 23 | $this->assertCount(30, {{modelName}}::all()); 24 | git 25 | $response = $this->json('GET', self::BASE_URI); 26 | 27 | $response->assertJsonStructure([ 28 | 'data' => [ 29 | '*' => [ 30 | {{column}} 31 | ] 32 | ], 33 | 'links' => ['first', 'last', 'prev', 'next'], 34 | 'meta' => [ 35 | 'current_page', 'last_page', 'from', 'to', 36 | 'path', 'per_page', 'total' 37 | ] 38 | ]); 39 | } 40 | 41 | /** 42 | * @test 43 | */ 44 | public function checkRequiredFields() 45 | { 46 | $response = $this->json('POST', self::BASE_URI, []); 47 | 48 | {{required}} 49 | } 50 | 51 | /** 52 | * @test 53 | */ 54 | public function {{objectName}}CanBeCreated() 55 | { 56 | ${{objectName}}Fake = {{modelName}}::factory()->make(); 57 | 58 | $response = $this->json('POST', self::BASE_URI, ${{objectName}}Fake->toArray()); 59 | 60 | $response->assertStatus(201); 61 | 62 | $this->assertCount(1, {{modelName}}::all()); 63 | 64 | $this->assertDatabaseHas('{{tableName}}', ${{objectName}}Fake->getAttributes()); 65 | } 66 | 67 | /** 68 | * @test 69 | */ 70 | public function {{objectName}}CanBeDisplayed() 71 | { 72 | ${{objectName}}Fake ={{modelName}}::factory()->make(); 73 | $this->json('POST', self::BASE_URI, ${{objectName}}Fake->toArray()); 74 | 75 | ${{objectName}} = {{modelName}}::first(); 76 | 77 | $response = $this->json('GET', self::BASE_URI . '/' . ${{objectName}}->id); 78 | 79 | $response->assertStatus(200); 80 | } 81 | 82 | /** 83 | * @test 84 | */ 85 | public function {{objectName}}CanBeUpdated() 86 | { 87 | ${{objectName}}Fakes = {{modelName}}::factory()->count(2)->make(); 88 | $this->json('POST', self::BASE_URI, ${{objectName}}Fakes->first()->toArray()); 89 | 90 | ${{objectName}} = {{modelName}}::first(); 91 | 92 | $response = $this->json('PUT', self::BASE_URI . '/' . ${{objectName}}->id, ${{objectName}}Fakes->last()->toArray()); 93 | 94 | $response->assertStatus(200); 95 | 96 | $this->assertDatabaseHas('{{tableName}}', ${{objectName}}Fakes->last()->getAttributes()); 97 | } 98 | 99 | /** 100 | * @test 101 | */ 102 | public function {{objectName}}CanBeDeleted() 103 | { 104 | ${{objectName}}Fake = {{modelName}}::factory()->make(); 105 | $this->json('POST', self::BASE_URI, ${{objectName}}Fake->toArray()); 106 | 107 | $this->assertCount(1, {{modelName}}::all()); 108 | 109 | ${{objectName}} = {{modelName}}::first(); 110 | 111 | $response = $this->json('DELETE', self::BASE_URI . '/' . ${{objectName}}->id); 112 | 113 | $response->assertStatus(204); 114 | $this->assertCount(0, {{modelName}}::all()); 115 | 116 | $this->assertDatabaseMissing('{{tableName}}', ${{objectName}}Fake->getAttributes()); 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /src/stubs/tests/Feature/WebTest.stub: -------------------------------------------------------------------------------- 1 | post(self::BASE_URI, []); 22 | 23 | {{required}} 24 | } 25 | 26 | /** 27 | * @test 28 | */ 29 | public function {{objectName}}CanBeCreated() 30 | { 31 | ${{objectName}}Fake = {{modelName}}::factory()->make(); 32 | 33 | $response = $this->post(self::BASE_URI, ${{objectName}}Fake->toArray()); 34 | 35 | $response->assertSessionHas('type', 'success'); 36 | 37 | $this->assertCount(1, {{modelName}}::all()); 38 | 39 | $this->assertDatabaseHas('{{tableName}}', ${{objectName}}Fake->getAttributes()); 40 | } 41 | 42 | /** 43 | * @test 44 | */ 45 | public function {{objectName}}CanBeUpdated() 46 | { 47 | ${{objectName}} = {{modelName}}::factory()->make(); 48 | ${{objectName}}Fake = {{modelName}}::factory()->make(); 49 | 50 | $response = $this->put(self::BASE_URI . '/' . ${{objectName}}->id, ${{objectName}}Fake->toArray()); 51 | 52 | $response->assertSessionHas('type', 'success'); 53 | 54 | $this->assertDatabaseHas('{{tableName}}', ${{objectName}}Fake->getAttributes()); 55 | } 56 | 57 | /** 58 | * @test 59 | */ 60 | public function {{objectName}}CanBeDeleted() 61 | { 62 | ${{objectName}} = {{modelName}}::factory()->make(); 63 | 64 | $response = $this->delete(self::BASE_URI . '/' . ${{objectName}}->id); 65 | 66 | $response->assertSessionHas('type', 'success'); 67 | 68 | $this->assertDatabaseMissing('{{tableName}}', ${{objectName}}->getAttributes()); 69 | } 70 | } 71 | --------------------------------------------------------------------------------