├── .github └── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md ├── .gitignore ├── .travis.yml ├── CONTRIBUTING.MD ├── LICENSE ├── README.md ├── codecov.yml ├── composer.json ├── phpunit.xml ├── src ├── ApiCrudGeneratorServiceProvider.php ├── Commands │ └── ApiCrudGenerator.php ├── Core │ ├── Generator.php │ ├── Generators │ │ ├── ControllerGenerator.php │ │ ├── FactoryGenerator.php │ │ ├── ModelGenerator.php │ │ ├── RequestGenerator.php │ │ ├── ResourceGenerator.php │ │ ├── RouteGenerator.php │ │ └── TestGenerator.php │ └── Stub.php ├── Interfaces │ └── Generator.php └── stubs │ ├── Controller.stub │ ├── Factory.stub │ ├── Model.stub │ ├── Passport-Routes.stub │ ├── Request.stub │ ├── Resource.stub │ ├── Routes.stub │ └── Test.stub └── tests ├── GeneratorTest.php ├── Test.php └── Unit ├── ControllerGeneratorTest.php ├── FactoryGeneratorTest.php ├── ModelGeneratorTest.php ├── RequestGeneratorTest.php ├── ResourceGeneratorTest.php ├── RouteGeneratorTest.php ├── StubTest.php └── TestGeneratorTest.php /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Desktop (please complete the following information):** 27 | - OS: [e.g. iOS] 28 | - Browser [e.g. chrome, safari] 29 | - Version [e.g. 22] 30 | 31 | **Smartphone (please complete the following information):** 32 | - Device: [e.g. iPhone6] 33 | - OS: [e.g. iOS8.1] 34 | - Browser [e.g. stock browser, safari] 35 | - Version [e.g. 22] 36 | 37 | **Additional context** 38 | Add any other context about the problem here. 39 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | *.iml 3 | vendor/ 4 | composer.lock 5 | .php_cs.cache 6 | .phpunit.result.cache 7 | app/ 8 | database/ 9 | routes/ -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: php 2 | 3 | php: 4 | - 7.3 5 | - 7.4 6 | - 8.0 7 | 8 | before_script: 9 | - composer self-update 10 | - composer install --no-interaction 11 | 12 | script: 13 | - vendor/bin/phpunit --coverage-clover=coverage.xml 14 | 15 | after_success: 16 | - bash <(curl -s https://codecov.io/bash) 17 | -------------------------------------------------------------------------------- /CONTRIBUTING.MD: -------------------------------------------------------------------------------- 1 | # Contributing to Api Crud Generator 2 | Thank you for your interest in making Api-Crud-Generator better! There are several ways you can get involved. 3 | 4 | ## Reporting issues and suggesting new features 5 | If Api-Crud-Generator is not working properly, please open an issue and mark it as Bug. Please add steps to reproduce the bug. 6 | If you want to suggest new ideas, please mark it as enhancement. 7 | 8 | ## Finding issues you can help with 9 | 10 | Looking for something to work on? 11 | [Issues marked *good first issue*](https://github.com/AndreaCivita/Laravel-ApiCrudGenerator/labels/good%20first%20issue) 12 | are a good place to start. 13 | 14 | You can also check [the *help wanted* tag](https://github.com/AndreaCivita/Laravel-ApiCrudGenerator/labels/help%20wanted) 15 | to find other issues to help with. 16 | 17 | 18 | ## Making changes to the code 19 | 20 | Follow this steps for contributing: 21 | 0) If you are fixing an issue, please assign to yourself, else go to step 1 22 | 1) Fork this repo 23 | 2) Apply all changes 24 | 3) Write Tests. Test will help us so much! 25 | 4) Make a pull request. 26 | 27 | All pull request will be merged with 'dev' branch. The `master` branch should always be in a healthy state which is ready for release 28 | 29 | ### Submitting a pull request and participating in code review 30 | Writing a good description for your pull request is crucial to help reviewers and future 31 | maintainers understand your change. More detail is better. 32 | - [Link the issue you're addressing in the pull request](https://github.com/blog/957-introducing-issue-mentions). 33 | - Describe *why* the change is being made and *why* you've chosen a particular solution. 34 | - Describe any manual testing you performed to validate your change. 35 | 36 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Andrea Civita 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Build Status](https://travis-ci.org/andreacivita/Laravel-ApiCrudGenerator.svg?branch=master)](https://travis-ci.org/andreacivita/Laravel-ApiCrudGenerator) 2 | [![codecov](https://codecov.io/gh/andreacivita/Laravel-ApiCrudGenerator/branch/master/graph/badge.svg)](https://codecov.io/gh/andreacivita/Laravel-ApiCrudGenerator) 3 | 4 | [![Codacy Badge](https://api.codacy.com/project/badge/Grade/9f34629292c94cbbb29cc6de75465b24)](https://app.codacy.com/app/andreacivita/Laravel-ApiCrudGenerator?utm_source=github.com&utm_medium=referral&utm_content=andreacivita/Laravel-ApiCrudGenerator&utm_campaign=Badge_Grade_Dashboard) 5 | [![Latest Stable Version](https://poser.pugx.org/andreacivita/api-crud-generator/v/stable)](https://packagist.org/packages/andreacivita/api-crud-generator) 6 | [![License](https://poser.pugx.org/andreacivita/api-crud-generator/license)](https://packagist.org/packages/andreacivita/api-crud-generator) 7 | 8 | # Laravel | API CRUD Generator 9 | 10 | This Generator package provides generators of Models, Controllers, Request, Routes & Tests for a painless development. 11 | 12 | ## INSTALL 13 | 14 | Install the package through [Composer](https://getcomposer.org/). 15 | 16 | Run the Composer require command from the Terminal: 17 | 18 | ```sh 19 | composer require andreacivita/api-crud-generator --dev 20 | ``` 21 | 22 | ### SETUP 23 | 24 | Run this command from the Terminal 25 | 26 | ```sh 27 | php artisan vendor:publish 28 | ``` 29 | Select andreacivita/api-crud-generator and setup it's complete. 30 | 31 | 32 | ## USAGE 33 | 34 | ### Managing all database 35 | Usage of this package is very simple. 36 | 37 | First, let's supposing I want to generate CRUD for all table in my db. 38 | 39 | So, we run 40 | 41 | ```sh 42 | php artisan make:crud --all 43 | ``` 44 | 45 | No further options required. Your setup is complete! 46 | 47 | 48 | ### Interactive mode 49 | 50 | You can manage a single table with interactive mode or manually (see [next paragraph](https://github.com/andreacivita/Laravel-ApiCrudGenerator#managing-a-single-db-table)). 51 | 52 | Just run 53 | 54 | ```sh 55 | php artisan make:crud --interactive 56 | ``` 57 | 58 | Crud generator will ask you several data (Name of resource, Table name and use of timestamps). 59 | 60 | 61 | ### Managing a single db table 62 | 63 | Now i suppose generation of CRUD operations of Car db table. 64 | 65 | Run this command: 66 | 67 | ```sh 68 | php artisan make:crud Car 69 | ``` 70 | Done! You will have Car model (located in App/Model directory), CarController, CarRequest (used for input data) and Routes (located in routes/api.php). 71 | 72 | ### OPTIONS 73 | 74 | #### TABLE NAME 75 | By default, DB Table's name is plural, while Model class name is singular (e.g. Table => Cars, Model => Car). 76 | You can change this behavior specifying the name in terminal 77 | 78 | ```sh 79 | php artisan make:crud Car --table Car 80 | ``` 81 | This will create the same resources, but table name in model will be 'Car' (instead of default 'Cars') 82 | 83 | #### TIMESTAMPS 84 | 85 | By default, this packages will set all timestamps to false. You can change this doing this command: 86 | 87 | ```sh 88 | php artisan make:crud Car --timestamps true 89 | ``` 90 | 91 | This will set 'timestamps=true' in Model class. 92 | 93 | #### LARAVEL/PASSPORT INTEGRATION 94 | 95 | By default, Routes will be not protected by passport. However, you can generate Passport-protected routes with: 96 | 97 | ```sh 98 | php artisan make:crud Car --passport 99 | ``` 100 | 101 | This will set 'timestamps=true' in Model class. 102 | 103 | 104 | ## ROUTING 105 | 106 | Routes will follow Route::resource() Schema (default routing schema provided by Laravel). 107 | 108 | Example: i'm generating Car crud 109 | 110 | | Route | Method | Operation | 111 | | ------------- |:----------------:| ----------------:| 112 | | car | GET | Get all cars | 113 | | car/{id} | GET | Find car by id | 114 | | car | POST | Insert a new car | 115 | | car/{id} | PUT / PATCH | Update car by id | 116 | | car/{id} | DELETE | Delete car by id | 117 | 118 | 119 | Remember that all api routes have 'api/' prefix. 120 | 121 | ## TESTING 122 | 123 | When created CRUD structure (Controllers, Models, Request, Resource, Factory & Routes), this package generate Feature test file.
124 | 125 |
126 | 127 | ## SETUP GENERATED CRUD 128 | 129 | ### FACTORIES 130 | 131 | Tests now require factory class to manipulate data. 132 | You should provide data schema into your factory class (using Faker), so you'll be able to test easily your API 133 | 134 | ### VALIDATORS 135 | 136 | Write better code will helps you so much! So, default behavior of Controllers is to force you to use validated data. 137 | You have to set all validation rules into your FormRequest class. 138 | 139 | E.g. For Car Crud, you will set rules into your App\Request\CarRequest.php 140 | 141 |
142 | 143 | ## CONTRIBUTING 144 | 145 | This package is covered by MIT license. You are able to do whatever you want with this code. 146 | 147 | Please feel free to fork this package and contribute by submitting a pull request to enhance the functionalities. 148 | You can see issues or enhancement and assign task for contributing :) 149 | 150 | 151 | ## How can I thank you? 152 | 153 | Star this repo or follow me on GitHub. And, if you want, you can share this link! :) 154 | 155 |
156 | 157 | ## AUTHORS 158 | 159 | This package has been originally developed by [Andrea Civita](https://github.com/andreacivita)
160 | A special thanks goes to [Bastianjoel](https://github.com/bastianjoel) for it's pull request 161 | 162 | -------------------------------------------------------------------------------- /codecov.yml: -------------------------------------------------------------------------------- 1 | ignore: 2 | - "src/ApiCrudGeneratorServiceProvider.php" 3 | - " src/Commands/ApiCrudGenerator.php" 4 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "andreacivita/api-crud-generator", 3 | "description": "Simple API Crud generator for Laravel", 4 | "type": "library", 5 | "keywords": [ 6 | "laravel", 7 | "crud", 8 | "crud generator", 9 | "laravel crud generator", 10 | "api generator" 11 | ], 12 | "require": { 13 | "php": "^7.3|^8.0", 14 | "laravel/framework": "^8.0|^9.0", 15 | "doctrine/dbal": "^2.9", 16 | "illuminate/support": "^8.61" 17 | }, 18 | "require-dev": { 19 | "phpunit/phpunit": "^7|^8", 20 | "friendsofphp/php-cs-fixer": "^2.16" 21 | }, 22 | "license": "MIT", 23 | "authors": [ 24 | { 25 | "name": "Andrea Civita", 26 | "email": "andreacivita994@gmail.com", 27 | "homepage": "https://www.andreacivita.it/" 28 | } 29 | ], 30 | "minimum-stability": "dev", 31 | "prefer-stable": true, 32 | "autoload": { 33 | "psr-4": { 34 | "AndreaCivita\\ApiCrudGenerator\\": "src/" 35 | } 36 | }, 37 | "extra": { 38 | "laravel": { 39 | "providers": [ 40 | "AndreaCivita\\ApiCrudGenerator\\ApiCrudGeneratorServiceProvider" 41 | ] 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /phpunit.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | ./tests 6 | 7 | 8 | ./tests/Unit 9 | 10 | 11 | ./tests/Feature 12 | 13 | 14 | 15 | 16 | ./src 17 | 18 | src/ApiCrudGeneratorServiceProvider.php 19 | 20 | 21 | src/Commands/ApiCrudGenerator.php 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /src/ApiCrudGeneratorServiceProvider.php: -------------------------------------------------------------------------------- 1 | publishes([ 17 | __DIR__ . '/stubs/' => base_path('resources/stubs/'), 18 | ]); 19 | } 20 | 21 | /** 22 | * Register the service provider. 23 | * 24 | * @return void 25 | */ 26 | public function register() 27 | { 28 | $this->commands( 29 | 'AndreaCivita\ApiCrudGenerator\Commands\ApiCrudGenerator' 30 | ); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/Commands/ApiCrudGenerator.php: -------------------------------------------------------------------------------- 1 | controller = $controllerGenerator; 135 | $this->factory = $factoryGenerator; 136 | $this->model = $modelGenerator; 137 | $this->request = $requestGenerator; 138 | $this->resource = $resourceGenerator; 139 | $this->route = $routeGenerator; 140 | $this->test = $testGenerator; 141 | $this->str = $str; 142 | $this->schema = $schema; 143 | } 144 | 145 | /** 146 | * Execute the console command. 147 | * 148 | * @return int 149 | */ 150 | public function handle(): int 151 | { 152 | // Checking interactive mode 153 | if ($this->option('interactive') == "") { 154 | $this->interactive(); 155 | return 0; 156 | } 157 | 158 | // Checking all mode 159 | if ($this->option('all') == "") { 160 | $this->all(); 161 | return 0; 162 | } 163 | 164 | // Checking Passport mode 165 | if ($this->option('passport') == "") { 166 | $this->passport = true; 167 | } 168 | 169 | // If here, no interactive || all selected 170 | $name = ucwords($this->argument('name')); 171 | $table = $this->option('table'); 172 | $timestamps = $this->option('timestamps'); 173 | $this->generate($name, $table, $timestamps); 174 | return 0; 175 | } 176 | 177 | 178 | /** 179 | * Handle all-db generation 180 | */ 181 | protected function all() 182 | { 183 | try { 184 | $tables = DB::connection()->getDoctrineSchemaManager()->listTableNames(); 185 | foreach ($tables as $table) { 186 | $this->comment("Generating " . $table . " CRUD"); 187 | $columns = Schema::getColumnListing($table); 188 | $name = ucwords($this->str->singular($table)); 189 | in_array('created_at', $columns) ? $timestamps = true : $timestamps = false; 190 | $this->generate($name, $table, $timestamps); 191 | } 192 | } catch (QueryException $exception) { 193 | $this->error("Error: " . $exception->getMessage()); 194 | } 195 | } 196 | 197 | 198 | /** 199 | * Generate CRUD in interactive mode 200 | */ 201 | protected function interactive(): void 202 | { 203 | $this->info("Welcome in Interactive mode"); 204 | 205 | $this->comment("This command will guide you through creating your CRUD"); 206 | $name = $this->ask('What is name of your Model?'); 207 | $name = ucwords($name); 208 | $table = $this->ask("Table name [" . strtolower($this->str->plural($name)) . "]:"); 209 | if ($table == "") { 210 | $table = $this->str->plural($name); 211 | } 212 | $table = strtolower($table); 213 | $choice = $this->choice('Do your table has timestamps column?', ['No', 'Yes'], 0); 214 | $choice === "Yes" ? $timestamps = true : $timestamps = false; 215 | $this->info("Please confim this data"); 216 | $this->line("Name: $name"); 217 | $this->line("Table: $table"); 218 | $this->line("Timestamps: $choice"); 219 | 220 | $confirm = $this->ask("Press y to confirm, type N to restart"); 221 | if ($confirm == "y") { 222 | $this->generate($name, $table, $timestamps); 223 | return; 224 | } 225 | $this->error("Aborted!"); 226 | } 227 | 228 | 229 | /** 230 | * Handle data generation 231 | * @param $name string Model Name 232 | * @param $table string Table Name 233 | * @param $timestamps boolean 234 | */ 235 | protected function generate(string $name, string $table, bool $timestamps) 236 | { 237 | $this->controller->setData($name, $table)->generate(); 238 | $this->info("Generated Controller!"); 239 | 240 | $this->model->setData($name, $table, $timestamps)->generate(); 241 | $this->info("Generated Model!"); 242 | 243 | $this->request->setData($name)->generate(); 244 | $this->info("Generated Request!"); 245 | 246 | $this->resource->setData($name)->generate(); 247 | $this->info("Generated Resource!"); 248 | 249 | $this->route->setData($name, $this->passport)->generate(); 250 | $this->info("Generated routes!"); 251 | 252 | $this->factory->setData($name)->generate(); 253 | $this->info("Generated Factory!"); 254 | 255 | $this->test->setData($name)->generate(); 256 | $this->info("Generated Test!"); 257 | } 258 | } 259 | -------------------------------------------------------------------------------- /src/Core/Generator.php: -------------------------------------------------------------------------------- 1 | files = $files; 38 | $this->str = $str; 39 | $this->stub = $stub; 40 | } 41 | 42 | /** 43 | * Generate model class from stubs 44 | * 45 | * @param $name string name of model class 46 | * @param $table string name of DB table 47 | * @param $timestamps boolean set timestamps true | false 48 | * @return bool|int 49 | */ 50 | public function model(string $name, string $table, bool $timestamps) 51 | { 52 | $table === "default" ? $table = strtolower($this->str->plural($name)) : null; 53 | 54 | $timeDeclaration = ""; 55 | if ($timestamps === false) { 56 | $timeDeclaration = 'public $timestamps = false;'; 57 | } 58 | 59 | $content = $this->stub->parseStub('Model', $name, [ 60 | 'tableDeclaration' => $table, 61 | 'timestamps' => $timeDeclaration 62 | ]); 63 | 64 | if (!$this->files->exists("app/Models/")) { 65 | $this->files->makeDirectory("app/Models/"); 66 | } 67 | return $this->files->put("app/Models/{$name}.php", $content); 68 | } 69 | 70 | /** 71 | * Create controller from controller.stub 72 | * 73 | * @param $name string name of model class 74 | * @param $table string name of db table 75 | * @return bool|int 76 | */ 77 | public function controller(string $name, string $table) 78 | { 79 | $content = $this->stub->parseStub('Controller', $name, ['table' => $table]); 80 | 81 | return $this->files->put("app/Http/Controllers/{$name}Controller.php", $content); 82 | } 83 | 84 | /** 85 | * Generate Request from request.stub 86 | * 87 | * @param $name string 88 | * @return bool|int 89 | */ 90 | public function request(string $name) 91 | { 92 | $content = $this->stub->parseStub('Request', $name); 93 | 94 | if (!$this->files->exists("app/Http/Requests/")) { 95 | $this->files->makeDirectory("app/Http/Requests/"); 96 | } 97 | return $this->files->put("app/Http/Requests/{$name}Request.php", $content); 98 | } 99 | 100 | /** 101 | * Generate Resource from Resource.stub 102 | * 103 | * @param $name 104 | * @return bool|int 105 | */ 106 | public function resource($name) 107 | { 108 | $content = $this->stub->parseStub('Resource', $name); 109 | 110 | if (!$this->files->exists("app/Http/Resources/")) { 111 | $this->files->makeDirectory("app/Http/Resources/"); 112 | } 113 | return $this->files->put("app/Http/Resources/{$name}Resource.php", $content); 114 | } 115 | 116 | /** 117 | * Generate factory from Factory.stub 118 | * 119 | * @param $name string 120 | * @return int 121 | */ 122 | public function factory(string $name): int 123 | { 124 | $content = $this->stub->parseStub('Factory', $name); 125 | 126 | if (!$this->files->exists("database/factories/")) { 127 | $this->files->makeDirectory("database/factories/"); 128 | } 129 | return $this->files->put("database/factories/{$name}Factory.php", $content); 130 | } 131 | 132 | /** 133 | * Generate routes 134 | * 135 | * @param $name 136 | * @return int 137 | */ 138 | public function routes($name) 139 | { 140 | $content = $this->stub->parseStub('Routes', $name); 141 | 142 | return $this->files->append("routes/api.php", $content); 143 | } 144 | 145 | /** 146 | * @param $name string 147 | * @return int 148 | */ 149 | public function secureRoutes(string $name) : int 150 | { 151 | $content = $this->stub->parseStub('Passport-Routes', $name); 152 | 153 | return $this->files->append("routes/api.php", $content); 154 | } 155 | 156 | /** 157 | * Generate unit test 158 | * 159 | * @param $name string 160 | * @return int 161 | */ 162 | public function test(string $name) : int 163 | { 164 | $content = $this->stub->parseStub('Test', $name); 165 | 166 | if (!$this->files->exists("tests/Feature/")) { 167 | $this->files->makeDirectory("tests/Feature/"); 168 | } 169 | return $this->files->append("tests/Feature/{$name}Test.php", $content); 170 | } 171 | } 172 | -------------------------------------------------------------------------------- /src/Core/Generators/ControllerGenerator.php: -------------------------------------------------------------------------------- 1 | stub = $stub; 37 | $this->fileSystem = $this->stub->getFilesystemInstance(); 38 | } 39 | 40 | /** 41 | * @param string $name 42 | * @param string $table 43 | * @return ControllerGenerator 44 | */ 45 | public function setData(string $name, string $table): ControllerGenerator 46 | { 47 | $this->name = $name; 48 | $this->table = $table; 49 | return $this; 50 | } 51 | 52 | /** 53 | * @inheritDoc 54 | */ 55 | public function generate() 56 | { 57 | $content = $this->stub->parseStub('Controller', $this->name, ['table' => $this->table]); 58 | 59 | if (!$this->fileSystem->exists("app/Http/Controllers/")) { 60 | $this->fileSystem->makeDirectory("app/Http/Controllers/", 0755, true); 61 | } 62 | 63 | return $this->fileSystem->put("app/Http/Controllers/{$this->name}Controller.php", $content); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/Core/Generators/FactoryGenerator.php: -------------------------------------------------------------------------------- 1 | stub = $stub; 33 | $this->fileSystem = $this->stub->getFilesystemInstance(); 34 | } 35 | 36 | /** 37 | * @param string $name 38 | * @return $this 39 | */ 40 | public function setData(string $name): FactoryGenerator 41 | { 42 | $this->name = $name; 43 | return $this; 44 | } 45 | 46 | /** 47 | * @inheritDoc 48 | */ 49 | public function generate() 50 | { 51 | $content = $this->stub->parseStub('Factory', $this->name); 52 | 53 | if (!$this->fileSystem->exists("database/factories/")) { 54 | $this->fileSystem->makeDirectory("database/factories/", 0755, true); 55 | } 56 | return $this->fileSystem->put("database/factories/{$this->name}Factory.php", $content); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/Core/Generators/ModelGenerator.php: -------------------------------------------------------------------------------- 1 | stub = $stub; 48 | $this->fileSystem = $stub->getFilesystemInstance(); 49 | $this->str = $stub->getStrInstance(); 50 | } 51 | 52 | /** 53 | * @param string $name 54 | * @param string $table 55 | * @param bool $timestamps 56 | * @return $this 57 | */ 58 | public function setData(string $name, string $table, bool $timestamps): ModelGenerator 59 | { 60 | $this->name = $name; 61 | $this->table = $table; 62 | $this->timestamps = $timestamps; 63 | return $this; 64 | } 65 | 66 | 67 | /** 68 | * @inheritDoc 69 | */ 70 | public function generate() 71 | { 72 | $content = $this->stub->parseStub('Model', $this->name, [ 73 | 'tableDeclaration' => $this->table === "default" ? $this->str->lower($this->str->plural($this->name)) : null, 74 | 'timestamps' => $this->timestamps ? 'public $timestamps = false;' : '' 75 | ]); 76 | 77 | if (!$this->fileSystem->exists("app/Models/")) { 78 | $this->fileSystem->makeDirectory("app/Models/", 0755, true); 79 | } 80 | return $this->fileSystem->put("app/Models/{$this->name}.php", $content); 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /src/Core/Generators/RequestGenerator.php: -------------------------------------------------------------------------------- 1 | stub = $stub; 32 | $this->fileSystem = $stub->getFilesystemInstance(); 33 | } 34 | 35 | /** 36 | * @param string $name 37 | * @return RequestGenerator $this 38 | */ 39 | public function setData(string $name): RequestGenerator 40 | { 41 | $this->name = $name; 42 | return $this; 43 | } 44 | /** 45 | * @inheritDoc 46 | */ 47 | public function generate() 48 | { 49 | $content = $this->stub->parseStub('Request', $this->name); 50 | 51 | if (!$this->fileSystem->exists("app/Http/Requests/")) { 52 | $this->fileSystem->makeDirectory("app/Http/Requests/", 0755, true); 53 | } 54 | 55 | return $this->fileSystem->put("app/Http/Requests/{$this->name}Request.php", $content); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/Core/Generators/ResourceGenerator.php: -------------------------------------------------------------------------------- 1 | stub = $stub; 33 | $this->fileSystem = $this->stub->getFilesystemInstance(); 34 | } 35 | 36 | public function setData(string $name): ResourceGenerator 37 | { 38 | $this->name = $name; 39 | return $this; 40 | } 41 | 42 | /** 43 | * @inheritDoc 44 | */ 45 | public function generate() 46 | { 47 | $content = $this->stub->parseStub('Resource', $this->name); 48 | 49 | if (!$this->fileSystem->exists("app/Http/Resources/")) { 50 | $this->fileSystem->makeDirectory("app/Http/Resources/", 0755, true); 51 | } 52 | return $this->fileSystem->put("app/Http/Resources/{$this->name}Resource.php", $content); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/Core/Generators/RouteGenerator.php: -------------------------------------------------------------------------------- 1 | stub = $stub; 38 | $this->fileSystem = $this->stub->getFilesystemInstance(); 39 | } 40 | 41 | /** 42 | * @param string $modelName 43 | * @param bool $secure 44 | * @return $this 45 | */ 46 | public function setData(string $modelName, bool $secure): RouteGenerator 47 | { 48 | $this->modelName = $modelName; 49 | $this->secure = $secure; 50 | return $this; 51 | } 52 | 53 | /** 54 | * @inheritDoc 55 | */ 56 | public function generate() 57 | { 58 | $fileName = $this->secure ? 'Passport-Routes' : 'Routes'; 59 | $content = $this->stub->parseStub($fileName, $this->modelName); 60 | 61 | if (!$this->fileSystem->exists("routes/")) { 62 | $this->fileSystem->makeDirectory("routes/", 0755, true); 63 | $this->fileSystem->put('routes/api.php', ""); 64 | } 65 | 66 | return $this->fileSystem->append("routes/api.php", $content); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/Core/Generators/TestGenerator.php: -------------------------------------------------------------------------------- 1 | stub = $stub; 32 | $this->fileSystem = $this->stub->getFilesystemInstance(); 33 | } 34 | 35 | /** 36 | * @param string $modelName 37 | * @return $this 38 | */ 39 | public function setData(string $modelName): TestGenerator 40 | { 41 | $this->modelName = $modelName; 42 | return $this; 43 | } 44 | 45 | /** 46 | * @inheritDoc 47 | */ 48 | public function generate() 49 | { 50 | $content = $this->stub->parseStub('Test', $this->modelName); 51 | 52 | if (!$this->fileSystem->exists("tests/Feature/")) { 53 | $this->fileSystem->makeDirectory("tests/Feature/", 0775, true); 54 | } 55 | return $this->fileSystem->append("tests/Feature/{$this->modelName}Test.php", $content); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/Core/Stub.php: -------------------------------------------------------------------------------- 1 | filesystem = $filesystem; 41 | $this->str = $str; 42 | } 43 | 44 | /** 45 | * Get the file from the stub 46 | * 47 | * @param string $name 48 | * @return string 49 | * @throws FileNotFoundException 50 | */ 51 | public function getStub($name) : string 52 | { 53 | if ($this->filesystem->exists("/resources/stubs/$name.stub")) { 54 | return $this->filesystem->get("/resources/stubs/$name.stub"); 55 | } 56 | 57 | return $this->filesystem->get(__DIR__ . "/../stubs/$name.stub"); 58 | } 59 | 60 | 61 | /** 62 | * Fill stub with data 63 | * 64 | * @param $stub string name of stub 65 | * @param $name string name of resource 66 | * @param $args array additional placeholders to replace 67 | * @return string 68 | */ 69 | public function parseStub(string $stub, string $name, array $args = []) : string 70 | { 71 | $toParse = array_merge([ 72 | 'modelName' => $name, 73 | 'modelNamePluralLowerCase' => $args['table'] ?? $this->str->lower($this->str->plural($name)), 74 | 'modelNameSingularLowerCase' => $this->str->lower($name) 75 | ], $args); 76 | 77 | try { 78 | return $this->str->replace( 79 | array_map(function ($key) { 80 | return "{{{$key}}}"; 81 | }, array_keys($toParse)), 82 | array_values($toParse), 83 | $this->getStub($stub) 84 | ); 85 | } catch (FileNotFoundException $e) { 86 | return "Stub not found"; 87 | } 88 | } 89 | 90 | 91 | /** 92 | * @return Filesystem 93 | */ 94 | public function getFilesystemInstance() : Filesystem 95 | { 96 | return $this->filesystem; 97 | } 98 | 99 | /** 100 | * @return Str 101 | */ 102 | public function getStrInstance() : Str 103 | { 104 | return $this->str; 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /src/Interfaces/Generator.php: -------------------------------------------------------------------------------- 1 | validated()); 35 | 36 | return new {{modelName}}Resource(${{modelNameSingularLowerCase}}); 37 | } 38 | 39 | /** 40 | * Display the specified resource. 41 | * 42 | * @param {{modelName}} ${{modelNameSingularLowerCase}} 43 | * @return {{modelName}}Resource 44 | */ 45 | public function show({{modelName}} ${{modelNameSingularLowerCase}}) 46 | { 47 | return new {{modelName}}Resource(${{modelNameSingularLowerCase}}); 48 | } 49 | 50 | /** 51 | * Update the specified resource in storage. 52 | * 53 | * @param {{modelName}}Request $request 54 | * @param {{modelName}} ${{modelNameSingularLowerCase}} 55 | * @return {{modelName}}Resource 56 | */ 57 | public function update({{modelName}}Request $request, {{modelName}} ${{modelNameSingularLowerCase}}) 58 | { 59 | ${{modelNameSingularLowerCase}}->update($request->validated()); 60 | 61 | return new {{modelName}}Resource(${{modelNameSingularLowerCase}}); 62 | } 63 | 64 | /** 65 | * Remove the specified resource from storage. 66 | * 67 | * @param {{modelName}} ${{modelNameSingularLowerCase}} 68 | * @return JsonResponse 69 | */ 70 | public function destroy({{modelName}} ${{modelNameSingularLowerCase}}) 71 | { 72 | ${{modelNameSingularLowerCase}}->delete(); 73 | 74 | return response()->json("DELETED", 204); 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /src/stubs/Factory.stub: -------------------------------------------------------------------------------- 1 | resource('{{modelNameSingularLowerCase}}', \App\Http\Controllers\{{modelName}}Controller::class, ['only' => [ 2 | 'index', 'store', 'show', 'update', 'destroy' 3 | ]]); 4 | -------------------------------------------------------------------------------- /src/stubs/Request.stub: -------------------------------------------------------------------------------- 1 | [ 2 | 'index', 'store', 'show', 'update', 'destroy' 3 | ]]); 4 | -------------------------------------------------------------------------------- /src/stubs/Test.stub: -------------------------------------------------------------------------------- 1 | {{modelNameSingularLowerCase}} = {{modelName}}::factory()->make(); 24 | } 25 | 26 | /** 27 | * List all {{modelNamePluralLowerCase}} 28 | * 29 | * @return void 30 | */ 31 | public function testIndex() : void 32 | { 33 | $this->getJson('/api/{{modelNameSingularLowerCase}}') 34 | ->assertSuccessful(); 35 | } 36 | 37 | /** 38 | * Show {{modelNameSingularLowerCase}} 39 | * 40 | * @return void 41 | */ 42 | public function testShow() : void 43 | { 44 | $this->getJson('/api/{{modelNameSingularLowerCase}}/1') 45 | ->assertSuccessful(); 46 | 47 | $this->getJson('/api/{{modelNameSingularLowerCase}}/0') 48 | ->assertNotFound(); 49 | } 50 | 51 | /** 52 | * Create new {{modelNameSingularLowerCase}} 53 | * 54 | * @return void 55 | */ 56 | public function testStore() : void 57 | { 58 | $this->postJson('/api/{{modelNameSingularLowerCase}}', $this->{{modelNameSingularLowerCase}}->toArray()) 59 | ->assertSuccessful(); 60 | } 61 | 62 | /** 63 | * Edit {{modelNameSingularLowerCase}} 64 | * 65 | * @return void 66 | */ 67 | public function testUpdate() : void 68 | { 69 | $this->patchJson('/api/{{modelNameSingularLowerCase}}/1', $this->{{modelNameSingularLowerCase}}->toArray()) 70 | ->assertSuccessful(); 71 | } 72 | 73 | /** 74 | * Delete {{modelNameSingularLowerCase}} 75 | * 76 | * @return void 77 | */ 78 | public function testDestroy() : void 79 | { 80 | $id = {{modelName}}::all()->last()->id; 81 | 82 | $this->deleteJson('/api/{{modelNameSingularLowerCase}}/'.$id) 83 | ->assertSuccessful(); 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /tests/GeneratorTest.php: -------------------------------------------------------------------------------- 1 | files = new Illuminate\Filesystem\Filesystem(); 20 | } 21 | 22 | 23 | public function setUp(): void 24 | { 25 | parent::setUp(); 26 | $this->name = "Car"; 27 | $this->table = "cars"; 28 | $this->generator = new Generator( 29 | new Illuminate\Filesystem\Filesystem(), 30 | new \Illuminate\Support\Str(), 31 | new \AndreaCivita\ApiCrudGenerator\Core\Stub( 32 | new Illuminate\Filesystem\Filesystem(), 33 | new \Illuminate\Support\Str() 34 | ) 35 | ); 36 | } 37 | 38 | public function testModel() : void 39 | { 40 | $this->files->makeDirectory('app/Models', 0777, true); 41 | $this->assertIsInt($this->generator->model($this->name, $this->table, true)); 42 | $this->assertIsInt($this->generator->model($this->name, $this->table, false)); 43 | } 44 | 45 | public function testController() : void 46 | { 47 | $this->files->makeDirectory('app/Http/Controllers', 0777, true); 48 | $this->assertIsInt($this->generator->controller($this->name, $this->table)); 49 | } 50 | 51 | public function testRequest() : void 52 | { 53 | $this->files->makeDirectory('app/Http/Requests', 0777, true); 54 | $this->assertIsInt($this->generator->request($this->name)); 55 | } 56 | 57 | public function testResource() : void 58 | { 59 | $this->files->makeDirectory('app/Http/Resources', 0777, true); 60 | $this->assertIsInt($this->generator->resource($this->name)); 61 | } 62 | 63 | public function testFactory() : void 64 | { 65 | $this->files->makeDirectory('database/factories', 0777, true); 66 | $this->assertIsInt($this->generator->factory($this->name)); 67 | } 68 | 69 | public function testRoutes() : void 70 | { 71 | $this->files->makeDirectory('routes', 0777, true); 72 | $this->files->put('routes/api.php', ""); 73 | $this->assertIsInt($this->generator->routes($this->name)); 74 | } 75 | 76 | public function testSecureRoutes() : void 77 | { 78 | $this->files->makeDirectory('routes', 0777, true); 79 | $this->files->put('routes/api.php', ""); 80 | $this->assertIsInt($this->generator->secureRoutes($this->name)); 81 | } 82 | 83 | public function testMakeTests(): void 84 | { 85 | $this->files->makeDirectory('tests/Feature', 0777, true); 86 | $this->assertIsInt($this->generator->test($this->name)); 87 | } 88 | 89 | 90 | public function tearDown(): void 91 | { 92 | $files = new Illuminate\Filesystem\Filesystem(); 93 | $files->cleanDirectory('app'); 94 | $files->cleanDirectory('database'); 95 | $files->cleanDirectory('routes'); 96 | $files->cleanDirectory('tests/Feature'); 97 | $files->deleteDirectory('app'); 98 | $files->deleteDirectory('database'); 99 | $files->deleteDirectory('routes'); 100 | $files->deleteDirectory('tests/Feature'); 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /tests/Test.php: -------------------------------------------------------------------------------- 1 | assertTrue(!false); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /tests/Unit/ControllerGeneratorTest.php: -------------------------------------------------------------------------------- 1 | modelName = 'Car'; 32 | $this->tableName = 'cars'; 33 | $this->controllerGenerator = new ControllerGenerator(new Stub(new Filesystem(), new Str())); 34 | } 35 | 36 | public function testSetData() 37 | { 38 | $this->assertInstanceOf( 39 | ControllerGenerator::class, 40 | $this->controllerGenerator->setData($this->modelName, $this->tableName) 41 | ); 42 | } 43 | 44 | 45 | public function testGenerate() 46 | { 47 | $this->controllerGenerator->setData($this->modelName, $this->tableName); 48 | $this->assertIsInt($this->controllerGenerator->generate()); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /tests/Unit/FactoryGeneratorTest.php: -------------------------------------------------------------------------------- 1 | modelName = 'Car'; 26 | $this->factoryGenerator = new FactoryGenerator(new Stub(new Filesystem(), new Str())); 27 | } 28 | 29 | public function testSetData() 30 | { 31 | $this->assertInstanceOf( 32 | FactoryGenerator::class, 33 | $this->factoryGenerator->setData($this->modelName) 34 | ); 35 | } 36 | 37 | 38 | public function testGenerate() 39 | { 40 | $this->factoryGenerator->setData($this->modelName); 41 | $this->assertIsInt($this->factoryGenerator->generate()); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /tests/Unit/ModelGeneratorTest.php: -------------------------------------------------------------------------------- 1 | modelName = 'Car'; 33 | $this->tableName = 'cars'; 34 | $this->modelGenerator = new ModelGenerator(new Stub(new Filesystem(), new Str())); 35 | } 36 | 37 | public function testSetData() 38 | { 39 | $this->assertInstanceOf( 40 | ModelGenerator::class, 41 | $this->modelGenerator->setData($this->modelName, $this->tableName, false) 42 | ); 43 | } 44 | 45 | 46 | public function testGenerate() 47 | { 48 | $this->modelGenerator->setData($this->modelName, $this->tableName, true); 49 | $this->assertIsInt($this->modelGenerator->generate()); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /tests/Unit/RequestGeneratorTest.php: -------------------------------------------------------------------------------- 1 | modelName = 'Car'; 26 | $this->requestGenerator = new RequestGenerator(new Stub(new Filesystem(), new Str())); 27 | } 28 | 29 | public function testSetData() 30 | { 31 | $this->assertInstanceOf( 32 | RequestGenerator::class, 33 | $this->requestGenerator->setData($this->modelName) 34 | ); 35 | } 36 | 37 | 38 | public function testGenerate() 39 | { 40 | $this->requestGenerator->setData($this->modelName); 41 | $this->assertIsInt($this->requestGenerator->generate()); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /tests/Unit/ResourceGeneratorTest.php: -------------------------------------------------------------------------------- 1 | modelName = 'Car'; 26 | $this->resourceGenerator = new ResourceGenerator(new Stub(new Filesystem(), new Str())); 27 | } 28 | 29 | public function testSetData() 30 | { 31 | $this->assertInstanceOf( 32 | ResourceGenerator::class, 33 | $this->resourceGenerator->setData($this->modelName) 34 | ); 35 | } 36 | 37 | 38 | public function testGenerate() 39 | { 40 | $this->resourceGenerator->setData($this->modelName); 41 | $this->assertIsInt($this->resourceGenerator->generate()); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /tests/Unit/RouteGeneratorTest.php: -------------------------------------------------------------------------------- 1 | modelName = 'Car'; 26 | $this->routeGenerator = new RouteGenerator(new Stub(new Filesystem(), new Str())); 27 | } 28 | 29 | public function testSetData() 30 | { 31 | $this->assertInstanceOf( 32 | RouteGenerator::class, 33 | $this->routeGenerator->setData($this->modelName, false) 34 | ); 35 | } 36 | 37 | 38 | public function testGenerate() 39 | { 40 | $this->routeGenerator->setData($this->modelName, false); 41 | $this->assertIsInt($this->routeGenerator->generate()); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /tests/Unit/StubTest.php: -------------------------------------------------------------------------------- 1 | stub = new Stub(new Filesystem(), new Str()); 23 | $this->names = [ 24 | 'Controller', 25 | 'Factory', 26 | 'Model', 27 | 'Passport-Routes', 28 | 'Request', 29 | 'Resource', 30 | 'Routes', 31 | 'Test', 32 | ]; 33 | } 34 | 35 | public function testGetStub() 36 | { 37 | foreach ($this->names as $name) { 38 | echo "Fetching $name stub... \n"; 39 | $this->assertIsString($this->stub->getStub($name)); 40 | echo $name . " ✅ \n"; 41 | } 42 | 43 | $this->expectException(FileNotFoundException::class); 44 | $this->stub->getStub('StubThatDoesNotExist'); 45 | } 46 | 47 | /** 48 | * Test StubClass 49 | */ 50 | public function testParseStub(): void 51 | { 52 | foreach ($this->names as $name) { 53 | $this->assertIsString($this->stub->parseStub($name, 'Car', ['table' => 'cars'])); 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /tests/Unit/TestGeneratorTest.php: -------------------------------------------------------------------------------- 1 | modelName = 'Car'; 26 | $this->testGenerator = new TestGenerator(new Stub(new Filesystem(), new Str())); 27 | } 28 | 29 | public function testSetData() 30 | { 31 | $this->assertInstanceOf( 32 | TestGenerator::class, 33 | $this->testGenerator->setData($this->modelName) 34 | ); 35 | } 36 | 37 | 38 | public function testGenerate() 39 | { 40 | $this->testGenerator->setData($this->modelName); 41 | $this->assertIsInt($this->testGenerator->generate()); 42 | } 43 | } 44 | --------------------------------------------------------------------------------