├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── composer.json ├── config └── crudgenerator.php ├── doc ├── README.md ├── configuration.md ├── fields.md ├── installation.md ├── options.md ├── templates.md └── usage.md ├── phpunit.xml.dist ├── publish └── views │ └── admin │ ├── dashboard.blade.php │ └── sidebar.blade.php ├── src ├── Commands │ ├── CrudApiCommand.php │ ├── CrudApiControllerCommand.php │ ├── CrudCommand.php │ ├── CrudControllerCommand.php │ ├── CrudLangCommand.php │ ├── CrudMigrationCommand.php │ ├── CrudModelCommand.php │ └── CrudViewCommand.php ├── CrudGeneratorServiceProvider.php └── stubs │ ├── api-controller.stub │ ├── controller.stub │ ├── lang.stub │ ├── migration.stub │ ├── model.stub │ └── views │ └── blade │ ├── create.blade.stub │ ├── edit.blade.stub │ ├── form-fields │ ├── checkbox-field.blade.stub │ ├── form-field.blade.stub │ ├── input-field.blade.stub │ ├── password-field.blade.stub │ ├── radio-field.blade.stub │ ├── select-field.blade.stub │ ├── textarea-field.blade.stub │ └── wrap-field.blade.stub │ ├── form.blade.stub │ ├── index.blade.stub │ └── show.blade.stub └── tests ├── CrudGeneratorTest.php ├── Kernel.php ├── TestCase.php └── temp └── .gitignore /.gitignore: -------------------------------------------------------------------------------- 1 | vendor 2 | composer.lock 3 | .DS_Store -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: php 2 | 3 | php: 4 | - 8.0 5 | - 8.2 6 | 7 | sudo: false 8 | 9 | install: travis_retry composer install --no-interaction --prefer-dist --no-suggest 10 | 11 | script: vendor/bin/phpunit --verbose 12 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2015-2017 Sohel Amin 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 | # Laravel CRUD Generator 2 | 3 | [![Build Status](https://travis-ci.org/appzcoder/crud-generator.svg)](https://travis-ci.org/appzcoder/crud-generator.svg) 4 | [![Total Downloads](https://poser.pugx.org/appzcoder/crud-generator/d/total.svg)](https://packagist.org/packages/appzcoder/crud-generator) 5 | [![Latest Stable Version](https://poser.pugx.org/appzcoder/crud-generator/v/stable.svg)](https://packagist.org/packages/appzcoder/crud-generator) 6 | [![License](https://poser.pugx.org/appzcoder/crud-generator/license.svg)](https://packagist.org/packages/appzcoder/crud-generator) 7 | 8 | This Generator package provides various generators like CRUD, API, Controller, Model, Migration, View for your painless development of your applications. 9 | 10 | ## Requirements 11 | Laravel >= 8.0 12 | PHP >= 8.0.0 13 | 14 | ## Installation 15 | ``` 16 | composer require appzcoder/crud-generator --dev 17 | ``` 18 | 19 | ## Documentation 20 | Go through to the [detailed documentation](doc#readme) 21 | 22 | ## Screencast 23 | 24 | [![Screencast](http://img.youtube.com/vi/831-PFBsYfw/0.jpg)](https://www.youtube.com/watch?v=K2G3kMQtY5Y) 25 | 26 | #### If you're still looking for easier one then try this [Admin Panel](https://github.com/appzcoder/laravel-admin) 27 | 28 | ## Author 29 | 30 | [Sohel Amin](http://sohelamin.com) :email: [Email Me](mailto:sohelamincse@gmail.com) 31 | 32 | ## License 33 | 34 | This project is licensed under the MIT License - see the [License File](LICENSE) for details 35 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "appzcoder/crud-generator", 3 | "license": "MIT", 4 | "description": "Laravel CRUD Generator", 5 | "keywords": [ 6 | "laravel", 7 | "crud", 8 | "crud generator", 9 | "laravel crud generator", 10 | "api generator" 11 | ], 12 | "authors": [ 13 | { 14 | "name": "Sohel Amin", 15 | "email": "sohelamincse@gmail.com", 16 | "homepage": "http://www.appzcoder.com" 17 | } 18 | ], 19 | "require": { 20 | "php": "^8.0", 21 | "illuminate/support": "^8.0|^9.0|^10.0|^11.0|^12.0", 22 | "laravel/breeze": "^1.10|^2.0" 23 | }, 24 | "require-dev": { 25 | "mockery/mockery": "^1.0", 26 | "phpunit/phpunit": "^5.7|^8.0|^9.0", 27 | "orchestra/testbench": "^3.3|^4.0|^5.0" 28 | }, 29 | "autoload": { 30 | "psr-4": { 31 | "Appzcoder\\CrudGenerator\\": "src/" 32 | }, 33 | "classmap": [ 34 | "tests" 35 | ] 36 | }, 37 | "extra": { 38 | "laravel": { 39 | "providers": [ 40 | "Appzcoder\\CrudGenerator\\CrudGeneratorServiceProvider" 41 | ] 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /config/crudgenerator.php: -------------------------------------------------------------------------------- 1 | false, 6 | 7 | /* 8 | |-------------------------------------------------------------------------- 9 | | Crud Generator Template Stubs Storage Path 10 | |-------------------------------------------------------------------------- 11 | | 12 | | Here you can specify your custom template path for the generator. 13 | | 14 | */ 15 | 16 | 'path' => base_path('resources/crud-generator/'), 17 | 18 | /** 19 | * Columns number to show in view's table. 20 | */ 21 | 'view_columns_number' => 3, 22 | 23 | /** 24 | * Delimiter for template vars 25 | */ 26 | 'custom_delimiter' => ['%%', '%%'], 27 | 28 | /* 29 | |-------------------------------------------------------------------------- 30 | | Dynamic templating 31 | |-------------------------------------------------------------------------- 32 | | 33 | | Here you can specify your customs templates for the generator. 34 | | You can set new templates or delete some templates if you do not want them. 35 | | You can also choose which values are passed to the views and you can specify a custom delimiter for all templates 36 | | 37 | | Those values are available : 38 | | 39 | | formFields 40 | | formFieldsHtml 41 | | varName 42 | | crudName 43 | | crudNameCap 44 | | crudNameSingular 45 | | primaryKey 46 | | modelName 47 | | modelNameCap 48 | | viewName 49 | | routePrefix 50 | | routePrefixCap 51 | | routeGroup 52 | | formHeadingHtml 53 | | formBodyHtml 54 | | 55 | | 56 | */ 57 | 'dynamic_view_template' => [ 58 | 'index' => ['formHeadingHtml', 'formBodyHtml', 'crudName', 'crudNameCap', 'modelName', 'viewName', 'routeGroup', 'primaryKey'], 59 | 'form' => ['formFieldsHtml'], 60 | 'create' => ['crudName', 'crudNameCap', 'modelName', 'modelNameCap', 'viewName', 'routeGroup', 'viewTemplateDir'], 61 | 'edit' => ['crudName', 'crudNameSingular', 'crudNameCap', 'modelNameCap', 'modelName', 'viewName', 'routeGroup', 'primaryKey', 'viewTemplateDir'], 62 | 'show' => ['formHeadingHtml', 'formBodyHtml', 'formBodyHtmlForShowView', 'crudName', 'crudNameSingular', 'crudNameCap', 'modelName', 'viewName', 'routeGroup', 'primaryKey'], 63 | /* 64 | * Add new stubs templates here if you need to, like action, datatable... 65 | * custom_template needs to be activated for this to work 66 | */ 67 | ] 68 | 69 | 70 | ]; 71 | -------------------------------------------------------------------------------- /doc/README.md: -------------------------------------------------------------------------------- 1 | # Documentation 2 | 3 | This documentation will guide you to generate your stuffs for your application. Let's begin! 4 | 5 | ## Getting Started 6 | 7 | 1. [Installation](installation.md) 8 | 2. [Configuration](configuration.md) 9 | 3. [Usage](usage.md) 10 | 4. [Fields](fields.md) 11 | 5. [Options](options.md) 12 | 6. [Templates](templates.md) 13 | -------------------------------------------------------------------------------- /doc/configuration.md: -------------------------------------------------------------------------------- 1 | ## Configuration 2 | 3 | You will find a configuration file located at `config/crudgenerator.php` 4 | 5 | ### Custom Template 6 | 7 | When you want to use your own custom template files, then you should turn it on and it will use the files from `resources/crud-generator/` 8 | 9 | ```php 10 | 'custom_template' => false, 11 | ``` 12 | 13 | ### Path 14 | 15 | You can change your template path easily, the default path is `resources/crud-generator/`. 16 | 17 | ```php 18 | 'path' => base_path('resources/crud-generator/'), 19 | ``` 20 | 21 | ### View Columns 22 | 23 | When generating CRUD or the views, the generator will assume the column number to show for the CRUD grid or detail automatically from the config. You can change it. 24 | 25 | ```php 26 | 'view_columns_number' => 3, 27 | ``` 28 | 29 | ### Custom Delimiter 30 | 31 | Set your delimiter which you use for your template vars. The default delimiter is `%%` in everywhere. 32 | 33 | ```php 34 | 'custom_delimiter' => ['%%', '%%'], 35 | ``` 36 | Note: You should use the delimiter same as yours template files. 37 | 38 | ### View Template Vars 39 | 40 | This configuration will help you to use any custom template vars in the views `index`, `form`, `create`, `edit`, `show` 41 | 42 | ```php 43 | 'dynamic_view_template' => [], 44 | ``` 45 | 46 | [← Back to index](README.md) 47 | -------------------------------------------------------------------------------- /doc/fields.md: -------------------------------------------------------------------------------- 1 | ## Supported Fields 2 | 3 | You can use any of the fields from the list. 4 | 5 | ### Form Field Types: 6 | 7 | * text 8 | * textarea 9 | * password 10 | * email 11 | * number 12 | * date 13 | * datetime 14 | * time 15 | * radio 16 | * select 17 | * file 18 | 19 | ### Migration Field Types: 20 | 21 | * string 22 | * char 23 | * varchar 24 | * date 25 | * datetime 26 | * time 27 | * timestamp 28 | * text 29 | * mediumtext 30 | * longtext 31 | * json 32 | * jsonb 33 | * binary 34 | * integer 35 | * bigint 36 | * mediumint 37 | * tinyint 38 | * smallint 39 | * boolean 40 | * decimal 41 | * double 42 | * float 43 | * enum 44 | 45 | [← Back to index](README.md) 46 | -------------------------------------------------------------------------------- /doc/installation.md: -------------------------------------------------------------------------------- 1 | ## Installation 2 | 3 | To get started, you should add the `appzcoder/crud-generator` Composer dependency to your project: 4 | ``` 5 | composer require appzcoder/crud-generator --dev 6 | ``` 7 | Once the package is installed, you should publish its assets using the `vendor:publish` Artisan command: 8 | ``` 9 | php artisan vendor:publish --provider="Appzcoder\CrudGenerator\CrudGeneratorServiceProvider" 10 | ``` 11 | Before you begin, it's important to install the Laravel Breeze package with the Blade template, as there is a dependency on it. 12 | ``` 13 | php artisan breeze:install 14 | 15 | php artisan migrate 16 | npm install 17 | npm run dev 18 | ``` 19 | 20 | [← Back to index](README.md) 21 | -------------------------------------------------------------------------------- /doc/options.md: -------------------------------------------------------------------------------- 1 | ## Options 2 | 3 | ### CRUD Options: 4 | 5 | | Option | Description | 6 | | --- | --- | 7 | | `--fields` | The field names for the form. e.g. ```--fields='title#string; content#text; category#select#options={"technology": "Technology", "tips": "Tips", "health": "Health"}; user_id#integer#unsigned'``` | 8 | | `--fields_from_file` | Fields from a JSON file. e.g. `--fields_from_file="/path/to/fields.json"` | 9 | | `--validations` | Validation rules for the fields "col_name#rules_set" e.g. ` "title#min:10|max:30|required" ` - See https://laravel.com/docs/master/validation#available-validation-rules | 10 | | `--controller-namespace` | The namespace of the controller - sub directories will be created | 11 | | `--model-namespace` | The namespace that the model will be placed in - directories will be created | 12 | | `--pk` | The name of the primary key | 13 | | `--pagination` | The amount of models per page for index pages | 14 | | `--indexes` | The fields to add an index to. append "#unique" to a field name to add a unique index. Create composite fields by separating fieldnames with a pipe (` --indexes="title,field1|field2#unique" ` will create normal index on title, and unique composite on fld1 and fld2) | 15 | | `--foreign-keys` | Any foreign keys for the table. e.g. `--foreign-keys="user_id#id#users#cascade"` where user_id is the column name, id is the name of the field on the foreign table, users is the name of the foreign table, and cascade is the operation 'ON DELETE' together with 'ON UPDATE' | 16 | | `--relationships` | The relationships for the model. e.g. `--relationships="comments#hasMany#App\Comment"` in the format | 17 | | `--route` | Include Crud route to routes.php? yes or no | 18 | | `--route-group` | Prefix of the route group | 19 | | `--view-path` | The name of the view path | 20 | | `--form-helper` | Helper for the form. eg. `--form-helper=blade`, `--form-helper=custom-template` | 21 | | `--localize` | Allow to localize. e.g. localize=yes | 22 | | `--locales` | Locales language type. e.g. locals=en | 23 | | `--soft-deletes` | Include soft deletes fields. eg. `--soft-deletes=yes` | 24 | 25 | 26 | ### Controller Options: 27 | 28 | | Option | Description | 29 | | --- | --- | 30 | | `--crud-name` | The name of the crud. e.g. ```--crud-name="post"``` | 31 | | `--model-name` | The name of the model. e.g. ```--model-name="Post"``` | 32 | | `--model-namespace` | The namespace of the model. e.g. ```--model-namespace="Custom\Namespace\Post"``` | 33 | | `--controller-namespace` | The namespace of the controller. e.g. ```--controller-namespace="Http\Controllers\Client"``` | 34 | | `--view-path` | The name of the view path | 35 | | `--fields` | The field names for the form. e.g. ```--fields='title#string; content#text; category#select#options={"technology": "Technology", "tips": "Tips", "health": "Health"}; user_id#integer#unsigned'``` | 36 | | `--validations` | Validation rules for the fields "col_name#rules_set" e.g. ``` "title#min:10|max:30|required" ``` - See https://laravel.com/docs/master/validation#available-validation-rules | 37 | | `--route-group` | Prefix of the route group | 38 | | `--pagination` | The amount of models per page for index pages | 39 | | `--force` | Overwrite already existing controller. | 40 | 41 | ### View Options: 42 | 43 | | Option | Description | 44 | | --- | --- | 45 | | `--fields` | The field names for the form. e.g. ```--fields='title#string; content#text; category#select#options={"technology": "Technology", "tips": "Tips", "health": "Health"}; user_id#integer#unsigned'``` | 46 | | `--view-path` | The name of the view path | 47 | | `--route-group` | Prefix of the route group | 48 | | `--pk` | The name of the primary key | 49 | | `--validations` | Validation rules for the form "col_name#rules_set" e.g. ``` "title#min:10|max:30|required" ``` - See https://laravel.com/docs/master/validation#available-validation-rules | 50 | | `--form-helper` | Helper for the form. eg. `--form-helper=blade`, `--form-helper=custom-template` | 51 | | `--custom-data` | Some additional values to use in the crud. | 52 | | `--localize` | Allow to localize. e.g. localize=yes | 53 | 54 | ### Model Options: 55 | 56 | | Option | Description | 57 | | --- | --- | 58 | | `--table` | The name of the table | 59 | | `--fillable` | The name of the view path | 60 | | `--relationships` | The relationships for the model. e.g. `--relationships="comments#hasMany#App\Comment"` in the format | 61 | | `--pk` | The name of the primary key | 62 | | `--soft-deletes` | Include soft deletes fields. eg. `--soft-deletes=yes` | 63 | 64 | ### Migration Options: 65 | 66 | | Option | Description | 67 | | --- | --- | 68 | | `--schema` | The name of the schema | 69 | | `--indexes` | The fields to add an index to. append "#unique" to a field name to add a unique index. Create composite fields by separating fieldnames with a pipe (` --indexes="title,field1|field2#unique" ` will create normal index on title, and unique composite on fld1 and fld2) | 70 | | `--foreign-keys` | Any foreign keys for the table. e.g. `--foreign-keys="user_id#id#users#cascade"` where user_id is the column name, id is the name of the field on the foreign table, users is the name of the foreign table, and cascade is the operation 'ON DELETE' together with 'ON UPDATE' | 71 | | `--pk` | The name of the primary key | 72 | | `--soft-deletes` | Include soft deletes fields. eg. `--soft-deletes=yes` | 73 | 74 | ### Lang Options: 75 | 76 | | Option | Description | 77 | | --- | --- | 78 | | `--fields` | The field names for the form. e.g. ```--fields='title#string; content#text``` | 79 | | `--locales` | Locales language type. e.g. locals=en | 80 | 81 | ### API CRUD Options: 82 | 83 | | Option | Description | 84 | | --- | --- | 85 | | `--fields` | The field names for the form. e.g. ```--fields='title#string; content#text; category#select#options={"technology": "Technology", "tips": "Tips", "health": "Health"}; user_id#integer#unsigned'``` | 86 | | `--fields_from_file` | Fields from a JSON file. e.g. `--fields_from_file="/path/to/fields.json"` | 87 | | `--validations` | Validation rules for the fields "col_name#rules_set" e.g. ` "title#min:10|max:30|required" ` - See https://laravel.com/docs/master/validation#available-validation-rules | 88 | | `--controller-namespace` | The namespace of the controller - sub directories will be created | 89 | | `--model-namespace` | The namespace that the model will be placed in - directories will be created | 90 | | `--pk` | The name of the primary key | 91 | | `--pagination` | The amount of models per page for index pages | 92 | | `--indexes` | The fields to add an index to. append "#unique" to a field name to add a unique index. Create composite fields by separating fieldnames with a pipe (` --indexes="title,field1|field2#unique" ` will create normal index on title, and unique composite on fld1 and fld2) | 93 | | `--foreign-keys` | Any foreign keys for the table. e.g. `--foreign-keys="user_id#id#users#cascade"` where user_id is the column name, id is the name of the field on the foreign table, users is the name of the foreign table, and cascade is the operation 'ON DELETE' together with 'ON UPDATE' | 94 | | `--relationships` | The relationships for the model. e.g. `--relationships="comments#hasMany#App\Comment"` in the format | 95 | | `--route` | Include Crud route to routes.php? yes or no | 96 | | `--route-group` | Prefix of the route group | 97 | | `--soft-deletes` | Include soft deletes fields. eg. `--soft-deletes=yes` | 98 | 99 | ### API Controller Options: 100 | 101 | | Option | Description | 102 | | --- | --- | 103 | | `--crud-name` | The name of the crud. e.g. ```--crud-name="post"``` | 104 | | `--model-name` | The name of the model. e.g. ```--model-name="Post"``` | 105 | | `--model-namespace` | The namespace of the model. e.g. ```--model-namespace="Custom\Namespace\Post"``` | 106 | | `--controller-namespace` | The namespace of the controller. e.g. ```--controller-namespace="Http\Controllers\Client"``` | 107 | | `--validations` | Validation rules for the fields "col_name#rules_set" e.g. ``` "title#min:10|max:30|required" ``` - See https://laravel.com/docs/master/validation#available-validation-rules | 108 | | `--pagination` | The amount of models per page for index pages | 109 | | `--force` | Overwrite already existing controller. | 110 | 111 | [← Back to index](README.md) 112 | -------------------------------------------------------------------------------- /doc/templates.md: -------------------------------------------------------------------------------- 1 | ## Custom Templates 2 | 3 | The package allows user to extensively customize or use own templates. 4 | 5 | ### All Templates 6 | 7 | To customize or change the template, you need to follow these steps: 8 | 9 | 1. Just make sure you've published all assets of this package. If you didn't just run this command. 10 | ```php 11 | php artisan vendor:publish --provider="Appzcoder\CrudGenerator\CrudGeneratorServiceProvider" 12 | ``` 13 | 2. To override the default template with yours, turn on `custom_template` option in the `config/crudgenerator.php` file. 14 | ```php 15 | 'custom_template' => true, 16 | ``` 17 | 18 | 3. Now you can customize everything from this `resources/crud-generator/` directory. 19 | 20 | 4. Even if you need to use any custom variable just add those in the `config/crudgenerator.php` file. 21 | 22 | ### Form Helper 23 | 24 | You can use any form template for your forms. In order to do that, you just need to mention the helper package name while generating the main CRUD or views with this option `--form-helper`. This generator use `blade` as default helper. 25 | 26 | To use the any other form helper, you need to follow these steps: 27 | 28 | 1. Make sure you've installed & configured the desire helper package. 29 | 30 | 2. For use custom helper template, you should turn on `custom_template` option in the `config/crudgenerator.php` file. 31 | 32 | 3. Now put your files into `resources/crud-generator/views/` directory. Suppose your helper is `custom-template` then you should create a directory as `resources/crud-generator/views/custom-template`. You can also copy the template files from other helper directory, then modify as yours. 33 | 34 | 4. You're ready to generate the CRUD with your helper. 35 | ``` 36 | php artisan crud:generate Posts --fields='title#string; content#text; category#select#options={"technology": "Technology", "tips": "Tips", "health": "Health"}' --view-path=admin --controller-namespace=Admin --route-group=admin --form-helper=custom-template 37 | ``` 38 | 39 | [← Back to index](README.md) 40 | -------------------------------------------------------------------------------- /doc/usage.md: -------------------------------------------------------------------------------- 1 | ## Usage 2 | 3 | ### CRUD Command 4 | 5 | ``` 6 | php artisan crud:generate Posts --fields='title#string; content#text; category#select#options={"technology": "Technology", "tips": "Tips", "health": "Health"}' --view-path=admin --controller-namespace=App\\Http\\Controllers\\Admin --route-group=admin --form-helper=blade 7 | ``` 8 | 9 | #### CRUD fields from a JSON file: 10 | 11 | ```json 12 | { 13 | "fields": [ 14 | { 15 | "name": "title", 16 | "type": "string" 17 | }, 18 | { 19 | "name": "content", 20 | "type": "text" 21 | }, 22 | { 23 | "name": "category", 24 | "type": "select", 25 | "options": { 26 | "technology": "Technology", 27 | "tips": "Tips", 28 | "health": "Health" 29 | } 30 | }, 31 | { 32 | "name": "user_id", 33 | "type": "integer#unsigned" 34 | } 35 | ], 36 | "foreign_keys": [ 37 | { 38 | "column": "user_id", 39 | "references": "id", 40 | "on": "users", 41 | "onDelete": "cascade" 42 | } 43 | ], 44 | "relationships": [ 45 | { 46 | "name": "user", 47 | "type": "belongsTo", 48 | "class": "App\\Models\\User" 49 | } 50 | ], 51 | "validations": [ 52 | { 53 | "field": "title", 54 | "rules": "required|max:10" 55 | } 56 | ] 57 | } 58 | ``` 59 | 60 | ``` 61 | php artisan crud:generate Posts --fields_from_file="/path/to/fields.json" --view-path=admin --controller-namespace=App\\Http\\Controllers\\Admin --route-group=admin --form-helper=blade 62 | ``` 63 | 64 | ### Other Commands 65 | 66 | For controller: 67 | 68 | ``` 69 | php artisan crud:controller PostsController --crud-name=posts --model-name=Post --view-path="directory" --route-group=admin 70 | ``` 71 | 72 | For model: 73 | 74 | ``` 75 | php artisan crud:model Post --fillable="['title', 'body']" 76 | ``` 77 | 78 | For migration: 79 | 80 | ``` 81 | php artisan crud:migration posts --schema="title#string; body#text" 82 | ``` 83 | 84 | For view: 85 | 86 | ``` 87 | php artisan crud:view posts --fields="title#string; body#text" --view-path="directory" --route-group=admin 88 | --form-helper=blade 89 | ``` 90 | 91 | By default, the generator will attempt to append the crud route to your ```Route``` file. If you don't want the route added, you can use this option ```--route=no```. 92 | 93 | After creating all resources, run migrate command. *If necessary, include the route for your crud as well.* 94 | 95 | ``` 96 | php artisan migrate 97 | ``` 98 | 99 | If you chose not to add the crud route in automatically (see above), you will need to include the route manually. 100 | ```php 101 | Route::resource('posts', 'PostsController'); 102 | ``` 103 | 104 | ### API Commands 105 | 106 | For api crud: 107 | 108 | ``` 109 | php artisan crud:api Posts --fields='title#string; content#text' --controller-namespace=Api 110 | ``` 111 | 112 | For api controller: 113 | 114 | ``` 115 | php artisan crud:api-controller Api\\PostsController --crud-name=posts --model-name=Post 116 | ``` 117 | 118 | ### File Upload 119 | If you want to add file on a CRUD just mention the field type as `file` eg. ```--fields='avatar#file;``` 120 | 121 | All the files will upload to `storage\app\public\uploads` directory. So you should symbolic the storage dir to public access. 122 | ``` 123 | php artisan storage:link 124 | ``` 125 | Get your uploaded file as: 126 | ```php 127 | $file = Storage::disk('public')->get('uploads\filename.jpg'); 128 | ``` 129 | 130 | [← Back to index](README.md) 131 | -------------------------------------------------------------------------------- /phpunit.xml.dist: -------------------------------------------------------------------------------- 1 | 2 | 16 | 17 | 18 | ./tests 19 | 20 | 21 | 22 | 23 | ./src 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /publish/views/admin/dashboard.blade.php: -------------------------------------------------------------------------------- 1 | @extends('layouts.app') 2 | 3 | @section('content') 4 |
5 |
6 | @include('admin.sidebar') 7 | 8 |
9 |
10 |
Dashboard
11 | 12 |
13 | Your application's dashboard. 14 |
15 |
16 |
17 |
18 |
19 | @endsection 20 | -------------------------------------------------------------------------------- /publish/views/admin/sidebar.blade.php: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 | Sidebar 5 |
6 | 7 |
8 | 15 |
16 |
17 |
18 | -------------------------------------------------------------------------------- /src/Commands/CrudApiCommand.php: -------------------------------------------------------------------------------- 1 | argument('name'); 63 | $modelName = Str::singular($name); 64 | $migrationName = Str::plural(Str::snake($name)); 65 | $tableName = $migrationName; 66 | 67 | $routeGroup = $this->option('route-group'); 68 | $this->routeName = ($routeGroup) ? $routeGroup . '/' . Str::snake($name, '-') : Str::snake($name, '-'); 69 | $perPage = intval($this->option('pagination')); 70 | 71 | $controllerNamespace = ($this->option('controller-namespace')) ? $this->option('controller-namespace') . '\\' : ''; 72 | $modelNamespace = ($this->option('model-namespace')) ? trim($this->option('model-namespace')) . '\\' : 'Models\\'; 73 | 74 | $fields = rtrim($this->option('fields'), ';'); 75 | 76 | if ($this->option('fields_from_file')) { 77 | $fields = $this->processJSONFields($this->option('fields_from_file')); 78 | } 79 | 80 | $primaryKey = $this->option('pk'); 81 | 82 | $foreignKeys = $this->option('foreign-keys'); 83 | 84 | if ($this->option('fields_from_file')) { 85 | $foreignKeys = $this->processJSONForeignKeys($this->option('fields_from_file')); 86 | } 87 | 88 | $validations = trim($this->option('validations')); 89 | if ($this->option('fields_from_file')) { 90 | $validations = $this->processJSONValidations($this->option('fields_from_file')); 91 | } 92 | 93 | $fieldsArray = explode(';', $fields); 94 | $fillableArray = []; 95 | $migrationFields = ''; 96 | 97 | foreach ($fieldsArray as $item) { 98 | $spareParts = explode('#', trim($item)); 99 | $fillableArray[] = $spareParts[0]; 100 | $modifier = !empty($spareParts[2]) ? $spareParts[2] : 'nullable'; 101 | 102 | // Process migration fields 103 | $migrationFields .= $spareParts[0] . '#' . $spareParts[1]; 104 | $migrationFields .= '#' . $modifier; 105 | $migrationFields .= ';'; 106 | } 107 | 108 | $commaSeparetedString = implode("', '", $fillableArray); 109 | $fillable = "['" . $commaSeparetedString . "']"; 110 | 111 | $indexes = $this->option('indexes'); 112 | $relationships = $this->option('relationships'); 113 | if ($this->option('fields_from_file')) { 114 | $relationships = $this->processJSONRelationships($this->option('fields_from_file')); 115 | } 116 | 117 | $softDeletes = $this->option('soft-deletes'); 118 | 119 | $this->call('crud:api-controller', ['name' => $controllerNamespace . $modelName . 'Controller', '--crud-name' => $name, '--model-name' => $modelName, '--model-namespace' => $modelNamespace, '--pagination' => $perPage, '--validations' => $validations]); 120 | $this->call('crud:model', ['name' => $modelNamespace . $modelName, '--fillable' => $fillable, '--table' => $tableName, '--pk' => $primaryKey, '--relationships' => $relationships, '--soft-deletes' => $softDeletes]); 121 | $this->call('crud:migration', ['name' => $migrationName, '--schema' => $migrationFields, '--pk' => $primaryKey, '--indexes' => $indexes, '--foreign-keys' => $foreignKeys, '--soft-deletes' => $softDeletes]); 122 | 123 | // Updating the Http/routes.php file 124 | $routeFile = base_path('routes/api.php'); 125 | 126 | if (file_exists($routeFile) && (strtolower($this->option('route')) === 'yes')) { 127 | $this->controller = ($controllerNamespace != '') ? $controllerNamespace . $modelName . 'Controller' : $modelName . 'Controller'; 128 | 129 | $isAdded = File::append($routeFile, "\n" . implode("\n", $this->addRoutes())); 130 | 131 | if ($isAdded) { 132 | $this->info('Crud Api route added to ' . $routeFile); 133 | } else { 134 | $this->info('Unable to add the route to ' . $routeFile); 135 | } 136 | } 137 | } 138 | 139 | /** 140 | * Add routes. 141 | * 142 | * @return array 143 | */ 144 | protected function addRoutes() 145 | { 146 | return ["Route::resource('" . $this->routeName . "', '" . $this->controller . "', ['except' => ['create', 'edit']]);"]; 147 | } 148 | 149 | /** 150 | * Process the JSON Fields. 151 | * 152 | * @param string $file 153 | * 154 | * @return string 155 | */ 156 | protected function processJSONFields($file) 157 | { 158 | $json = File::get($file); 159 | $fields = json_decode($json); 160 | 161 | $fieldsString = ''; 162 | foreach ($fields->fields as $field) { 163 | if ($field->type == 'select') { 164 | $fieldsString .= $field->name . '#' . $field->type . '#options=' . json_encode($field->options) . ';'; 165 | } else { 166 | $fieldsString .= $field->name . '#' . $field->type . ';'; 167 | } 168 | } 169 | 170 | $fieldsString = rtrim($fieldsString, ';'); 171 | 172 | return $fieldsString; 173 | } 174 | 175 | /** 176 | * Process the JSON Foreign keys. 177 | * 178 | * @param string $file 179 | * 180 | * @return string 181 | */ 182 | protected function processJSONForeignKeys($file) 183 | { 184 | $json = File::get($file); 185 | $fields = json_decode($json); 186 | 187 | if (! property_exists($fields, 'foreign_keys')) { 188 | return ''; 189 | } 190 | 191 | $foreignKeysString = ''; 192 | foreach ($fields->foreign_keys as $foreign_key) { 193 | $foreignKeysString .= $foreign_key->column . '#' . $foreign_key->references . '#' . $foreign_key->on; 194 | 195 | if (property_exists($foreign_key, 'onDelete')) { 196 | $foreignKeysString .= '#' . $foreign_key->onDelete; 197 | } 198 | 199 | if (property_exists($foreign_key, 'onUpdate')) { 200 | $foreignKeysString .= '#' . $foreign_key->onUpdate; 201 | } 202 | 203 | $foreignKeysString .= ','; 204 | } 205 | 206 | $foreignKeysString = rtrim($foreignKeysString, ','); 207 | 208 | return $foreignKeysString; 209 | } 210 | 211 | /** 212 | * Process the JSON Relationships. 213 | * 214 | * @param string $file 215 | * 216 | * @return string 217 | */ 218 | protected function processJSONRelationships($file) 219 | { 220 | $json = File::get($file); 221 | $fields = json_decode($json); 222 | 223 | if (!property_exists($fields, 'relationships')) { 224 | return ''; 225 | } 226 | 227 | $relationsString = ''; 228 | foreach ($fields->relationships as $relation) { 229 | $relationsString .= $relation->name . '#' . $relation->type . '#' . $relation->class . ';'; 230 | } 231 | 232 | $relationsString = rtrim($relationsString, ';'); 233 | 234 | return $relationsString; 235 | } 236 | 237 | /** 238 | * Process the JSON Validations. 239 | * 240 | * @param string $file 241 | * 242 | * @return string 243 | */ 244 | protected function processJSONValidations($file) 245 | { 246 | $json = File::get($file); 247 | $fields = json_decode($json); 248 | 249 | if (!property_exists($fields, 'validations')) { 250 | return ''; 251 | } 252 | 253 | $validationsString = ''; 254 | foreach ($fields->validations as $validation) { 255 | $validationsString .= $validation->field . '#' . $validation->rules . ';'; 256 | } 257 | 258 | $validationsString = rtrim($validationsString, ';'); 259 | 260 | return $validationsString; 261 | } 262 | } 263 | -------------------------------------------------------------------------------- /src/Commands/CrudApiControllerCommand.php: -------------------------------------------------------------------------------- 1 | option('controller-namespace') ? $this->option('controller-namespace') : 'Http\Controllers'); 61 | } 62 | 63 | /** 64 | * Determine if the class already exists. 65 | * 66 | * @param string $rawName 67 | * @return bool 68 | */ 69 | protected function alreadyExists($rawName) 70 | { 71 | if ($this->option('force')) { 72 | return false; 73 | } 74 | return parent::alreadyExists($rawName); 75 | } 76 | 77 | /** 78 | * Build the model class with the given name. 79 | * 80 | * @param string $name 81 | * 82 | * @return string 83 | */ 84 | protected function buildClass($name) 85 | { 86 | $stub = $this->files->get($this->getStub()); 87 | 88 | $crudName = strtolower($this->option('crud-name')); 89 | $crudNameSingular = Str::singular($crudName); 90 | $modelName = $this->option('model-name'); 91 | $modelNamespace = $this->option('model-namespace'); 92 | $perPage = intval($this->option('pagination')); 93 | $validations = rtrim($this->option('validations'), ';'); 94 | 95 | $validationRules = ''; 96 | if (trim($validations) != '') { 97 | $validationRules = "\$this->validate(\$request, ["; 98 | 99 | $rules = explode(';', $validations); 100 | foreach ($rules as $v) { 101 | if (trim($v) == '') { 102 | continue; 103 | } 104 | 105 | // extract field name and args 106 | $parts = explode('#', $v); 107 | $fieldName = trim($parts[0]); 108 | $rules = trim($parts[1]); 109 | $validationRules .= "\n\t\t\t'$fieldName' => '$rules',"; 110 | } 111 | 112 | $validationRules = substr($validationRules, 0, -1); // lose the last comma 113 | $validationRules .= "\n\t\t]);"; 114 | } 115 | 116 | return $this->replaceNamespace($stub, $name) 117 | ->replaceCrudName($stub, $crudName) 118 | ->replaceCrudNameSingular($stub, $crudNameSingular) 119 | ->replaceModelName($stub, $modelName) 120 | ->replaceModelNamespace($stub, $modelNamespace) 121 | ->replaceModelNamespaceSegments($stub, $modelNamespace) 122 | ->replaceValidationRules($stub, $validationRules) 123 | ->replacePaginationNumber($stub, $perPage) 124 | ->replaceClass($stub, $name); 125 | } 126 | 127 | /** 128 | * Replace the crudName for the given stub. 129 | * 130 | * @param string $stub 131 | * @param string $crudName 132 | * 133 | * @return $this 134 | */ 135 | protected function replaceCrudName(&$stub, $crudName) 136 | { 137 | $stub = str_replace('{{crudName}}', $crudName, $stub); 138 | 139 | return $this; 140 | } 141 | 142 | /** 143 | * Replace the crudNameSingular for the given stub. 144 | * 145 | * @param string $stub 146 | * @param string $crudNameSingular 147 | * 148 | * @return $this 149 | */ 150 | protected function replaceCrudNameSingular(&$stub, $crudNameSingular) 151 | { 152 | $stub = str_replace('{{crudNameSingular}}', $crudNameSingular, $stub); 153 | 154 | return $this; 155 | } 156 | 157 | /** 158 | * Replace the modelName for the given stub. 159 | * 160 | * @param string $stub 161 | * @param string $modelName 162 | * 163 | * @return $this 164 | */ 165 | protected function replaceModelName(&$stub, $modelName) 166 | { 167 | $stub = str_replace('{{modelName}}', $modelName, $stub); 168 | 169 | return $this; 170 | } 171 | 172 | /** 173 | * Replace the modelNamespace for the given stub. 174 | * 175 | * @param string $stub 176 | * @param string $modelNamespace 177 | * 178 | * @return $this 179 | */ 180 | protected function replaceModelNamespace(&$stub, $modelNamespace) 181 | { 182 | $stub = str_replace('{{modelNamespace}}', $modelNamespace, $stub); 183 | 184 | return $this; 185 | } 186 | 187 | /** 188 | * Replace the modelNamespace segments for the given stub 189 | * 190 | * @param $stub 191 | * @param $modelNamespace 192 | * 193 | * @return $this 194 | */ 195 | protected function replaceModelNamespaceSegments(&$stub, $modelNamespace) 196 | { 197 | $modelSegments = explode('\\', $modelNamespace); 198 | foreach ($modelSegments as $key => $segment) { 199 | $stub = str_replace('{{modelNamespace[' . $key . ']}}', $segment, $stub); 200 | } 201 | 202 | $stub = preg_replace('{{modelNamespace\[\d*\]}}', '', $stub); 203 | 204 | return $this; 205 | } 206 | 207 | /** 208 | * Replace the validationRules for the given stub. 209 | * 210 | * @param string $stub 211 | * @param string $validationRules 212 | * 213 | * @return $this 214 | */ 215 | protected function replaceValidationRules(&$stub, $validationRules) 216 | { 217 | $stub = str_replace('{{validationRules}}', $validationRules, $stub); 218 | 219 | return $this; 220 | } 221 | 222 | /** 223 | * Replace the pagination placeholder for the given stub 224 | * 225 | * @param $stub 226 | * @param $perPage 227 | * 228 | * @return $this 229 | */ 230 | protected function replacePaginationNumber(&$stub, $perPage) 231 | { 232 | $stub = str_replace('{{pagination}}', $perPage, $stub); 233 | 234 | return $this; 235 | } 236 | } 237 | -------------------------------------------------------------------------------- /src/Commands/CrudCommand.php: -------------------------------------------------------------------------------- 1 | argument('name'); 67 | $modelName = Str::singular($name); 68 | $migrationName = Str::plural(Str::snake($name)); 69 | $tableName = $migrationName; 70 | 71 | $routeGroup = $this->option('route-group'); 72 | $this->routeName = ($routeGroup) ? $routeGroup . '/' . Str::snake($name, '-') : Str::snake($name, '-'); 73 | $perPage = intval($this->option('pagination')); 74 | 75 | $controllerNamespace = ($this->option('controller-namespace')) ? $this->option('controller-namespace') . '\\' : ''; 76 | $modelNamespace = ($this->option('model-namespace')) ? trim($this->option('model-namespace')) . '\\' : 'Models\\'; 77 | 78 | $fields = rtrim($this->option('fields'), ';'); 79 | 80 | if ($this->option('fields_from_file')) { 81 | $fields = $this->processJSONFields($this->option('fields_from_file')); 82 | } 83 | 84 | $primaryKey = $this->option('pk'); 85 | $viewPath = $this->option('view-path'); 86 | 87 | $foreignKeys = $this->option('foreign-keys'); 88 | 89 | if ($this->option('fields_from_file')) { 90 | $foreignKeys = $this->processJSONForeignKeys($this->option('fields_from_file')); 91 | } 92 | 93 | $validations = trim($this->option('validations')); 94 | if ($this->option('fields_from_file')) { 95 | $validations = $this->processJSONValidations($this->option('fields_from_file')); 96 | } 97 | 98 | $fieldsArray = explode(';', $fields); 99 | $fillableArray = []; 100 | $migrationFields = ''; 101 | 102 | foreach ($fieldsArray as $item) { 103 | $spareParts = explode('#', trim($item)); 104 | $fillableArray[] = $spareParts[0]; 105 | $modifier = !empty($spareParts[2]) ? $spareParts[2] : 'nullable'; 106 | 107 | // Process migration fields 108 | $migrationFields .= $spareParts[0] . '#' . $spareParts[1]; 109 | $migrationFields .= '#' . $modifier; 110 | $migrationFields .= ';'; 111 | } 112 | 113 | $commaSeparetedString = implode("', '", $fillableArray); 114 | $fillable = "['" . $commaSeparetedString . "']"; 115 | 116 | $localize = $this->option('localize'); 117 | $locales = $this->option('locales'); 118 | 119 | $indexes = $this->option('indexes'); 120 | $relationships = $this->option('relationships'); 121 | if ($this->option('fields_from_file')) { 122 | $relationships = $this->processJSONRelationships($this->option('fields_from_file')); 123 | } 124 | 125 | $formHelper = $this->option('form-helper'); 126 | $softDeletes = $this->option('soft-deletes'); 127 | 128 | $this->call('crud:controller', ['name' => $controllerNamespace . $modelName . 'Controller', '--crud-name' => $name, '--model-name' => $modelName, '--model-namespace' => $modelNamespace, '--view-path' => $viewPath, '--route-group' => $routeGroup, '--pagination' => $perPage, '--fields' => $fields, '--validations' => $validations]); 129 | $this->call('crud:model', ['name' => $modelNamespace . $modelName, '--fillable' => $fillable, '--table' => $tableName, '--pk' => $primaryKey, '--relationships' => $relationships, '--soft-deletes' => $softDeletes]); 130 | $this->call('crud:migration', ['name' => $migrationName, '--schema' => $migrationFields, '--pk' => $primaryKey, '--indexes' => $indexes, '--foreign-keys' => $foreignKeys, '--soft-deletes' => $softDeletes]); 131 | $this->call('crud:view', ['name' => $name, '--fields' => $fields, '--validations' => $validations, '--view-path' => $viewPath, '--route-group' => $routeGroup, '--localize' => $localize, '--pk' => $primaryKey, '--form-helper' => $formHelper]); 132 | if ($localize == 'yes') { 133 | $this->call('crud:lang', ['name' => $name, '--fields' => $fields, '--locales' => $locales]); 134 | } 135 | 136 | // Updating the Http/routes.php file 137 | $routeFile = base_path('routes/web.php'); 138 | 139 | if (file_exists($routeFile) && (strtolower($this->option('route')) === 'yes')) { 140 | $this->controller = ($controllerNamespace != '') ? $controllerNamespace . $modelName . 'Controller' : $modelName . 'Controller'; 141 | 142 | $isAdded = File::append($routeFile, "\n" . implode("\n", $this->addRoutes())); 143 | 144 | if ($isAdded) { 145 | $this->info('Crud/Resource route added to ' . $routeFile); 146 | } else { 147 | $this->info('Unable to add the route to ' . $routeFile); 148 | } 149 | } 150 | } 151 | 152 | /** 153 | * Add routes. 154 | * 155 | * @return array 156 | */ 157 | protected function addRoutes() 158 | { 159 | return ["Route::resource('" . $this->routeName . "', '" . $this->controller . "');"]; 160 | } 161 | 162 | /** 163 | * Process the JSON Fields. 164 | * 165 | * @param string $file 166 | * 167 | * @return string 168 | */ 169 | protected function processJSONFields($file) 170 | { 171 | $json = File::get($file); 172 | $fields = json_decode($json); 173 | 174 | $fieldsString = ''; 175 | foreach ($fields->fields as $field) { 176 | if ($field->type === 'select' || $field->type === 'enum') { 177 | $fieldsString .= $field->name . '#' . $field->type . '#options=' . json_encode($field->options) . ';'; 178 | } else { 179 | $fieldsString .= $field->name . '#' . $field->type . ';'; 180 | } 181 | } 182 | 183 | $fieldsString = rtrim($fieldsString, ';'); 184 | 185 | return $fieldsString; 186 | } 187 | 188 | /** 189 | * Process the JSON Foreign keys. 190 | * 191 | * @param string $file 192 | * 193 | * @return string 194 | */ 195 | protected function processJSONForeignKeys($file) 196 | { 197 | $json = File::get($file); 198 | $fields = json_decode($json); 199 | 200 | if (! property_exists($fields, 'foreign_keys')) { 201 | return ''; 202 | } 203 | 204 | $foreignKeysString = ''; 205 | foreach ($fields->foreign_keys as $foreign_key) { 206 | $foreignKeysString .= $foreign_key->column . '#' . $foreign_key->references . '#' . $foreign_key->on; 207 | 208 | if (property_exists($foreign_key, 'onDelete')) { 209 | $foreignKeysString .= '#' . $foreign_key->onDelete; 210 | } 211 | 212 | if (property_exists($foreign_key, 'onUpdate')) { 213 | $foreignKeysString .= '#' . $foreign_key->onUpdate; 214 | } 215 | 216 | $foreignKeysString .= ','; 217 | } 218 | 219 | $foreignKeysString = rtrim($foreignKeysString, ','); 220 | 221 | return $foreignKeysString; 222 | } 223 | 224 | /** 225 | * Process the JSON Relationships. 226 | * 227 | * @param string $file 228 | * 229 | * @return string 230 | */ 231 | protected function processJSONRelationships($file) 232 | { 233 | $json = File::get($file); 234 | $fields = json_decode($json); 235 | 236 | if (!property_exists($fields, 'relationships')) { 237 | return ''; 238 | } 239 | 240 | $relationsString = ''; 241 | foreach ($fields->relationships as $relation) { 242 | $relationsString .= $relation->name . '#' . $relation->type . '#' . $relation->class . ';'; 243 | } 244 | 245 | $relationsString = rtrim($relationsString, ';'); 246 | 247 | return $relationsString; 248 | } 249 | 250 | /** 251 | * Process the JSON Validations. 252 | * 253 | * @param string $file 254 | * 255 | * @return string 256 | */ 257 | protected function processJSONValidations($file) 258 | { 259 | $json = File::get($file); 260 | $fields = json_decode($json); 261 | 262 | if (!property_exists($fields, 'validations')) { 263 | return ''; 264 | } 265 | 266 | $validationsString = ''; 267 | foreach ($fields->validations as $validation) { 268 | $validationsString .= $validation->field . '#' . $validation->rules . ';'; 269 | } 270 | 271 | $validationsString = rtrim($validationsString, ';'); 272 | 273 | return $validationsString; 274 | } 275 | } 276 | -------------------------------------------------------------------------------- /src/Commands/CrudControllerCommand.php: -------------------------------------------------------------------------------- 1 | option('controller-namespace') ? $this->option('controller-namespace') : 'Http\Controllers'); 64 | } 65 | 66 | /** 67 | * Determine if the class already exists. 68 | * 69 | * @param string $rawName 70 | * @return bool 71 | */ 72 | protected function alreadyExists($rawName) 73 | { 74 | if ($this->option('force')) { 75 | return false; 76 | } 77 | return parent::alreadyExists($rawName); 78 | } 79 | 80 | /** 81 | * Build the model class with the given name. 82 | * 83 | * @param string $name 84 | * 85 | * @return string 86 | */ 87 | protected function buildClass($name) 88 | { 89 | $stub = $this->files->get($this->getStub()); 90 | 91 | $viewPath = $this->option('view-path') ? $this->option('view-path') . '.' : ''; 92 | $crudName = strtolower($this->option('crud-name')); 93 | $crudNameSingular = Str::singular($crudName); 94 | $modelName = $this->option('model-name'); 95 | $modelNamespace = $this->option('model-namespace'); 96 | $routeGroup = ($this->option('route-group')) ? $this->option('route-group') . '/' : ''; 97 | $routePrefix = ($this->option('route-group')) ? $this->option('route-group') : ''; 98 | $routePrefixCap = ucfirst($routePrefix); 99 | $perPage = intval($this->option('pagination')); 100 | $viewName = Str::snake($this->option('crud-name'), '-'); 101 | $fields = $this->option('fields'); 102 | $validations = rtrim($this->option('validations'), ';'); 103 | 104 | $validationRules = ''; 105 | if (trim($validations) != '') { 106 | $validationRules = "\$this->validate(\$request, ["; 107 | 108 | $rules = explode(';', $validations); 109 | foreach ($rules as $v) { 110 | if (trim($v) == '') { 111 | continue; 112 | } 113 | 114 | // extract field name and args 115 | $parts = explode('#', $v); 116 | $fieldName = trim($parts[0]); 117 | $rules = trim($parts[1]); 118 | $validationRules .= "\n\t\t\t'$fieldName' => '$rules',"; 119 | } 120 | 121 | $validationRules = substr($validationRules, 0, -1); // lose the last comma 122 | $validationRules .= "\n\t\t]);"; 123 | } 124 | 125 | if (\App::VERSION() < '5.3') { 126 | $snippet = <<hasFile('{{fieldName}}')) { 128 | \$file = \$request->file('{{fieldName}}'); 129 | \$fileName = str_random(40) . '.' . \$file->getClientOriginalExtension(); 130 | \$destinationPath = storage_path('/app/public/uploads'); 131 | \$file->move(\$destinationPath, \$fileName); 132 | \$requestData['{{fieldName}}'] = 'uploads/' . \$fileName; 133 | } 134 | EOD; 135 | } else { 136 | $snippet = <<hasFile('{{fieldName}}')) { 138 | \$requestData['{{fieldName}}'] = \$request->file('{{fieldName}}') 139 | ->store('uploads', 'public'); 140 | } 141 | EOD; 142 | } 143 | 144 | 145 | $fieldsArray = explode(';', $fields); 146 | $fileSnippet = ''; 147 | $whereSnippet = ''; 148 | 149 | if ($fields) { 150 | $x = 0; 151 | foreach ($fieldsArray as $index => $item) { 152 | $itemArray = explode('#', $item); 153 | 154 | if (trim($itemArray[1]) == 'file') { 155 | $fileSnippet .= str_replace('{{fieldName}}', trim($itemArray[0]), $snippet) . "\n"; 156 | } 157 | 158 | $fieldName = trim($itemArray[0]); 159 | 160 | $whereSnippet .= ($index == 0) ? "where('$fieldName', 'LIKE', \"%\$keyword%\")" . "\n " : "->orWhere('$fieldName', 'LIKE', \"%\$keyword%\")" . "\n "; 161 | } 162 | 163 | $whereSnippet .= "->"; 164 | } 165 | 166 | return $this->replaceNamespace($stub, $name) 167 | ->replaceViewPath($stub, $viewPath) 168 | ->replaceViewName($stub, $viewName) 169 | ->replaceCrudName($stub, $crudName) 170 | ->replaceCrudNameSingular($stub, $crudNameSingular) 171 | ->replaceModelName($stub, $modelName) 172 | ->replaceModelNamespace($stub, $modelNamespace) 173 | ->replaceModelNamespaceSegments($stub, $modelNamespace) 174 | ->replaceRouteGroup($stub, $routeGroup) 175 | ->replaceRoutePrefix($stub, $routePrefix) 176 | ->replaceRoutePrefixCap($stub, $routePrefixCap) 177 | ->replaceValidationRules($stub, $validationRules) 178 | ->replacePaginationNumber($stub, $perPage) 179 | ->replaceFileSnippet($stub, $fileSnippet) 180 | ->replaceWhereSnippet($stub, $whereSnippet) 181 | ->replaceClass($stub, $name); 182 | } 183 | 184 | /** 185 | * Replace the viewName fo the given stub. 186 | * 187 | * @param string $stub 188 | * @param string $viewName 189 | * 190 | * @return $this 191 | */ 192 | protected function replaceViewName(&$stub, $viewName) 193 | { 194 | $stub = str_replace('{{viewName}}', $viewName, $stub); 195 | 196 | return $this; 197 | } 198 | 199 | /** 200 | * Replace the viewPath for the given stub. 201 | * 202 | * @param string $stub 203 | * @param string $viewPath 204 | * 205 | * @return $this 206 | */ 207 | protected function replaceViewPath(&$stub, $viewPath) 208 | { 209 | $stub = str_replace('{{viewPath}}', $viewPath, $stub); 210 | 211 | return $this; 212 | } 213 | 214 | /** 215 | * Replace the crudName for the given stub. 216 | * 217 | * @param string $stub 218 | * @param string $crudName 219 | * 220 | * @return $this 221 | */ 222 | protected function replaceCrudName(&$stub, $crudName) 223 | { 224 | $stub = str_replace('{{crudName}}', $crudName, $stub); 225 | 226 | return $this; 227 | } 228 | 229 | /** 230 | * Replace the crudNameSingular for the given stub. 231 | * 232 | * @param string $stub 233 | * @param string $crudNameSingular 234 | * 235 | * @return $this 236 | */ 237 | protected function replaceCrudNameSingular(&$stub, $crudNameSingular) 238 | { 239 | $stub = str_replace('{{crudNameSingular}}', $crudNameSingular, $stub); 240 | 241 | return $this; 242 | } 243 | 244 | /** 245 | * Replace the modelName for the given stub. 246 | * 247 | * @param string $stub 248 | * @param string $modelName 249 | * 250 | * @return $this 251 | */ 252 | protected function replaceModelName(&$stub, $modelName) 253 | { 254 | $stub = str_replace('{{modelName}}', $modelName, $stub); 255 | 256 | return $this; 257 | } 258 | 259 | /** 260 | * Replace the modelNamespace for the given stub. 261 | * 262 | * @param string $stub 263 | * @param string $modelNamespace 264 | * 265 | * @return $this 266 | */ 267 | protected function replaceModelNamespace(&$stub, $modelNamespace) 268 | { 269 | $stub = str_replace('{{modelNamespace}}', $modelNamespace, $stub); 270 | 271 | return $this; 272 | } 273 | 274 | /** 275 | * Replace the modelNamespace segments for the given stub 276 | * 277 | * @param $stub 278 | * @param $modelNamespace 279 | * 280 | * @return $this 281 | */ 282 | protected function replaceModelNamespaceSegments(&$stub, $modelNamespace) 283 | { 284 | $modelSegments = explode('\\', $modelNamespace); 285 | foreach ($modelSegments as $key => $segment) { 286 | $stub = str_replace('{{modelNamespace[' . $key . ']}}', $segment, $stub); 287 | } 288 | 289 | $stub = preg_replace('{{modelNamespace\[\d*\]}}', '', $stub); 290 | 291 | return $this; 292 | } 293 | 294 | /** 295 | * Replace the routePrefix for the given stub. 296 | * 297 | * @param string $stub 298 | * @param string $routePrefix 299 | * 300 | * @return $this 301 | */ 302 | protected function replaceRoutePrefix(&$stub, $routePrefix) 303 | { 304 | $stub = str_replace('{{routePrefix}}', $routePrefix, $stub); 305 | 306 | return $this; 307 | } 308 | 309 | /** 310 | * Replace the routePrefixCap for the given stub. 311 | * 312 | * @param string $stub 313 | * @param string $routePrefixCap 314 | * 315 | * @return $this 316 | */ 317 | protected function replaceRoutePrefixCap(&$stub, $routePrefixCap) 318 | { 319 | $stub = str_replace('{{routePrefixCap}}', $routePrefixCap, $stub); 320 | 321 | return $this; 322 | } 323 | 324 | /** 325 | * Replace the routeGroup for the given stub. 326 | * 327 | * @param string $stub 328 | * @param string $routeGroup 329 | * 330 | * @return $this 331 | */ 332 | protected function replaceRouteGroup(&$stub, $routeGroup) 333 | { 334 | $stub = str_replace('{{routeGroup}}', $routeGroup, $stub); 335 | 336 | return $this; 337 | } 338 | 339 | /** 340 | * Replace the validationRules for the given stub. 341 | * 342 | * @param string $stub 343 | * @param string $validationRules 344 | * 345 | * @return $this 346 | */ 347 | protected function replaceValidationRules(&$stub, $validationRules) 348 | { 349 | $stub = str_replace('{{validationRules}}', $validationRules, $stub); 350 | 351 | return $this; 352 | } 353 | 354 | /** 355 | * Replace the pagination placeholder for the given stub 356 | * 357 | * @param $stub 358 | * @param $perPage 359 | * 360 | * @return $this 361 | */ 362 | protected function replacePaginationNumber(&$stub, $perPage) 363 | { 364 | $stub = str_replace('{{pagination}}', $perPage, $stub); 365 | 366 | return $this; 367 | } 368 | 369 | /** 370 | * Replace the file snippet for the given stub 371 | * 372 | * @param $stub 373 | * @param $fileSnippet 374 | * 375 | * @return $this 376 | */ 377 | protected function replaceFileSnippet(&$stub, $fileSnippet) 378 | { 379 | $stub = str_replace('{{fileSnippet}}', $fileSnippet, $stub); 380 | 381 | return $this; 382 | } 383 | 384 | /** 385 | * Replace the where snippet for the given stub 386 | * 387 | * @param $stub 388 | * @param $whereSnippet 389 | * 390 | * @return $this 391 | */ 392 | protected function replaceWhereSnippet(&$stub, $whereSnippet) 393 | { 394 | $stub = str_replace('{{whereSnippet}}', $whereSnippet, $stub); 395 | 396 | return $this; 397 | } 398 | } 399 | -------------------------------------------------------------------------------- /src/Commands/CrudLangCommand.php: -------------------------------------------------------------------------------- 1 | viewDirectoryPath = config('crudgenerator.custom_template') 58 | ? config('crudgenerator.path') 59 | : __DIR__ . '/../stubs/'; 60 | } 61 | 62 | /** 63 | * Execute the console command. 64 | * 65 | * @return void 66 | */ 67 | public function handle() 68 | { 69 | $this->crudName = $this->argument('name'); 70 | $this->locales = explode(',', $this->option('locales')); 71 | 72 | $fields = $this->option('fields'); 73 | $fieldsArray = explode(';', $fields); 74 | 75 | $this->formFields = array(); 76 | 77 | if ($fields) { 78 | $x = 0; 79 | foreach ($fieldsArray as $item) { 80 | $itemArray = explode('#', $item); 81 | $this->formFields[$x]['name'] = trim($itemArray[0]); 82 | 83 | $x++; 84 | } 85 | } 86 | 87 | foreach ($this->locales as $locale) { 88 | $locale = trim($locale); 89 | $path = config('view.paths')[0] . '/../lang/' . $locale . '/'; 90 | 91 | //create directory for locale 92 | if (!File::isDirectory($path)) { 93 | File::makeDirectory($path, 0755, true); 94 | } 95 | 96 | $langFile = $this->viewDirectoryPath . 'lang.stub'; 97 | $newLangFile = $path . lcfirst($this->crudName) . '.php'; 98 | if (!File::copy($langFile, $newLangFile)) { 99 | echo "failed to copy $langFile...\n"; 100 | } else { 101 | $this->templateVars($newLangFile); 102 | } 103 | 104 | $this->info('Lang [' . $locale . '] created successfully.'); 105 | } 106 | } 107 | 108 | /** 109 | * Translate form's fields. 110 | * 111 | * @param string $newLangFile 112 | * 113 | * @return void 114 | */ 115 | private function templateVars($newLangFile) 116 | { 117 | $messages = []; 118 | foreach ($this->formFields as $field) { 119 | $index = $field['name']; 120 | $text = ucwords(strtolower(str_replace('_', ' ', $index))); 121 | $messages[] = "'$index' => '$text'"; 122 | } 123 | 124 | File::put($newLangFile, str_replace('%%messages%%', implode(",\n", $messages), File::get($newLangFile))); 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /src/Commands/CrudMigrationCommand.php: -------------------------------------------------------------------------------- 1 | 'char', 43 | 'date' => 'date', 44 | 'datetime' => 'dateTime', 45 | 'time' => 'time', 46 | 'timestamp' => 'timestamp', 47 | 'text' => 'text', 48 | 'mediumtext' => 'mediumText', 49 | 'longtext' => 'longText', 50 | 'json' => 'json', 51 | 'jsonb' => 'jsonb', 52 | 'binary' => 'binary', 53 | 'number' => 'integer', 54 | 'integer' => 'integer', 55 | 'bigint' => 'bigInteger', 56 | 'mediumint' => 'mediumInteger', 57 | 'tinyint' => 'tinyInteger', 58 | 'smallint' => 'smallInteger', 59 | 'boolean' => 'boolean', 60 | 'decimal' => 'decimal', 61 | 'double' => 'double', 62 | 'float' => 'float', 63 | 'enum' => 'enum', 64 | ]; 65 | 66 | /** 67 | * Get the stub file for the generator. 68 | * 69 | * @return string 70 | */ 71 | protected function getStub() 72 | { 73 | return config('crudgenerator.custom_template') 74 | ? config('crudgenerator.path') . '/migration.stub' 75 | : __DIR__ . '/../stubs/migration.stub'; 76 | } 77 | 78 | /** 79 | * Get the destination class path. 80 | * 81 | * @param string $name 82 | * 83 | * @return string 84 | */ 85 | protected function getPath($name) 86 | { 87 | $name = str_replace($this->laravel->getNamespace(), '', $name); 88 | $datePrefix = date('Y_m_d_His'); 89 | 90 | return database_path('/migrations/') . $datePrefix . '_create_' . $name . '_table.php'; 91 | } 92 | 93 | /** 94 | * Build the model class with the given name. 95 | * 96 | * @param string $name 97 | * 98 | * @return string 99 | */ 100 | protected function buildClass($name) 101 | { 102 | $stub = $this->files->get($this->getStub()); 103 | 104 | $tableName = $this->argument('name'); 105 | $className = 'Create' . str_replace(' ', '', ucwords(str_replace('_', ' ', $tableName))) . 'Table'; 106 | 107 | $fieldsToIndex = trim($this->option('indexes')) != '' ? explode(',', $this->option('indexes')) : []; 108 | $foreignKeys = trim($this->option('foreign-keys')) != '' ? explode(',', $this->option('foreign-keys')) : []; 109 | 110 | $schema = rtrim($this->option('schema'), ';'); 111 | $fields = explode(';', $schema); 112 | 113 | $data = array(); 114 | 115 | if ($schema) { 116 | $x = 0; 117 | foreach ($fields as $field) { 118 | $fieldArray = explode('#', $field); 119 | $data[$x]['name'] = trim($fieldArray[0]); 120 | $data[$x]['type'] = trim($fieldArray[1]); 121 | if (($data[$x]['type'] === 'select' 122 | || $data[$x]['type'] === 'enum') 123 | && isset($fieldArray[2]) 124 | ) { 125 | $options = trim($fieldArray[2]); 126 | $data[$x]['options'] = str_replace('options=', '', $options); 127 | } 128 | 129 | $data[$x]['modifier'] = ''; 130 | 131 | $modifierLookup = [ 132 | 'comment', 133 | 'default', 134 | 'first', 135 | 'nullable', 136 | 'unsigned', 137 | ]; 138 | 139 | if (isset($fieldArray[2]) && in_array(trim($fieldArray[2]), $modifierLookup)) { 140 | $data[$x]['modifier'] = "->" . trim($fieldArray[2]) . "()"; 141 | } 142 | 143 | $x++; 144 | } 145 | } 146 | 147 | $tabIndent = ' '; 148 | 149 | $schemaFields = ''; 150 | foreach ($data as $item) { 151 | if (isset($this->typeLookup[$item['type']])) { 152 | $type = $this->typeLookup[$item['type']]; 153 | 154 | if ($type === 'select' || $type === 'enum') { 155 | $enumOptions = array_keys(json_decode($item['options'], true)); 156 | $enumOptionsStr = implode(",", array_map(function ($string) { 157 | return '"' . $string . '"'; 158 | }, $enumOptions)); 159 | $schemaFields .= "\$table->" . $type . "('" . $item['name'] . "', [" . $enumOptionsStr . "])"; 160 | } else { 161 | $schemaFields .= "\$table->" . $type . "('" . $item['name'] . "')"; 162 | } 163 | } else { 164 | $schemaFields .= "\$table->string('" . $item['name'] . "')"; 165 | } 166 | 167 | // Append column modifier 168 | $schemaFields .= $item['modifier']; 169 | $schemaFields .= ";\n" . $tabIndent . $tabIndent . $tabIndent; 170 | } 171 | 172 | // add indexes and unique indexes as necessary 173 | foreach ($fieldsToIndex as $fldData) { 174 | $line = trim($fldData); 175 | 176 | // is a unique index specified after the #? 177 | // if no hash present, we append one to make life easier 178 | if (strpos($line, '#') === false) { 179 | $line .= '#'; 180 | } 181 | 182 | // parts[0] = field name (or names if pipe separated) 183 | // parts[1] = unique specified 184 | $parts = explode('#', $line); 185 | if (strpos($parts[0], '|') !== 0) { 186 | $fieldNames = "['" . implode("', '", explode('|', $parts[0])) . "']"; // wrap single quotes around each element 187 | } else { 188 | $fieldNames = trim($parts[0]); 189 | } 190 | 191 | if (count($parts) > 1 && $parts[1] == 'unique') { 192 | $schemaFields .= "\$table->unique(" . trim($fieldNames) . ")"; 193 | } else { 194 | $schemaFields .= "\$table->index(" . trim($fieldNames) . ")"; 195 | } 196 | 197 | $schemaFields .= ";\n" . $tabIndent . $tabIndent . $tabIndent; 198 | } 199 | 200 | // foreign keys 201 | foreach ($foreignKeys as $fk) { 202 | $line = trim($fk); 203 | 204 | $parts = explode('#', $line); 205 | 206 | // if we don't have three parts, then the foreign key isn't defined properly 207 | // --foreign-keys="foreign_entity_id#id#foreign_entity#onDelete#onUpdate" 208 | if (count($parts) == 3) { 209 | $schemaFields .= "\$table->foreign('" . trim($parts[0]) . "')" 210 | . "->references('" . trim($parts[1]) . "')->on('" . trim($parts[2]) . "')"; 211 | } elseif (count($parts) == 4) { 212 | $schemaFields .= "\$table->foreign('" . trim($parts[0]) . "')" 213 | . "->references('" . trim($parts[1]) . "')->on('" . trim($parts[2]) . "')" 214 | . "->onDelete('" . trim($parts[3]) . "')" . "->onUpdate('" . trim($parts[3]) . "')"; 215 | } elseif (count($parts) == 5) { 216 | $schemaFields .= "\$table->foreign('" . trim($parts[0]) . "')" 217 | . "->references('" . trim($parts[1]) . "')->on('" . trim($parts[2]) . "')" 218 | . "->onDelete('" . trim($parts[3]) . "')" . "->onUpdate('" . trim($parts[4]) . "')"; 219 | } else { 220 | continue; 221 | } 222 | 223 | $schemaFields .= ";\n" . $tabIndent . $tabIndent . $tabIndent; 224 | } 225 | 226 | $primaryKey = $this->option('pk'); 227 | $softDeletes = $this->option('soft-deletes'); 228 | 229 | $softDeletesSnippets = ''; 230 | if ($softDeletes == 'yes') { 231 | $softDeletesSnippets = "\$table->softDeletes();\n" . $tabIndent . $tabIndent . $tabIndent; 232 | } 233 | 234 | $schemaUp = 235 | "Schema::create('" . $tableName . "', function (Blueprint \$table) { 236 | \$table->increments('" . $primaryKey . "'); 237 | \$table->timestamps();\n" . $tabIndent . $tabIndent . $tabIndent . 238 | $softDeletesSnippets . 239 | $schemaFields . 240 | "});"; 241 | 242 | $schemaDown = "Schema::drop('" . $tableName . "');"; 243 | 244 | return $this->replaceSchemaUp($stub, $schemaUp) 245 | ->replaceSchemaDown($stub, $schemaDown) 246 | ->replaceClass($stub, $className); 247 | } 248 | 249 | /** 250 | * Replace the schema_up for the given stub. 251 | * 252 | * @param string $stub 253 | * @param string $schemaUp 254 | * 255 | * @return $this 256 | */ 257 | protected function replaceSchemaUp(&$stub, $schemaUp) 258 | { 259 | $stub = str_replace('{{schema_up}}', $schemaUp, $stub); 260 | 261 | return $this; 262 | } 263 | 264 | /** 265 | * Replace the schema_down for the given stub. 266 | * 267 | * @param string $stub 268 | * @param string $schemaDown 269 | * 270 | * @return $this 271 | */ 272 | protected function replaceSchemaDown(&$stub, $schemaDown) 273 | { 274 | $stub = str_replace('{{schema_down}}', $schemaDown, $stub); 275 | 276 | return $this; 277 | } 278 | } 279 | -------------------------------------------------------------------------------- /src/Commands/CrudModelCommand.php: -------------------------------------------------------------------------------- 1 | files->get($this->getStub()); 69 | 70 | $table = $this->option('table') ?: $this->argument('name'); 71 | $fillable = $this->option('fillable'); 72 | $primaryKey = $this->option('pk'); 73 | $relationships = trim($this->option('relationships')) != '' ? explode(';', trim($this->option('relationships'))) : []; 74 | $softDeletes = $this->option('soft-deletes'); 75 | 76 | if (!empty($primaryKey)) { 77 | $primaryKey = <<replaceNamespace($stub, $name) 88 | ->replaceTable($stub, $table) 89 | ->replaceFillable($stub, $fillable) 90 | ->replacePrimaryKey($stub, $primaryKey) 91 | ->replaceSoftDelete($stub, $softDeletes); 92 | 93 | foreach ($relationships as $rel) { 94 | // relationshipname#relationshiptype#args_separated_by_pipes 95 | // e.g. employees#hasMany#App\Employee|id|dept_id 96 | // user is responsible for ensuring these relationships are valid 97 | $parts = explode('#', $rel); 98 | 99 | if (count($parts) != 3) { 100 | continue; 101 | } 102 | 103 | // blindly wrap each arg in single quotes 104 | $args = explode('|', trim($parts[2])); 105 | $argsString = ''; 106 | foreach ($args as $k => $v) { 107 | if (trim($v) == '') { 108 | continue; 109 | } 110 | 111 | $argsString .= "'" . trim($v) . "', "; 112 | } 113 | 114 | $argsString = substr($argsString, 0, -2); // remove last comma 115 | 116 | $ret->createRelationshipFunction($stub, trim($parts[0]), trim($parts[1]), $argsString); 117 | } 118 | 119 | $ret->replaceRelationshipPlaceholder($stub); 120 | 121 | return $ret->replaceClass($stub, $name); 122 | } 123 | 124 | /** 125 | * Replace the table for the given stub. 126 | * 127 | * @param string $stub 128 | * @param string $table 129 | * 130 | * @return $this 131 | */ 132 | protected function replaceTable(&$stub, $table) 133 | { 134 | $stub = str_replace('{{table}}', $table, $stub); 135 | 136 | return $this; 137 | } 138 | 139 | /** 140 | * Replace the fillable for the given stub. 141 | * 142 | * @param string $stub 143 | * @param string $fillable 144 | * 145 | * @return $this 146 | */ 147 | protected function replaceFillable(&$stub, $fillable) 148 | { 149 | $stub = str_replace('{{fillable}}', $fillable, $stub); 150 | 151 | return $this; 152 | } 153 | 154 | /** 155 | * Replace the primary key for the given stub. 156 | * 157 | * @param string $stub 158 | * @param string $primaryKey 159 | * 160 | * @return $this 161 | */ 162 | protected function replacePrimaryKey(&$stub, $primaryKey) 163 | { 164 | $stub = str_replace('{{primaryKey}}', $primaryKey, $stub); 165 | 166 | return $this; 167 | } 168 | 169 | /** 170 | * Replace the (optional) soft deletes part for the given stub. 171 | * 172 | * @param string $stub 173 | * @param string $replaceSoftDelete 174 | * 175 | * @return $this 176 | */ 177 | protected function replaceSoftDelete(&$stub, $replaceSoftDelete) 178 | { 179 | if ($replaceSoftDelete == 'yes') { 180 | $stub = str_replace('{{softDeletes}}', "use SoftDeletes;\n ", $stub); 181 | $stub = str_replace('{{useSoftDeletes}}', "use Illuminate\Database\Eloquent\SoftDeletes;\n", $stub); 182 | } else { 183 | $stub = str_replace('{{softDeletes}}', '', $stub); 184 | $stub = str_replace('{{useSoftDeletes}}', '', $stub); 185 | } 186 | 187 | return $this; 188 | } 189 | 190 | /** 191 | * Create the code for a model relationship 192 | * 193 | * @param string $stub 194 | * @param string $relationshipName the name of the function, e.g. owners 195 | * @param string $relationshipType the type of the relationship, hasOne, hasMany, belongsTo etc 196 | * @param array $relationshipArgs args for the relationship function 197 | */ 198 | protected function createRelationshipFunction(&$stub, $relationshipName, $relationshipType, $argsString) 199 | { 200 | $tabIndent = ' '; 201 | $code = "public function " . $relationshipName . "()\n" . $tabIndent . "{\n" . $tabIndent . $tabIndent 202 | . "return \$this->" . $relationshipType . "(" . $argsString . ");" 203 | . "\n" . $tabIndent . "}"; 204 | 205 | $str = '{{relationships}}'; 206 | $stub = str_replace($str, $code . "\n" . $tabIndent . $str, $stub); 207 | 208 | return $this; 209 | } 210 | 211 | /** 212 | * remove the relationships placeholder when it's no longer needed 213 | * 214 | * @param $stub 215 | * @return $this 216 | */ 217 | protected function replaceRelationshipPlaceholder(&$stub) 218 | { 219 | $stub = str_replace('{{relationships}}', '', $stub); 220 | return $this; 221 | } 222 | } 223 | -------------------------------------------------------------------------------- /src/Commands/CrudViewCommand.php: -------------------------------------------------------------------------------- 1 | 'text', 48 | 'char' => 'text', 49 | 'varchar' => 'text', 50 | 'text' => 'textarea', 51 | 'mediumtext' => 'textarea', 52 | 'longtext' => 'textarea', 53 | 'json' => 'textarea', 54 | 'jsonb' => 'textarea', 55 | 'binary' => 'textarea', 56 | 'password' => 'password', 57 | 'email' => 'email', 58 | 'number' => 'number', 59 | 'integer' => 'number', 60 | 'bigint' => 'number', 61 | 'mediumint' => 'number', 62 | 'tinyint' => 'number', 63 | 'smallint' => 'number', 64 | 'decimal' => 'number', 65 | 'double' => 'number', 66 | 'float' => 'number', 67 | 'date' => 'date', 68 | 'datetime' => 'datetime-local', 69 | 'timestamp' => 'datetime-local', 70 | 'time' => 'time', 71 | 'radio' => 'radio', 72 | 'boolean' => 'radio', 73 | 'enum' => 'select', 74 | 'select' => 'select', 75 | 'file' => 'file', 76 | ]; 77 | 78 | /** 79 | * Variables that can be used in stubs 80 | * 81 | * @var array 82 | */ 83 | protected $vars = [ 84 | 'formFields', 85 | 'formFieldsHtml', 86 | 'varName', 87 | 'crudName', 88 | 'crudNameCap', 89 | 'crudNameSingular', 90 | 'primaryKey', 91 | 'modelName', 92 | 'modelNameCap', 93 | 'viewName', 94 | 'routePrefix', 95 | 'routePrefixCap', 96 | 'routeGroup', 97 | 'formHeadingHtml', 98 | 'formBodyHtml', 99 | 'viewTemplateDir', 100 | 'formBodyHtmlForShowView', 101 | ]; 102 | 103 | /** 104 | * Form's fields. 105 | * 106 | * @var array 107 | */ 108 | protected $formFields = []; 109 | 110 | /** 111 | * Html of Form's fields. 112 | * 113 | * @var string 114 | */ 115 | protected $formFieldsHtml = ''; 116 | 117 | /** 118 | * Number of columns to show from the table. Others are hidden. 119 | * 120 | * @var integer 121 | */ 122 | protected $defaultColumnsToShow = 3; 123 | 124 | /** 125 | * Variable name with first letter in lowercase 126 | * 127 | * @var string 128 | */ 129 | protected $varName = ''; 130 | 131 | /** 132 | * Name of the Crud. 133 | * 134 | * @var string 135 | */ 136 | protected $crudName = ''; 137 | 138 | /** 139 | * Crud Name in capital form. 140 | * 141 | * @var string 142 | */ 143 | protected $crudNameCap = ''; 144 | 145 | /** 146 | * Crud Name in singular form. 147 | * 148 | * @var string 149 | */ 150 | protected $crudNameSingular = ''; 151 | 152 | /** 153 | * Primary key of the model. 154 | * 155 | * @var string 156 | */ 157 | protected $primaryKey = 'id'; 158 | 159 | /** 160 | * Name of the Model. 161 | * 162 | * @var string 163 | */ 164 | protected $modelName = ''; 165 | 166 | /** 167 | * Name of the Model with first letter in capital 168 | * 169 | * @var string 170 | */ 171 | protected $modelNameCap = ''; 172 | 173 | /** 174 | * Name of the View Dir. 175 | * 176 | * @var string 177 | */ 178 | protected $viewName = ''; 179 | 180 | /** 181 | * Prefix of the route 182 | * 183 | * @var string 184 | */ 185 | protected $routePrefix = ''; 186 | 187 | /** 188 | * Prefix of the route with first letter in capital letter 189 | * 190 | * @var string 191 | */ 192 | protected $routePrefixCap = ''; 193 | 194 | /** 195 | * Name or prefix of the Route Group. 196 | * 197 | * @var string 198 | */ 199 | protected $routeGroup = ''; 200 | 201 | /** 202 | * Html of the form heading. 203 | * 204 | * @var string 205 | */ 206 | protected $formHeadingHtml = ''; 207 | 208 | /** 209 | * Html of the form body. 210 | * 211 | * @var string 212 | */ 213 | protected $formBodyHtml = ''; 214 | 215 | /** 216 | * Html of view to show. 217 | * 218 | * @var string 219 | */ 220 | protected $formBodyHtmlForShowView = ''; 221 | 222 | /** 223 | * User defined values 224 | * 225 | * @var array 226 | */ 227 | protected $customData = []; 228 | 229 | /** 230 | * Template directory where views are generated 231 | * 232 | * @var string 233 | */ 234 | protected $viewTemplateDir = ''; 235 | 236 | /** 237 | * Delimiter used for replacing values 238 | * 239 | * @var array 240 | */ 241 | protected $delimiter; 242 | 243 | /** 244 | * Create a new command instance. 245 | */ 246 | public function __construct() 247 | { 248 | parent::__construct(); 249 | 250 | if (config('crudgenerator.view_columns_number')) { 251 | $this 252 | ->defaultColumnsToShow = config('crudgenerator.view_columns_number'); 253 | } 254 | 255 | $this->delimiter = config('crudgenerator.custom_delimiter') 256 | ? config('crudgenerator.custom_delimiter') 257 | : ['%%', '%%']; 258 | } 259 | 260 | /** 261 | * Execute the console command. 262 | * 263 | * @return void 264 | */ 265 | public function handle() 266 | { 267 | $formHelper = $this->option('form-helper'); 268 | $this->viewDirectoryPath = config('crudgenerator.custom_template') 269 | ? config('crudgenerator.path') . 'views/' . $formHelper . '/' 270 | : __DIR__ . '/../stubs/views/' . $formHelper . '/'; 271 | 272 | 273 | $this->crudName = strtolower($this->argument('name')); 274 | $this->varName = lcfirst($this->argument('name')); 275 | $this->crudNameCap = ucwords($this->crudName); 276 | $this->crudNameSingular = Str::singular($this->crudName); 277 | $this->modelName = Str::singular($this->argument('name')); 278 | $this->modelNameCap = ucfirst($this->modelName); 279 | $this->customData = $this->option('custom-data'); 280 | $this->primaryKey = $this->option('pk'); 281 | $this->routeGroup = ($this->option('route-group')) 282 | ? $this->option('route-group') . '/' 283 | : $this->option('route-group'); 284 | $this->routePrefix = ($this->option('route-group')) ? $this->option('route-group') : ''; 285 | $this->routePrefixCap = ucfirst($this->routePrefix); 286 | $this->viewName = Str::snake($this->argument('name'), '-'); 287 | 288 | $viewDirectory = config('view.paths')[0] . '/'; 289 | if ($this->option('view-path')) { 290 | $this->userViewPath = $this->option('view-path'); 291 | $path = $viewDirectory . $this->userViewPath . '/' . $this->viewName . '/'; 292 | } else { 293 | $path = $viewDirectory . $this->viewName . '/'; 294 | } 295 | 296 | $this->viewTemplateDir = isset($this->userViewPath) 297 | ? $this->userViewPath . '.' . $this->viewName 298 | : $this->viewName; 299 | 300 | if (!File::isDirectory($path)) { 301 | File::makeDirectory($path, 0755, true); 302 | } 303 | 304 | $fields = $this->option('fields'); 305 | $fieldsArray = explode(';', $fields); 306 | 307 | $this->formFields = []; 308 | 309 | $validations = $this->option('validations'); 310 | 311 | if ($fields) { 312 | $x = 0; 313 | foreach ($fieldsArray as $item) { 314 | $itemArray = explode('#', $item); 315 | 316 | $this->formFields[$x]['name'] = trim($itemArray[0]); 317 | $this->formFields[$x]['type'] = trim($itemArray[1]); 318 | $this->formFields[$x]['required'] = preg_match('/' . $itemArray[0] . '/', $validations)? true : false; 319 | 320 | if (($this->formFields[$x]['type'] === 'select' 321 | || $this->formFields[$x]['type'] === 'enum') 322 | && isset($itemArray[2]) 323 | ) { 324 | $options = trim($itemArray[2]); 325 | $options = str_replace('options=', '', $options); 326 | 327 | $this->formFields[$x]['options'] = $options; 328 | } 329 | 330 | $x++; 331 | } 332 | } 333 | 334 | foreach ($this->formFields as $item) { 335 | $this->formFieldsHtml .= $this->createField($item); 336 | } 337 | 338 | $i = 0; 339 | foreach ($this->formFields as $key => $value) { 340 | if ($i == $this->defaultColumnsToShow) { 341 | break; 342 | } 343 | 344 | $field = $value['name']; 345 | $label = ucwords(str_replace('_', ' ', $field)); 346 | if ($this->option('localize') == 'yes') { 347 | $label = '{{ trans(\'' . $this->crudName . '.' . $field . '\') }}'; 348 | } 349 | $this->formHeadingHtml .= '' . $label . ''; 350 | $this->formBodyHtml .= '{{ $item->' . $field . ' }}'; 351 | $this->formBodyHtmlForShowView .= ' ' . $label . ' {{ $%%crudNameSingular%%->' . $field . ' }} '; 352 | 353 | $i++; 354 | } 355 | 356 | $this->templateStubs($path); 357 | 358 | $this->info('View created successfully.'); 359 | } 360 | 361 | /** 362 | * Default template configuration if not provided 363 | * 364 | * @return array 365 | */ 366 | private function defaultTemplating() 367 | { 368 | return [ 369 | 'index' => ['formHeadingHtml', 'formBodyHtml', 'crudName', 'crudNameCap', 'modelName', 'viewName', 'routeGroup', 'primaryKey'], 370 | 'form' => ['formFieldsHtml'], 371 | 'create' => ['crudName', 'crudNameCap', 'modelName', 'modelNameCap', 'viewName', 'routeGroup', 'viewTemplateDir'], 372 | 'edit' => ['crudName', 'crudNameSingular', 'crudNameCap', 'modelNameCap', 'modelName', 'viewName', 'routeGroup', 'primaryKey', 'viewTemplateDir'], 373 | 'show' => ['formHeadingHtml', 'formBodyHtml', 'formBodyHtmlForShowView', 'crudName', 'crudNameSingular', 'crudNameCap', 'modelName', 'viewName', 'routeGroup', 'primaryKey'], 374 | ]; 375 | } 376 | 377 | /** 378 | * Generate files from stub 379 | * 380 | * @param $path 381 | */ 382 | protected function templateStubs($path) 383 | { 384 | $dynamicViewTemplate = config('crudgenerator.dynamic_view_template') 385 | ? config('crudgenerator.dynamic_view_template') 386 | : $this->defaultTemplating(); 387 | 388 | foreach ($dynamicViewTemplate as $name => $vars) { 389 | $file = $this->viewDirectoryPath . $name . '.blade.stub'; 390 | $newFile = $path . $name . '.blade.php'; 391 | if (!File::copy($file, $newFile)) { 392 | echo "failed to copy $file...\n"; 393 | } else { 394 | $this->templateVars($newFile, $vars); 395 | $this->userDefinedVars($newFile); 396 | } 397 | } 398 | } 399 | 400 | /** 401 | * Update specified values between delimiter with real values 402 | * 403 | * @param $file 404 | * @param $vars 405 | */ 406 | protected function templateVars($file, $vars) 407 | { 408 | $start = $this->delimiter[0]; 409 | $end = $this->delimiter[1]; 410 | 411 | foreach ($vars as $var) { 412 | $replace = $start . $var . $end; 413 | if (in_array($var, $this->vars)) { 414 | File::put($file, str_replace($replace, $this->$var, File::get($file))); 415 | } 416 | } 417 | } 418 | 419 | /** 420 | * Update custom values between delimiter with real values 421 | * 422 | * @param $file 423 | */ 424 | protected function userDefinedVars($file) 425 | { 426 | $start = $this->delimiter[0]; 427 | $end = $this->delimiter[1]; 428 | 429 | if ($this->customData !== null) { 430 | $customVars = explode(';', $this->customData); 431 | foreach ($customVars as $rawVar) { 432 | $arrayVar = explode('=', $rawVar); 433 | File::put($file, str_replace($start . $arrayVar[0] . $end, $arrayVar[1], File::get($file))); 434 | } 435 | } 436 | } 437 | 438 | /** 439 | * Form field wrapper. 440 | * 441 | * @param string $item 442 | * @param string $field 443 | * 444 | * @return string 445 | */ 446 | protected function wrapField($item, $field) 447 | { 448 | $formGroup = File::get($this->viewDirectoryPath . 'form-fields/wrap-field.blade.stub'); 449 | 450 | $labelText = "'" . ucwords(strtolower(str_replace('_', ' ', $item['name']))) . "'"; 451 | 452 | if ($this->option('localize') == 'yes') { 453 | $labelText = 'trans(\'' . $this->crudName . '.' . $item['name'] . '\')'; 454 | } 455 | 456 | return sprintf($formGroup, $item['name'], $labelText, $field); 457 | } 458 | 459 | /** 460 | * Form field generator. 461 | * 462 | * @param array $item 463 | * 464 | * @return string 465 | */ 466 | protected function createField($item) 467 | { 468 | switch ($this->typeLookup[$item['type']]) { 469 | case 'password': 470 | return $this->createPasswordField($item); 471 | case 'datetime-local': 472 | case 'time': 473 | return $this->createInputField($item); 474 | case 'radio': 475 | return $this->createRadioField($item); 476 | case 'textarea': 477 | return $this->createTextareaField($item); 478 | case 'select': 479 | case 'enum': 480 | return $this->createSelectField($item); 481 | default: // text 482 | return $this->createFormField($item); 483 | } 484 | } 485 | 486 | /** 487 | * Create a specific field using the form helper. 488 | * 489 | * @param array $item 490 | * 491 | * @return string 492 | */ 493 | protected function createFormField($item) 494 | { 495 | $start = $this->delimiter[0]; 496 | $end = $this->delimiter[1]; 497 | 498 | $required = $item['required'] ? 'required' : ''; 499 | 500 | $markup = File::get($this->viewDirectoryPath . 'form-fields/form-field.blade.stub'); 501 | $markup = str_replace($start . 'required' . $end, $required, $markup); 502 | $markup = str_replace($start . 'fieldType' . $end, $this->typeLookup[$item['type']], $markup); 503 | $markup = str_replace($start . 'itemName' . $end, $item['name'], $markup); 504 | $markup = str_replace($start . 'crudNameSingular' . $end, $this->crudNameSingular, $markup); 505 | 506 | return $this->wrapField( 507 | $item, 508 | $markup 509 | ); 510 | } 511 | 512 | /** 513 | * Create a password field using the form helper. 514 | * 515 | * @param array $item 516 | * 517 | * @return string 518 | */ 519 | protected function createPasswordField($item) 520 | { 521 | $start = $this->delimiter[0]; 522 | $end = $this->delimiter[1]; 523 | 524 | $required = $item['required'] ? 'required' : ''; 525 | 526 | $markup = File::get($this->viewDirectoryPath . 'form-fields/password-field.blade.stub'); 527 | $markup = str_replace($start . 'required' . $end, $required, $markup); 528 | $markup = str_replace($start . 'itemName' . $end, $item['name'], $markup); 529 | $markup = str_replace($start . 'crudNameSingular' . $end, $this->crudNameSingular, $markup); 530 | 531 | return $this->wrapField( 532 | $item, 533 | $markup 534 | ); 535 | } 536 | 537 | /** 538 | * Create a generic input field using the form helper. 539 | * 540 | * @param array $item 541 | * 542 | * @return string 543 | */ 544 | protected function createInputField($item) 545 | { 546 | $start = $this->delimiter[0]; 547 | $end = $this->delimiter[1]; 548 | 549 | $required = $item['required'] ? 'required' : ''; 550 | 551 | $markup = File::get($this->viewDirectoryPath . 'form-fields/input-field.blade.stub'); 552 | $markup = str_replace($start . 'required' . $end, $required, $markup); 553 | $markup = str_replace($start . 'fieldType' . $end, $this->typeLookup[$item['type']], $markup); 554 | $markup = str_replace($start . 'itemName' . $end, $item['name'], $markup); 555 | $markup = str_replace($start . 'crudNameSingular' . $end, $this->crudNameSingular, $markup); 556 | 557 | return $this->wrapField( 558 | $item, 559 | $markup 560 | ); 561 | } 562 | 563 | /** 564 | * Create a yes/no radio button group using the form helper. 565 | * 566 | * @param array $item 567 | * 568 | * @return string 569 | */ 570 | protected function createRadioField($item) 571 | { 572 | $start = $this->delimiter[0]; 573 | $end = $this->delimiter[1]; 574 | 575 | $markup = File::get($this->viewDirectoryPath . 'form-fields/radio-field.blade.stub'); 576 | $markup = str_replace($start . 'itemName' . $end, $item['name'], $markup); 577 | $markup = str_replace($start . 'crudNameSingular' . $end, $this->crudNameSingular, $markup); 578 | 579 | return $this->wrapField( 580 | $item, 581 | $markup 582 | ); 583 | } 584 | 585 | /** 586 | * Create a textarea field using the form helper. 587 | * 588 | * @param array $item 589 | * 590 | * @return string 591 | */ 592 | protected function createTextareaField($item) 593 | { 594 | $start = $this->delimiter[0]; 595 | $end = $this->delimiter[1]; 596 | 597 | $required = $item['required'] ? 'required' : ''; 598 | 599 | $markup = File::get($this->viewDirectoryPath . 'form-fields/textarea-field.blade.stub'); 600 | $markup = str_replace($start . 'required' . $end, $required, $markup); 601 | $markup = str_replace($start . 'fieldType' . $end, $this->typeLookup[$item['type']], $markup); 602 | $markup = str_replace($start . 'itemName' . $end, $item['name'], $markup); 603 | $markup = str_replace($start . 'crudNameSingular' . $end, $this->crudNameSingular, $markup); 604 | 605 | return $this->wrapField( 606 | $item, 607 | $markup 608 | ); 609 | } 610 | 611 | /** 612 | * Create a select field using the form helper. 613 | * 614 | * @param array $item 615 | * 616 | * @return string 617 | */ 618 | protected function createSelectField($item) 619 | { 620 | $start = $this->delimiter[0]; 621 | $end = $this->delimiter[1]; 622 | 623 | $required = $item['required'] ? 'required' : ''; 624 | 625 | $markup = File::get($this->viewDirectoryPath . 'form-fields/select-field.blade.stub'); 626 | $markup = str_replace($start . 'required' . $end, $required, $markup); 627 | $markup = str_replace($start . 'options' . $end, $item['options'], $markup); 628 | $markup = str_replace($start . 'itemName' . $end, $item['name'], $markup); 629 | $markup = str_replace($start . 'crudNameSingular' . $end, $this->crudNameSingular, $markup); 630 | 631 | return $this->wrapField( 632 | $item, 633 | $markup 634 | ); 635 | } 636 | } 637 | -------------------------------------------------------------------------------- /src/CrudGeneratorServiceProvider.php: -------------------------------------------------------------------------------- 1 | publishes([ 24 | __DIR__ . '/../config/crudgenerator.php' => config_path('crudgenerator.php'), 25 | ]); 26 | 27 | $this->publishes([ 28 | __DIR__ . '/../publish/views/' => base_path('resources/views/'), 29 | ]); 30 | 31 | $this->publishes([ 32 | __DIR__ . '/stubs/' => base_path('resources/crud-generator/'), 33 | ]); 34 | } 35 | 36 | /** 37 | * Register the service provider. 38 | * 39 | * @return void 40 | */ 41 | public function register() 42 | { 43 | $this->commands( 44 | 'Appzcoder\CrudGenerator\Commands\CrudCommand', 45 | 'Appzcoder\CrudGenerator\Commands\CrudControllerCommand', 46 | 'Appzcoder\CrudGenerator\Commands\CrudModelCommand', 47 | 'Appzcoder\CrudGenerator\Commands\CrudMigrationCommand', 48 | 'Appzcoder\CrudGenerator\Commands\CrudViewCommand', 49 | 'Appzcoder\CrudGenerator\Commands\CrudLangCommand', 50 | 'Appzcoder\CrudGenerator\Commands\CrudApiCommand', 51 | 'Appzcoder\CrudGenerator\Commands\CrudApiControllerCommand' 52 | ); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/stubs/api-controller.stub: -------------------------------------------------------------------------------- 1 | paginate({{pagination}}); 20 | 21 | return ${{crudName}}; 22 | } 23 | 24 | /** 25 | * Store a newly created resource in storage. 26 | * 27 | * @param \Illuminate\Http\Request $request 28 | * 29 | * @return \Illuminate\Http\Response 30 | */ 31 | public function store(Request $request) 32 | { 33 | {{validationRules}} 34 | ${{crudNameSingular}} = {{modelName}}::create($request->all()); 35 | 36 | return response()->json(${{crudNameSingular}}, 201); 37 | } 38 | 39 | /** 40 | * Display the specified resource. 41 | * 42 | * @param int $id 43 | * 44 | * @return \Illuminate\Http\Response 45 | */ 46 | public function show($id) 47 | { 48 | ${{crudNameSingular}} = {{modelName}}::findOrFail($id); 49 | 50 | return ${{crudNameSingular}}; 51 | } 52 | 53 | /** 54 | * Update the specified resource in storage. 55 | * 56 | * @param \Illuminate\Http\Request $request 57 | * @param int $id 58 | * 59 | * @return \Illuminate\Http\Response 60 | */ 61 | public function update(Request $request, $id) 62 | { 63 | {{validationRules}} 64 | ${{crudNameSingular}} = {{modelName}}::findOrFail($id); 65 | ${{crudNameSingular}}->update($request->all()); 66 | 67 | return response()->json(${{crudNameSingular}}, 200); 68 | } 69 | 70 | /** 71 | * Remove the specified resource from storage. 72 | * 73 | * @param int $id 74 | * 75 | * @return \Illuminate\Http\Response 76 | */ 77 | public function destroy($id) 78 | { 79 | {{modelName}}::destroy($id); 80 | 81 | return response()->json(null, 204); 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /src/stubs/controller.stub: -------------------------------------------------------------------------------- 1 | get('search'); 20 | $perPage = {{pagination}}; 21 | 22 | if (!empty($keyword)) { 23 | ${{crudName}} = {{modelName}}::{{whereSnippet}}latest()->paginate($perPage); 24 | } else { 25 | ${{crudName}} = {{modelName}}::latest()->paginate($perPage); 26 | } 27 | 28 | return view('{{viewPath}}{{viewName}}.index', compact('{{crudName}}')); 29 | } 30 | 31 | /** 32 | * Show the form for creating a new resource. 33 | * 34 | * @return \Illuminate\View\View 35 | */ 36 | public function create() 37 | { 38 | return view('{{viewPath}}{{viewName}}.create'); 39 | } 40 | 41 | /** 42 | * Store a newly created resource in storage. 43 | * 44 | * @param \Illuminate\Http\Request $request 45 | * 46 | * @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector 47 | */ 48 | public function store(Request $request) 49 | { 50 | {{validationRules}} 51 | $requestData = $request->all(); 52 | {{fileSnippet}} 53 | {{modelName}}::create($requestData); 54 | 55 | return redirect('{{routeGroup}}{{viewName}}')->with('flash_message', '{{modelName}} added!'); 56 | } 57 | 58 | /** 59 | * Display the specified resource. 60 | * 61 | * @param int $id 62 | * 63 | * @return \Illuminate\View\View 64 | */ 65 | public function show($id) 66 | { 67 | ${{crudNameSingular}} = {{modelName}}::findOrFail($id); 68 | 69 | return view('{{viewPath}}{{viewName}}.show', compact('{{crudNameSingular}}')); 70 | } 71 | 72 | /** 73 | * Show the form for editing the specified resource. 74 | * 75 | * @param int $id 76 | * 77 | * @return \Illuminate\View\View 78 | */ 79 | public function edit($id) 80 | { 81 | ${{crudNameSingular}} = {{modelName}}::findOrFail($id); 82 | 83 | return view('{{viewPath}}{{viewName}}.edit', compact('{{crudNameSingular}}')); 84 | } 85 | 86 | /** 87 | * Update the specified resource in storage. 88 | * 89 | * @param \Illuminate\Http\Request $request 90 | * @param int $id 91 | * 92 | * @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector 93 | */ 94 | public function update(Request $request, $id) 95 | { 96 | {{validationRules}} 97 | $requestData = $request->all(); 98 | {{fileSnippet}} 99 | ${{crudNameSingular}} = {{modelName}}::findOrFail($id); 100 | ${{crudNameSingular}}->update($requestData); 101 | 102 | return redirect('{{routeGroup}}{{viewName}}')->with('flash_message', '{{modelName}} updated!'); 103 | } 104 | 105 | /** 106 | * Remove the specified resource from storage. 107 | * 108 | * @param int $id 109 | * 110 | * @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector 111 | */ 112 | public function destroy($id) 113 | { 114 | {{modelName}}::destroy($id); 115 | 116 | return redirect('{{routeGroup}}{{viewName}}')->with('flash_message', '{{modelName}} deleted!'); 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /src/stubs/lang.stub: -------------------------------------------------------------------------------- 1 | 2 | 3 |

4 | {{ __('Dashboard') }} 5 |

6 |
7 | 8 |
9 |
10 |
11 |
12 | 13 |
14 |
15 |

16 | Create %%modelName%% 17 |

18 |
19 | < Back 20 |
21 |
22 | 23 | @if ($errors->any()) 24 |
    25 | @foreach ($errors->all() as $error) 26 |
  • {{ $error }}
  • 27 | @endforeach 28 |
29 | @endif 30 | 31 |
32 | @csrf() 33 | @include ('%%viewTemplateDir%%.form', ['formMode' => 'create']) 34 |
35 |
36 | 37 |
38 |
39 |
40 |
41 | 42 | -------------------------------------------------------------------------------- /src/stubs/views/blade/edit.blade.stub: -------------------------------------------------------------------------------- 1 | 2 | 3 |

4 | {{ __('Dashboard') }} 5 |

6 |
7 | 8 |
9 |
10 |
11 |
12 | 13 |
14 |
15 |

16 | Edit %%modelName%% #{{ $%%crudNameSingular%%->%%primaryKey%% }} 17 |

18 |
19 | < Back 20 |
21 |
22 | 23 | @if ($errors->any()) 24 |
    25 | @foreach ($errors->all() as $error) 26 |
  • {{ $error }}
  • 27 | @endforeach 28 |
29 | @endif 30 | 31 |
32 | {{ method_field('PATCH') }} 33 | @csrf() 34 | 35 | @include ('%%viewTemplateDir%%.form', ['formMode' => 'edit']) 36 |
37 |
38 | 39 |
40 |
41 |
42 |
43 |
44 | -------------------------------------------------------------------------------- /src/stubs/views/blade/form-fields/checkbox-field.blade.stub: -------------------------------------------------------------------------------- 1 |
2 | 6 |
7 |
8 | 12 |
13 | -------------------------------------------------------------------------------- /src/stubs/views/blade/form-fields/form-field.blade.stub: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/stubs/views/blade/form-fields/input-field.blade.stub: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/stubs/views/blade/form-fields/password-field.blade.stub: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/stubs/views/blade/form-fields/radio-field.blade.stub: -------------------------------------------------------------------------------- 1 |
2 | 6 |
7 |
8 | 12 |
13 | -------------------------------------------------------------------------------- /src/stubs/views/blade/form-fields/select-field.blade.stub: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/stubs/views/blade/form-fields/textarea-field.blade.stub: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/stubs/views/blade/form-fields/wrap-field.blade.stub: -------------------------------------------------------------------------------- 1 |
2 | 3 | %3$s 4 | {!! $errors->first('%1$s', '

:message

') !!} 5 |
6 | -------------------------------------------------------------------------------- /src/stubs/views/blade/form.blade.stub: -------------------------------------------------------------------------------- 1 | %%formFieldsHtml%% 2 | 3 |
4 | 7 |
8 | -------------------------------------------------------------------------------- /src/stubs/views/blade/index.blade.stub: -------------------------------------------------------------------------------- 1 | 2 | 3 |

4 | {{ __('Dashboard') }} 5 |

6 |
7 | 8 |
9 |
10 |
11 |
12 |
13 |
14 |

15 | %%crudNameCap%% 16 |

17 |
18 | Add New 19 |
20 |
21 |
22 | 23 | @if (session()->has('flash_message')) 24 |
25 | 26 | {{ session('flash_message') }} 27 | 28 | 31 |
32 | @endif 33 | 34 | 35 | 36 | 37 | 38 | %%formHeadingHtml%% 39 | 40 | 41 | 42 | 43 | @foreach($%%crudName%% as $item) 44 | 45 | 46 | %%formBodyHtml%% 47 | 57 | 58 | @endforeach 59 | 60 |
#Actions
{{ $loop->iteration }} 48 | 49 | 50 | 51 |
52 | {{ method_field('DELETE') }} 53 | @csrf() 54 | 55 |
56 |
61 | 62 |
63 | {!! $%%crudName%%->appends(['search' => Request::get('search')])->render() !!} 64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 | -------------------------------------------------------------------------------- /src/stubs/views/blade/show.blade.stub: -------------------------------------------------------------------------------- 1 | 2 | 3 |

4 | {{ __('Dashboard') }} 5 |

6 |
7 | 8 |
9 |
10 |
11 |
12 | 13 |
14 |
15 |

16 | Show %%modelName%% 17 |

18 |
19 | < Back 20 |
21 |
22 |
23 | 24 | 25 | 26 | 27 | 28 | 29 | %%formBodyHtmlForShowView%% 30 |
ID{{ $%%crudNameSingular%%->%%primaryKey%% }}
31 | 32 |
33 | 34 |
35 |
36 |
37 |
38 |
39 | -------------------------------------------------------------------------------- /tests/CrudGeneratorTest.php: -------------------------------------------------------------------------------- 1 | artisan('crud:generate', [ 8 | // 'name' => 'Posts', 9 | // '--fields' => "title#string; content#text; category#select#options=technology,tips,health", 10 | // ]); 11 | // $this->assertContains('Controller already exists!', $this->consoleOutput()); 12 | } 13 | 14 | public function testControllerGenerateCommand() 15 | { 16 | $this->artisan('crud:controller', [ 17 | 'name' => 'CustomersController', 18 | '--crud-name' => 'customers', 19 | '--model-name' => 'Customer', 20 | ]); 21 | 22 | $this->assertContains('Controller created successfully.', $this->consoleOutput()); 23 | 24 | $this->assertFileExists(app_path('Http/Controllers') . '/CustomersController.php'); 25 | } 26 | 27 | public function testModelGenerateCommand() 28 | { 29 | $this->artisan('crud:model', [ 30 | 'name' => 'Customer', 31 | '--fillable' => "['name', 'email']", 32 | ]); 33 | 34 | $this->assertContains('Model created successfully.', $this->consoleOutput()); 35 | 36 | $this->assertFileExists(app_path() . '/Customer.php'); 37 | } 38 | 39 | public function testMigrationGenerateCommand() 40 | { 41 | $this->artisan('crud:migration', [ 42 | 'name' => 'customers', 43 | '--schema' => 'name#string; email#email', 44 | ]); 45 | 46 | $this->assertContains('Migration created successfully.', $this->consoleOutput()); 47 | } 48 | 49 | public function testViewGenerateCommand() 50 | { 51 | $this->artisan('crud:view', [ 52 | 'name' => 'customers', 53 | '--fields' => "title#string; body#text", 54 | ]); 55 | 56 | $this->assertContains('View created successfully.', $this->consoleOutput()); 57 | 58 | $this->assertDirectoryExists(config('view.paths')[0] . '/customers'); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /tests/Kernel.php: -------------------------------------------------------------------------------- 1 | app['artisan']; 36 | } 37 | } -------------------------------------------------------------------------------- /tests/TestCase.php: -------------------------------------------------------------------------------- 1 | set('view.paths', [__DIR__ . '/temp/views']); 14 | 15 | $app['config']->set('crudgenerator', [ 16 | 'custom_template' => false, 17 | 'path' => base_path('resources/crud-generator/'), 18 | 'view_columns_number' => 3, 19 | 'dynamic_view_template' => [ 20 | 'index' => ['formHeadingHtml', 'formBodyHtml', 'crudName', 'crudNameCap', 'modelName', 'viewName', 'routeGroup', 'primaryKey'], 21 | 'form' => ['formFieldsHtml'], 22 | 'create' => ['crudName', 'crudNameCap', 'modelName', 'modelNameCap', 'viewName', 'routeGroup', 'viewTemplateDir'], 23 | 'edit' => ['crudName', 'crudNameSingular', 'crudNameCap', 'modelNameCap', 'modelName', 'viewName', 'routeGroup', 'primaryKey', 'viewTemplateDir'], 24 | 'show' => ['formHeadingHtml', 'formBodyHtml', 'formBodyHtmlForShowView', 'crudName', 'crudNameSingular', 'crudNameCap', 'modelName', 'viewName', 'routeGroup', 'primaryKey'], 25 | ] 26 | ]); 27 | } 28 | 29 | public function setUp() 30 | { 31 | parent::setUp(); 32 | 33 | exec('rm -rf ' . __DIR__ . '/temp/*'); 34 | exec('rm -rf ' . app_path() . '/*'); 35 | exec('rm -rf ' . database_path('migrations') . '/*'); 36 | } 37 | 38 | public function tearDown() 39 | { 40 | parent::tearDown(); 41 | 42 | $this->consoleOutput = ''; 43 | } 44 | 45 | public function resolveApplicationConsoleKernel($app) 46 | { 47 | $app->singleton('artisan', function ($app) { 48 | return new \Illuminate\Console\Application($app, $app['events'], $app->version()); 49 | }); 50 | 51 | $app->singleton('Illuminate\Contracts\Console\Kernel', Kernel::class); 52 | } 53 | 54 | public function consoleOutput() 55 | { 56 | return $this->consoleOutput ?: $this->consoleOutput = $this->app[Kernel::class]->output(); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /tests/temp/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | --------------------------------------------------------------------------------