├── .gitignore ├── .styleci.yml ├── .travis.yml ├── LICENSE ├── changelog.md ├── composer.json ├── config └── laravel-generator.php ├── contributing.md ├── phpunit.xml ├── readme.md ├── readme_zh_CN.md ├── resources ├── assets │ ├── css │ │ ├── element.css │ │ └── fonts │ │ │ ├── element-icons.ttf │ │ │ └── element-icons.woff │ ├── images │ │ └── logo.png │ ├── js │ │ ├── axios.js │ │ ├── element-2.4.js │ │ └── vue.js │ └── vs │ │ ├── base │ │ └── worker │ │ │ └── workerMain.js │ │ ├── basic-languages │ │ └── java │ │ │ └── java.js │ │ ├── editor │ │ ├── editor.main.css │ │ ├── editor.main.js │ │ ├── editor.main.nls.de.js │ │ ├── editor.main.nls.es.js │ │ ├── editor.main.nls.fr.js │ │ ├── editor.main.nls.it.js │ │ ├── editor.main.nls.ja.js │ │ ├── editor.main.nls.js │ │ ├── editor.main.nls.ko.js │ │ ├── editor.main.nls.ru.js │ │ ├── editor.main.nls.zh-cn.js │ │ └── editor.main.nls.zh-tw.js │ │ ├── language │ │ ├── css │ │ │ ├── cssMode.js │ │ │ └── cssWorker.js │ │ ├── html │ │ │ ├── htmlMode.js │ │ │ └── htmlWorker.js │ │ ├── json │ │ │ ├── jsonMode.js │ │ │ └── jsonWorker.js │ │ └── typescript │ │ │ ├── tsMode.js │ │ │ └── tsWorker.js │ │ └── loader.js ├── lang │ ├── en │ │ └── generator.php │ └── zh_CN │ │ └── generator.php ├── migrations │ └── 2025_04_11_095115_create_laravel_generators_table.php └── views │ ├── generator.blade.php │ ├── generator_logs.blade.php │ ├── generator_migrate.blade.php │ ├── index.blade.php │ ├── layout.blade.php │ ├── template_lists.blade.php │ └── template_update.blade.php ├── routes └── route.php ├── src ├── Console │ └── InstallCommand.php ├── Controllers │ ├── AssetController.php │ ├── GeneratorController.php │ └── GeneratorTemplateController.php ├── Database │ └── GeneratorSeeder.php ├── FileCreator.php ├── GeneratorServiceProvider.php ├── GeneratorUtils.php ├── Message.php ├── MigrationCreator.php └── Models │ ├── LaravelGenerator.php │ ├── LaravelGeneratorConfig.php │ ├── LaravelGeneratorLog.php │ └── LaravelGeneratorType.php └── tests ├── GeneratorUtilsTest.php └── LaravelGeneratorLogTest.php /.gitignore: -------------------------------------------------------------------------------- 1 | /vendor 2 | composer.lock 3 | .idea/* -------------------------------------------------------------------------------- /.styleci.yml: -------------------------------------------------------------------------------- 1 | preset: laravel -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: php 2 | php: 3 | - '7.0' 4 | - '7.1' 5 | - '7.2' 6 | - nightly 7 | 8 | sudo: false 9 | matrix: 10 | fast_finish: true 11 | 12 | before_script: 13 | - travis_retry composer self-update 14 | - travis_retry composer install --no-interaction 15 | 16 | script: 17 | - vendor/bin/phpunit -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) foryoufeng 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | 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, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /changelog.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to `laravel-generator` will be documented in this file. 4 | 5 | * # Version 4.0 6 | **更新时间:2025-06-04** 7 | 8 | 更新日志 9 | 10 | * Modified the template engine to blade and changed the default template content / 修改模板引擎为blade,并修改默认模板内容 11 | * Changed the configuration of the `laravel-generator.php` configuration file / 更改配置文件`laravel-generator.php`的配置 12 | * Added template migration for existing tables. / 增加已存在表的模板迁移 13 | 14 | * # Version 3.0 15 | * Added support for Laravel 12+ / 增加对Laravel 12+ 的支持 16 | * Added a record/log panel. / 添加生成记录面板 17 | * Modified the default template to Vue 3. / 修改默认模板为vue3 18 | --- 19 | 20 | * # Version 2.6 21 | * Added support for Laravel 10+ / 增加对Laravel 10+ 的支持 22 | 23 | --- 24 | 25 | * # Version 2.6 26 | * Added support for Laravel 9+ / 增加对Laravel 9+ 的支持 27 | * fix the add create:migrate bug / 修复创建迁移文件报错bug 28 | * fix the change migrate bug / 修复修改migrate表结构字段报错的bug 29 | --- 30 | 31 | * # Version 2.1 32 | * Added support for Laravel 6+ / 增加对Laravel 6+ 的支持 33 | * fix the primary key add bug / 修复新增字段为主键时引起的报错bug 34 | * fix the migrate show error cause by add two or more filed / 修复migrate板块添加多个字段时服务器报错的bug 35 | * fix the Relationships show error cause by add two or more relationship in the generator/ 修复generator板块中添加多个关联关系时服务器报错的bug 36 | * add some fields such as display name and rule and so on /新增显示名称、规则等字段 37 | --- 38 | 39 | * # Version 2.0 40 | * add tables / 添加数据表 41 | * add templates and can edit/add / 增加模板,可以进行添加和编辑 42 | * add Foreign and Relationships / 增加外键和关联关系 43 | * add some fields such as display name and rule and so on /新增显示名称、规则等字段 44 | --- 45 | * # Version 1.0 46 | 47 | ### Added 48 | - First version. / 第一个版本 49 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "foryoufeng/laravel-generator", 3 | "description": "A tool for generate Laravel code file", 4 | "license": "MIT", 5 | "authors": [ 6 | { 7 | "name": "foryoufeng", 8 | "email": "foryoufeng@gmail.com", 9 | "homepage": "https://github.com/foryoufeng" 10 | } 11 | ], 12 | "homepage": "https://github.com/foryoufeng/laravel-generator", 13 | "keywords": ["Laravel", "laravel-generator","code generator"], 14 | "require": { 15 | "laravel/framework": "~9.0||~10.0||~11.0||~12.0", 16 | "doctrine/dbal": "^4.2" 17 | }, 18 | "require-dev": { 19 | "roave/security-advisories": "dev-latest" 20 | }, 21 | "autoload": { 22 | "psr-4": { 23 | "Foryoufeng\\Generator\\": "src/" 24 | } 25 | }, 26 | "autoload-dev": { 27 | "psr-4": { 28 | "Foryoufeng\\Generator\\Tests\\": "tests" 29 | } 30 | }, 31 | "extra": { 32 | "laravel": { 33 | "providers": [ 34 | "Foryoufeng\\Generator\\GeneratorServiceProvider" 35 | ] 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /config/laravel-generator.php: -------------------------------------------------------------------------------- 1 | 'Laravel Generator', 5 | // the url to access 6 | 'route'=>'laravel-generator', 7 | // the rule can be used by the field 8 | 'rules'=>[ 9 | 'string', 10 | 'email', 11 | 'file', 12 | 'numeric', 13 | 'array', 14 | 'alpha', 15 | 'alpha_dash', 16 | 'alpha_num', 17 | 'date', 18 | 'boolean', 19 | 'distinct', 20 | 'phone', 21 | 'custom' 22 | ], 23 | 'custom_keys'=>[ 24 | 'author'=>env('GENERATOR_AUTHOR','system') 25 | ] 26 | ]; 27 | -------------------------------------------------------------------------------- /contributing.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | Contributions are welcome and will be fully credited. 4 | 5 | Contributions are accepted via Pull Requests on [Github](https://github.com/foryoufeng/laravel-generator). 6 | 7 | # Things you could do 8 | If you want to contribute but do not know where to start, this list provides some starting points. 9 | - Add license text 10 | - Remove rewriteRules.php 11 | - Set up TravisCI, StyleCI, ScrutinizerCI 12 | - Write a comprehensive ReadMe 13 | 14 | ## Pull Requests 15 | 16 | - **Add tests!** - Your patch won't be accepted if it doesn't have tests. 17 | 18 | - **Document any change in behaviour** - Make sure the `readme.md` and any other relevant documentation are kept up-to-date. 19 | 20 | - **Consider our release cycle** - We try to follow [SemVer v2.0.0](http://semver.org/). Randomly breaking public APIs is not an option. 21 | 22 | - **One pull request per feature** - If you want to do more than one thing, send multiple pull requests. 23 | 24 | - **Send coherent history** - Make sure each individual commit in your pull request is meaningful. If you had to make multiple intermediate commits while developing, please [squash them](http://www.git-scm.com/book/en/v2/Git-Tools-Rewriting-History#Changing-Multiple-Commit-Messages) before submitting. 25 | 26 | 27 | **Happy coding**! 28 | -------------------------------------------------------------------------------- /phpunit.xml: -------------------------------------------------------------------------------- 1 | 2 | 12 | 13 | 14 | ./tests/ 15 | 16 | 17 | 18 | 19 | src/ 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 |

2 | Laravel Generator 3 |

4 | 5 |

6 | Total Downloads 7 | Latest Stable Version 8 | License 9 |

10 | 11 | # Laravel Generator 12 | ## [中文文档](readme_zh_CN.md) 13 | A graphical interface code generator for quickly generating code for Laravel applications. 14 | 15 | 16 | # Installation 17 | 18 | If you have PHP and Composer installed, you can install the Laravel installer via Composer: 19 | 20 | ```bash 21 | composer require --dev foryoufeng/laravel-generator 22 | ``` 23 | 24 | Run the following command to install the code generator: 25 | 26 | ``` 27 | php artisan generator:install 28 | ``` 29 | 30 | Add the creator's information in the `.env` file: 31 | ```sh 32 | GENERATOR_AUTHOR=Your Name 33 | ``` 34 | 35 | Now you can access your application URL `http://localhost:8000/laravel-generator` to use `laravel-generator`. 36 | 37 | ## Configuration file 38 | 39 | Publish configuration file 40 | 41 | ```sh 42 | php artisan vendor:publish --tag=laravel-generator 43 | ``` 44 | 45 | `generator.php` file description: 46 | 47 | ```php 48 | 'Laravel Generator', 51 | // the url to access 52 | 'route'=>'laravel-generator', 53 | // Define rules 54 | 'rules' => [ 55 | 'string', 56 | 'email', 57 | 'file', 58 | 'numeric', 59 | 'array', 60 | 'alpha', 61 | 'alpha_dash', 62 | 'alpha_num', 63 | 'date', 64 | 'boolean', 65 | 'distinct', 66 | 'phone', 67 | 'custom' 68 | ], 69 | // Custom parameters 70 | 'custom_keys'=>[ 71 | 'author'=>env('GENERATOR_AUTHOR','system') 72 | ] 73 | ]; 74 | ``` 75 | 76 | ## Update Log 77 | 78 | View [changelog](changelog.md) for update logs. 79 | 80 | MIT. Please see the [license file](license.md) for more information. 81 | -------------------------------------------------------------------------------- /readme_zh_CN.md: -------------------------------------------------------------------------------- 1 |

2 | Laravel Generator 3 |

4 | 5 |

6 | Total Downloads 7 | Latest Stable Version 8 | License 9 |

10 | 11 | # Laravel Generator 12 | 为laravel应用快速生成代码的图形化界面代码生成器 13 | 14 | 15 | ## 安装 16 | 17 | 通过Composer 18 | 19 | ``` bash 20 | composer require --dev foryoufeng/laravel-generator 21 | ``` 22 | 23 | 运行如下命令来安装代码生成器 24 | 25 | ``` 26 | php artisan generator:install 27 | ``` 28 | 29 | 在`.env`中添加配置创建人的信息 30 | ```sh 31 | GENERATOR_AUTHOR=你的名字 32 | ``` 33 | 34 | 现在您可以访问您的应用url`http://localhost:8000/laravel-generator` 来使用`Laravel Generator`了 35 | 36 | 37 | ## 配置文件 38 | 39 | 发布配置文件 40 | 41 | ```sh 42 | php artisan vendor:publish --tag=laravel-generator 43 | ``` 44 | 45 | `generator.php` 文件说明 46 | 47 | ``` 48 | 'Laravel Generator', 52 | // 访问地址 53 | 'route'=>'laravel-generator', 54 | // 定义规则 55 | 'rules'=>[ 56 | 'string', 57 | 'email', 58 | 'file', 59 | 'numeric', 60 | 'array', 61 | 'alpha', 62 | 'alpha_dash', 63 | 'alpha_num', 64 | 'date', 65 | 'boolean', 66 | 'distinct', 67 | 'phone', 68 | 'custom' 69 | ], 70 | //自定义参数 71 | 'custom_keys'=>[ 72 | 'author'=>env('GENERATOR_AUTHOR','system') 73 | ] 74 | ]; 75 | ``` 76 | 77 | ## 更新记录 78 | 79 | 查看 [changelog](changelog.md) 获取更新记录 80 | 81 | MIT. Please see the [license file](license.md) for more information. 82 | -------------------------------------------------------------------------------- /resources/assets/css/fonts/element-icons.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/foryoufeng/laravel-generator/96a960d660ad8bf6f0955746712c10cfa582e9c3/resources/assets/css/fonts/element-icons.ttf -------------------------------------------------------------------------------- /resources/assets/css/fonts/element-icons.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/foryoufeng/laravel-generator/96a960d660ad8bf6f0955746712c10cfa582e9c3/resources/assets/css/fonts/element-icons.woff -------------------------------------------------------------------------------- /resources/assets/images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/foryoufeng/laravel-generator/96a960d660ad8bf6f0955746712c10cfa582e9c3/resources/assets/images/logo.png -------------------------------------------------------------------------------- /resources/assets/js/axios.js: -------------------------------------------------------------------------------- 1 | /* axios v0.18.0 | (c) 2018 by Matt Zabriskie */ 2 | !function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define([],t):"object"==typeof exports?exports.axios=t():e.axios=t()}(this,function(){return function(e){function t(r){if(n[r])return n[r].exports;var o=n[r]={exports:{},id:r,loaded:!1};return e[r].call(o.exports,o,o.exports,t),o.loaded=!0,o.exports}var n={};return t.m=e,t.c=n,t.p="",t(0)}([function(e,t,n){e.exports=n(1)},function(e,t,n){"use strict";function r(e){var t=new s(e),n=i(s.prototype.request,t);return o.extend(n,s.prototype,t),o.extend(n,t),n}var o=n(2),i=n(3),s=n(5),u=n(6),a=r(u);a.Axios=s,a.create=function(e){return r(o.merge(u,e))},a.Cancel=n(23),a.CancelToken=n(24),a.isCancel=n(20),a.all=function(e){return Promise.all(e)},a.spread=n(25),e.exports=a,e.exports.default=a},function(e,t,n){"use strict";function r(e){return"[object Array]"===R.call(e)}function o(e){return"[object ArrayBuffer]"===R.call(e)}function i(e){return"undefined"!=typeof FormData&&e instanceof FormData}function s(e){var t;return t="undefined"!=typeof ArrayBuffer&&ArrayBuffer.isView?ArrayBuffer.isView(e):e&&e.buffer&&e.buffer instanceof ArrayBuffer}function u(e){return"string"==typeof e}function a(e){return"number"==typeof e}function c(e){return"undefined"==typeof e}function f(e){return null!==e&&"object"==typeof e}function p(e){return"[object Date]"===R.call(e)}function d(e){return"[object File]"===R.call(e)}function l(e){return"[object Blob]"===R.call(e)}function h(e){return"[object Function]"===R.call(e)}function m(e){return f(e)&&h(e.pipe)}function y(e){return"undefined"!=typeof URLSearchParams&&e instanceof URLSearchParams}function w(e){return e.replace(/^\s*/,"").replace(/\s*$/,"")}function g(){return("undefined"==typeof navigator||"ReactNative"!==navigator.product)&&("undefined"!=typeof window&&"undefined"!=typeof document)}function v(e,t){if(null!==e&&"undefined"!=typeof e)if("object"!=typeof e&&(e=[e]),r(e))for(var n=0,o=e.length;n 6 | * @license MIT 7 | */ 8 | e.exports=function(e){return null!=e&&(n(e)||r(e)||!!e._isBuffer)}},function(e,t,n){"use strict";function r(e){this.defaults=e,this.interceptors={request:new s,response:new s}}var o=n(6),i=n(2),s=n(17),u=n(18);r.prototype.request=function(e){"string"==typeof e&&(e=i.merge({url:arguments[0]},arguments[1])),e=i.merge(o,{method:"get"},this.defaults,e),e.method=e.method.toLowerCase();var t=[u,void 0],n=Promise.resolve(e);for(this.interceptors.request.forEach(function(e){t.unshift(e.fulfilled,e.rejected)}),this.interceptors.response.forEach(function(e){t.push(e.fulfilled,e.rejected)});t.length;)n=n.then(t.shift(),t.shift());return n},i.forEach(["delete","get","head","options"],function(e){r.prototype[e]=function(t,n){return this.request(i.merge(n||{},{method:e,url:t}))}}),i.forEach(["post","put","patch"],function(e){r.prototype[e]=function(t,n,r){return this.request(i.merge(r||{},{method:e,url:t,data:n}))}}),e.exports=r},function(e,t,n){"use strict";function r(e,t){!i.isUndefined(e)&&i.isUndefined(e["Content-Type"])&&(e["Content-Type"]=t)}function o(){var e;return"undefined"!=typeof XMLHttpRequest?e=n(8):"undefined"!=typeof process&&(e=n(8)),e}var i=n(2),s=n(7),u={"Content-Type":"application/x-www-form-urlencoded"},a={adapter:o(),transformRequest:[function(e,t){return s(t,"Content-Type"),i.isFormData(e)||i.isArrayBuffer(e)||i.isBuffer(e)||i.isStream(e)||i.isFile(e)||i.isBlob(e)?e:i.isArrayBufferView(e)?e.buffer:i.isURLSearchParams(e)?(r(t,"application/x-www-form-urlencoded;charset=utf-8"),e.toString()):i.isObject(e)?(r(t,"application/json;charset=utf-8"),JSON.stringify(e)):e}],transformResponse:[function(e){if("string"==typeof e)try{e=JSON.parse(e)}catch(e){}return e}],timeout:0,xsrfCookieName:"XSRF-TOKEN",xsrfHeaderName:"X-XSRF-TOKEN",maxContentLength:-1,validateStatus:function(e){return e>=200&&e<300}};a.headers={common:{Accept:"application/json, text/plain, */*"}},i.forEach(["delete","get","head"],function(e){a.headers[e]={}}),i.forEach(["post","put","patch"],function(e){a.headers[e]=i.merge(u)}),e.exports=a},function(e,t,n){"use strict";var r=n(2);e.exports=function(e,t){r.forEach(e,function(n,r){r!==t&&r.toUpperCase()===t.toUpperCase()&&(e[t]=n,delete e[r])})}},function(e,t,n){"use strict";var r=n(2),o=n(9),i=n(12),s=n(13),u=n(14),a=n(10),c="undefined"!=typeof window&&window.btoa&&window.btoa.bind(window)||n(15);e.exports=function(e){return new Promise(function(t,f){var p=e.data,d=e.headers;r.isFormData(p)&&delete d["Content-Type"];var l=new XMLHttpRequest,h="onreadystatechange",m=!1;if("undefined"==typeof window||!window.XDomainRequest||"withCredentials"in l||u(e.url)||(l=new window.XDomainRequest,h="onload",m=!0,l.onprogress=function(){},l.ontimeout=function(){}),e.auth){var y=e.auth.username||"",w=e.auth.password||"";d.Authorization="Basic "+c(y+":"+w)}if(l.open(e.method.toUpperCase(),i(e.url,e.params,e.paramsSerializer),!0),l.timeout=e.timeout,l[h]=function(){if(l&&(4===l.readyState||m)&&(0!==l.status||l.responseURL&&0===l.responseURL.indexOf("file:"))){var n="getAllResponseHeaders"in l?s(l.getAllResponseHeaders()):null,r=e.responseType&&"text"!==e.responseType?l.response:l.responseText,i={data:r,status:1223===l.status?204:l.status,statusText:1223===l.status?"No Content":l.statusText,headers:n,config:e,request:l};o(t,f,i),l=null}},l.onerror=function(){f(a("Network Error",e,null,l)),l=null},l.ontimeout=function(){f(a("timeout of "+e.timeout+"ms exceeded",e,"ECONNABORTED",l)),l=null},r.isStandardBrowserEnv()){var g=n(16),v=(e.withCredentials||u(e.url))&&e.xsrfCookieName?g.read(e.xsrfCookieName):void 0;v&&(d[e.xsrfHeaderName]=v)}if("setRequestHeader"in l&&r.forEach(d,function(e,t){"undefined"==typeof p&&"content-type"===t.toLowerCase()?delete d[t]:l.setRequestHeader(t,e)}),e.withCredentials&&(l.withCredentials=!0),e.responseType)try{l.responseType=e.responseType}catch(t){if("json"!==e.responseType)throw t}"function"==typeof e.onDownloadProgress&&l.addEventListener("progress",e.onDownloadProgress),"function"==typeof e.onUploadProgress&&l.upload&&l.upload.addEventListener("progress",e.onUploadProgress),e.cancelToken&&e.cancelToken.promise.then(function(e){l&&(l.abort(),f(e),l=null)}),void 0===p&&(p=null),l.send(p)})}},function(e,t,n){"use strict";var r=n(10);e.exports=function(e,t,n){var o=n.config.validateStatus;n.status&&o&&!o(n.status)?t(r("Request failed with status code "+n.status,n.config,null,n.request,n)):e(n)}},function(e,t,n){"use strict";var r=n(11);e.exports=function(e,t,n,o,i){var s=new Error(e);return r(s,t,n,o,i)}},function(e,t){"use strict";e.exports=function(e,t,n,r,o){return e.config=t,n&&(e.code=n),e.request=r,e.response=o,e}},function(e,t,n){"use strict";function r(e){return encodeURIComponent(e).replace(/%40/gi,"@").replace(/%3A/gi,":").replace(/%24/g,"$").replace(/%2C/gi,",").replace(/%20/g,"+").replace(/%5B/gi,"[").replace(/%5D/gi,"]")}var o=n(2);e.exports=function(e,t,n){if(!t)return e;var i;if(n)i=n(t);else if(o.isURLSearchParams(t))i=t.toString();else{var s=[];o.forEach(t,function(e,t){null!==e&&"undefined"!=typeof e&&(o.isArray(e)?t+="[]":e=[e],o.forEach(e,function(e){o.isDate(e)?e=e.toISOString():o.isObject(e)&&(e=JSON.stringify(e)),s.push(r(t)+"="+r(e))}))}),i=s.join("&")}return i&&(e+=(e.indexOf("?")===-1?"?":"&")+i),e}},function(e,t,n){"use strict";var r=n(2),o=["age","authorization","content-length","content-type","etag","expires","from","host","if-modified-since","if-unmodified-since","last-modified","location","max-forwards","proxy-authorization","referer","retry-after","user-agent"];e.exports=function(e){var t,n,i,s={};return e?(r.forEach(e.split("\n"),function(e){if(i=e.indexOf(":"),t=r.trim(e.substr(0,i)).toLowerCase(),n=r.trim(e.substr(i+1)),t){if(s[t]&&o.indexOf(t)>=0)return;"set-cookie"===t?s[t]=(s[t]?s[t]:[]).concat([n]):s[t]=s[t]?s[t]+", "+n:n}}),s):s}},function(e,t,n){"use strict";var r=n(2);e.exports=r.isStandardBrowserEnv()?function(){function e(e){var t=e;return n&&(o.setAttribute("href",t),t=o.href),o.setAttribute("href",t),{href:o.href,protocol:o.protocol?o.protocol.replace(/:$/,""):"",host:o.host,search:o.search?o.search.replace(/^\?/,""):"",hash:o.hash?o.hash.replace(/^#/,""):"",hostname:o.hostname,port:o.port,pathname:"/"===o.pathname.charAt(0)?o.pathname:"/"+o.pathname}}var t,n=/(msie|trident)/i.test(navigator.userAgent),o=document.createElement("a");return t=e(window.location.href),function(n){var o=r.isString(n)?e(n):n;return o.protocol===t.protocol&&o.host===t.host}}():function(){return function(){return!0}}()},function(e,t){"use strict";function n(){this.message="String contains an invalid character"}function r(e){for(var t,r,i=String(e),s="",u=0,a=o;i.charAt(0|u)||(a="=",u%1);s+=a.charAt(63&t>>8-u%1*8)){if(r=i.charCodeAt(u+=.75),r>255)throw new n;t=t<<8|r}return s}var o="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";n.prototype=new Error,n.prototype.code=5,n.prototype.name="InvalidCharacterError",e.exports=r},function(e,t,n){"use strict";var r=n(2);e.exports=r.isStandardBrowserEnv()?function(){return{write:function(e,t,n,o,i,s){var u=[];u.push(e+"="+encodeURIComponent(t)),r.isNumber(n)&&u.push("expires="+new Date(n).toGMTString()),r.isString(o)&&u.push("path="+o),r.isString(i)&&u.push("domain="+i),s===!0&&u.push("secure"),document.cookie=u.join("; ")},read:function(e){var t=document.cookie.match(new RegExp("(^|;\\s*)("+e+")=([^;]*)"));return t?decodeURIComponent(t[3]):null},remove:function(e){this.write(e,"",Date.now()-864e5)}}}():function(){return{write:function(){},read:function(){return null},remove:function(){}}}()},function(e,t,n){"use strict";function r(){this.handlers=[]}var o=n(2);r.prototype.use=function(e,t){return this.handlers.push({fulfilled:e,rejected:t}),this.handlers.length-1},r.prototype.eject=function(e){this.handlers[e]&&(this.handlers[e]=null)},r.prototype.forEach=function(e){o.forEach(this.handlers,function(t){null!==t&&e(t)})},e.exports=r},function(e,t,n){"use strict";function r(e){e.cancelToken&&e.cancelToken.throwIfRequested()}var o=n(2),i=n(19),s=n(20),u=n(6),a=n(21),c=n(22);e.exports=function(e){r(e),e.baseURL&&!a(e.url)&&(e.url=c(e.baseURL,e.url)),e.headers=e.headers||{},e.data=i(e.data,e.headers,e.transformRequest),e.headers=o.merge(e.headers.common||{},e.headers[e.method]||{},e.headers||{}),o.forEach(["delete","get","head","post","put","patch","common"],function(t){delete e.headers[t]});var t=e.adapter||u.adapter;return t(e).then(function(t){return r(e),t.data=i(t.data,t.headers,e.transformResponse),t},function(t){return s(t)||(r(e),t&&t.response&&(t.response.data=i(t.response.data,t.response.headers,e.transformResponse))),Promise.reject(t)})}},function(e,t,n){"use strict";var r=n(2);e.exports=function(e,t,n){return r.forEach(n,function(n){e=n(e,t)}),e}},function(e,t){"use strict";e.exports=function(e){return!(!e||!e.__CANCEL__)}},function(e,t){"use strict";e.exports=function(e){return/^([a-z][a-z\d\+\-\.]*:)?\/\//i.test(e)}},function(e,t){"use strict";e.exports=function(e,t){return t?e.replace(/\/+$/,"")+"/"+t.replace(/^\/+/,""):e}},function(e,t){"use strict";function n(e){this.message=e}n.prototype.toString=function(){return"Cancel"+(this.message?": "+this.message:"")},n.prototype.__CANCEL__=!0,e.exports=n},function(e,t,n){"use strict";function r(e){if("function"!=typeof e)throw new TypeError("executor must be a function.");var t;this.promise=new Promise(function(e){t=e});var n=this;e(function(e){n.reason||(n.reason=new o(e),t(n.reason))})}var o=n(23);r.prototype.throwIfRequested=function(){if(this.reason)throw this.reason},r.source=function(){var e,t=new r(function(t){e=t});return{token:t,cancel:e}},e.exports=r},function(e,t){"use strict";e.exports=function(e){return function(t){return e.apply(null,t)}}}])}); 9 | //# sourceMappingURL=axios.min.map -------------------------------------------------------------------------------- /resources/assets/vs/basic-languages/java/java.js: -------------------------------------------------------------------------------- 1 | /*!----------------------------------------------------------------------------- 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * monaco-languages version: 1.6.0(858705e74270e53559a241fdee187e7a6ae53b23) 4 | * Released under the MIT license 5 | * https://github.com/Microsoft/monaco-languages/blob/master/LICENSE.md 6 | *-----------------------------------------------------------------------------*/ 7 | define("vs/basic-languages/java/java",["require","exports"],function(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.conf={wordPattern:/(-?\d*\.\d\w*)|([^\`\~\!\#\%\^\&\*\(\)\-\=\+\[\{\]\}\\\|\;\:\'\"\,\.\<\>\/\?\s]+)/g,comments:{lineComment:"//",blockComment:["/*","*/"]},brackets:[["{","}"],["[","]"],["(",")"]],autoClosingPairs:[{open:"{",close:"}"},{open:"[",close:"]"},{open:"(",close:")"},{open:'"',close:'"'},{open:"'",close:"'"}],surroundingPairs:[{open:"{",close:"}"},{open:"[",close:"]"},{open:"(",close:")"},{open:'"',close:'"'},{open:"'",close:"'"},{open:"<",close:">"}],folding:{markers:{start:new RegExp("^\\s*//\\s*(?:(?:#?region\\b)|(?:))")}}},t.language={defaultToken:"",tokenPostfix:".java",keywords:["abstract","continue","for","new","switch","assert","default","goto","package","synchronized","boolean","do","if","private","this","break","double","implements","protected","throw","byte","else","import","public","throws","case","enum","instanceof","return","transient","catch","extends","int","short","try","char","final","interface","static","void","class","finally","long","strictfp","volatile","const","float","native","super","while","true","false"],operators:["=",">","<","!","~","?",":","==","<=",">=","!=","&&","||","++","--","+","-","*","/","&","|","^","%","<<",">>",">>>","+=","-=","*=","/=","&=","|=","^=","%=","<<=",">>=",">>>="],symbols:/[=>](?!@symbols)/,"@brackets"],[/@symbols/,{cases:{"@operators":"delimiter","@default":""}}],[/@\s*[a-zA-Z_\$][\w\$]*/,"annotation"],[/(@digits)[eE]([\-+]?(@digits))?[fFdD]?/,"number.float"],[/(@digits)\.(@digits)([eE][\-+]?(@digits))?[fFdD]?/,"number.float"],[/0[xX](@hexdigits)[Ll]?/,"number.hex"],[/0(@octaldigits)[Ll]?/,"number.octal"],[/0[bB](@binarydigits)[Ll]?/,"number.binary"],[/(@digits)[fFdD]/,"number.float"],[/(@digits)[lL]?/,"number"],[/[;,.]/,"delimiter"],[/"([^"\\]|\\.)*$/,"string.invalid"],[/"/,"string","@string"],[/'[^\\']'/,"string"],[/(')(@escapes)(')/,["string","string.escape","string"]],[/'/,"string.invalid"]],whitespace:[[/[ \t\r\n]+/,""],[/\/\*\*(?!\/)/,"comment.doc","@javadoc"],[/\/\*/,"comment","@comment"],[/\/\/.*$/,"comment"]],comment:[[/[^\/*]+/,"comment"],[/\*\//,"comment","@pop"],[/[\/*]/,"comment"]],javadoc:[[/[^\/*]+/,"comment.doc"],[/\/\*/,"comment.doc.invalid"],[/\*\//,"comment.doc","@pop"],[/[\/*]/,"comment.doc"]],string:[[/[^\\"]+/,"string"],[/@escapes/,"string.escape"],[/\\./,"string.escape.invalid"],[/"/,"string","@pop"]]}}}); -------------------------------------------------------------------------------- /resources/assets/vs/language/css/cssMode.js: -------------------------------------------------------------------------------- 1 | /*!----------------------------------------------------------------------------- 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * monaco-css version: 2.3.0(a450f6054cbfb890c2ede882f6d2e47cc0e47f2f) 4 | * Released under the MIT license 5 | * https://github.com/Microsoft/monaco-css/blob/master/LICENSE.md 6 | *-----------------------------------------------------------------------------*/ 7 | define("vs/language/css/workerManager",["require","exports"],function(e,n){"use strict";Object.defineProperty(n,"__esModule",{value:!0});var t=function(){function e(e){var n=this;this._defaults=e,this._worker=null,this._idleCheckInterval=setInterval(function(){return n._checkIfIdle()},3e4),this._lastUsedTime=0,this._configChangeListener=this._defaults.onDidChange(function(){return n._stopWorker()})}return e.prototype._stopWorker=function(){this._worker&&(this._worker.dispose(),this._worker=null),this._client=null},e.prototype.dispose=function(){clearInterval(this._idleCheckInterval),this._configChangeListener.dispose(),this._stopWorker()},e.prototype._checkIfIdle=function(){this._worker&&(12e4e?r=i:t=i+1}var o=t-1;return a.create(o,e-n[o])},e.prototype.offsetAt=function(e){var n=this.getLineOffsets();if(e.line>=n.length)return this._content.length;if(e.line<0)return 0;var t=n[e.line],r=e.line+1e?r=i:n=i+1}var o=n-1;return a.create(o,e-t[o])},e.prototype.offsetAt=function(e){var t=this.getLineOffsets();if(e.line>=t.length)return this._content.length;if(e.line<0)return 0;var n=t[e.line],r=e.line+1'Migrate', 10 | 'generator'=>'Generator', 11 | 'creator'=>'Creator', 12 | 'modelName'=>'Model Name', 13 | 'generateLog'=>'Generate Logs', 14 | 'displayName'=>'Display Name', 15 | 'modelDisplayNameDesc'=>'The actual name of the model, such as User', 16 | 'fieldName'=>'the field in table', 17 | 'displayNameDesc'=>'The name actually displayed such as goods name', 18 | 'youNeed'=>'if you need', 19 | 'soAttach'=>'attach can be 5,2', 20 | 'templates'=>'templates', 21 | 'optional'=>'optional', 22 | 'noData'=>'no data', 23 | 'showLists'=>'ShowList', 24 | 'showListsDesc'=>'Show this field in the table lists', 25 | 'canSearch'=>'Search', 26 | 'canSearchDesc'=>'this field can search in the table lists', 27 | 'templateName'=>'name', 28 | 'templatePath'=>'path', 29 | 'templateFileName'=>'file_name', 30 | 'templateIsChecked'=>'is_checked', 31 | 'group'=>'Group', 32 | 'yes'=>'yes', 33 | 'sure'=>'Sure', 34 | 'cancel'=>'Cancel', 35 | 'notice'=>'Notice', 36 | 'no'=>'no', 37 | 'delete'=>'delete', 38 | 'edit'=>'edit', 39 | 'copy'=>'Copy', 40 | 'defaultValue'=>'Default Value', 41 | 'notFound'=>'not found', 42 | 'actions'=>'actions', 43 | 'add'=>'Add', 44 | 'rule'=>'Rule', 45 | 'action'=>'Action', 46 | 'foreign'=>'Foreign', 47 | 'relationship'=>'Relationships', 48 | 'relationshipDesc'=>'Available relationship fields', 49 | 'error'=>'error', 50 | 'success'=>'success', 51 | 'deleteSuccess'=>'Successfully deleted', 52 | 'deleteFailed'=>'Failed to delete', 53 | 'confirmDelete'=>'Confirm to delete ?', 54 | 'modelNotDelete'=>'Model is not allowed to delete', 55 | 'name'=>'name', 56 | 'addTime'=>'Add Time', 57 | 'updateTime'=>'Update Time', 58 | 'currentTime'=>'Current Time', 59 | 'file'=>'file', 60 | 'submitError'=>'submit error', 61 | 'submitSuccess'=>'submit success', 62 | 'namespace'=>'namespace', 63 | 'template'=>'template', 64 | 'total'=>'Total', 65 | 'line'=>' ', 66 | 'tableInfo'=>'Table Info', 67 | 'tableFields'=>'Table Fields', 68 | 'required'=>'is required', 69 | 'ifNotExit'=>'can add if not exist ', 70 | 'hasExists'=>'already exists', 71 | 'submit'=>'Submit', 72 | 'save'=>'Save', 73 | 'saveGenerate'=>'Save&&Generate', 74 | 'classInfo'=>'Class Info', 75 | 'className'=>'Class', 76 | 'classDisplayName'=>'Display Name', 77 | 'camelClassName'=>'Camel Class', 78 | 'SnakeClassName'=>'Snake Class', 79 | 'PluralClassName'=>'Plural Class', 80 | 'SnakePluralClassName'=>'Snake Plural Class', 81 | 'classNameDesc'=>'will be replaced to the real class name', 82 | 'classDisplayNameDesc'=>'will be replaced to the actual name of the model, which is mean to the modelDisplayName', 83 | 'camelClassNameDesc'=>'will be replaced to camel_case such as laravelGenerator', 84 | 'SnakeClassNameDesc'=>'will be replaced to snake_case such as laravel_generator', 85 | 'PluralClassNameDesc'=>'will be replaced to its plural form,currently only supports the English language such as LaravelGenerators', 86 | 'SnakePluralClassNameDesc'=>'will be replaced to snake and then to its plural form such as laravel_generators', 87 | 'template_not_empty' => "The template cannot be empty", 88 | 'exist_table' => 'Table already exists', 89 | 'select' => 'Please select', 90 | ]; 91 | -------------------------------------------------------------------------------- /resources/lang/zh_CN/generator.php: -------------------------------------------------------------------------------- 1 | '迁移', 10 | 'generator'=>'生成器', 11 | 'creator'=>'创建人', 12 | 'modelName'=>'Model名称', 13 | 'generateLog'=>'生成记录', 14 | 'displayName'=>'显示名称', 15 | 'modelDisplayNameDesc'=>'模型的实际名称,如用户', 16 | 'fieldName'=>'表里的字段名', 17 | 'displayNameDesc'=>'列表中实际显示的名称,如商品名称', 18 | 'youNeed'=>'如果你需要', 19 | 'soAttach'=>'那么attach可以填入5,2', 20 | 'templates'=>'模板', 21 | 'optional'=>'可选的', 22 | 'noData'=>'没有数据', 23 | 'showLists'=>'列表显示', 24 | 'showListsDesc'=>'该字段在列表页显示', 25 | 'canSearch'=>'搜索', 26 | 'canSearchDesc'=>'该字段在列表页是可搜索的', 27 | 'templateName'=>'名称', 28 | 'templatePath'=>'路径', 29 | 'templateFileName'=>'文件名', 30 | 'templateIsChecked'=>'是否选中', 31 | 'group'=>'所属组', 32 | 'yes'=>'是', 33 | 'sure'=>'确定', 34 | 'cancel'=>'取消', 35 | 'notice'=>'提示', 36 | 'no'=>'否', 37 | 'delete'=>'删除', 38 | 'edit'=>'编辑', 39 | 'copy'=>'复制', 40 | 'notFound'=>'没有找到', 41 | 'defaultValue'=>'默认值', 42 | 'actions'=>'操作', 43 | 'add'=>'添加', 44 | 'rule'=>'规则', 45 | 'action'=>'操作', 46 | 'foreign'=>'外键', 47 | 'relationship'=>'关联关系', 48 | 'relationshipDesc'=>'可使用的关联关系字段', 49 | 'error'=>'错误', 50 | 'success'=>'成功', 51 | 'deleteSuccess'=>'删除成功', 52 | 'deleteFailed'=>'删除失败', 53 | 'confirmDelete'=>'确认删除吗?', 54 | 'modelNotDelete'=>'model 是不允许删除的', 55 | 'name'=>'名称', 56 | 'addTime'=>'添加时间', 57 | 'updateTime'=>'更新时间', 58 | 'currentTime'=>'当前时间', 59 | 'file'=>'文件', 60 | 'submitError'=>'提交失败', 61 | 'submitSuccess'=>'提交成功', 62 | 'namespace'=>'命名空间', 63 | 'template'=>'模板', 64 | 'total'=>'共', 65 | 'line'=>'条', 66 | 'tableInfo'=>'表信息', 67 | 'tableFields'=>'表字段', 68 | 'required'=>'是必须的', 69 | 'ifNotExit'=>'如果不存着可以添加 ', 70 | 'hasExists'=>'已经存在', 71 | 'submit'=>'提交', 72 | 'save'=>'保存', 73 | 'saveGenerate'=>'保存并生成', 74 | 'classInfo'=>'类信息', 75 | 'className'=>'类', 76 | 'classDisplayName'=>'显示名称', 77 | 'camelClassName'=>'类驼峰', 78 | 'SnakeClassName'=>'类小写', 79 | 'PluralClassName'=>'类复数', 80 | 'SnakePluralClassName'=>'类小写复数', 81 | 'classNameDesc'=>'将被替换为类的实际名称', 82 | 'classDisplayNameDesc'=>'将被替换为类的实际名称,对应填入的modelDisplayName的值', 83 | 'camelClassNameDesc'=>'将被替换为类的驼峰形式 如laravelGenerator', 84 | 'SnakeClassNameDesc'=>'将被替换为类的小写形式 如laravel_generator', 85 | 'PluralClassNameDesc'=>'将被替换为类的复数形式 如LaravelGenerators', 86 | 'SnakePluralClassNameDesc'=>'将被替换为类的小写复数形式 如laravel_generators', 87 | 'template_not_empty' => "模板不能为空", 88 | 'exist_table'=>'已存在表', 89 | 'select'=>'请选择', 90 | ]; 91 | -------------------------------------------------------------------------------- /resources/migrations/2025_04_11_095115_create_laravel_generators_table.php: -------------------------------------------------------------------------------- 1 | increments('id'); 16 | $table->string('name')->unique()->comment('模板组'); 17 | $table->timestamps(); 18 | }); 19 | 20 | Schema::create('laravel_generators', function (Blueprint $table) { 21 | $table->increments('id'); 22 | $table->string('name')->comment('名称'); 23 | $table->string('path')->comment('保存路径'); 24 | $table->string('file_name')->comment('文件名'); 25 | $table->char('is_checked', 1)->comment('是否选中 0 不选中 1 选中'); 26 | $table->text('template')->coment('模板'); 27 | $table->unsignedInteger('template_id'); 28 | $table->foreign('template_id')->references('id')->on('laravel_generator_types'); 29 | 30 | $table->timestamps(); 31 | }); 32 | 33 | Schema::create('laravel_generator_logs', function (Blueprint $table) { 34 | $table->increments('id'); 35 | $table->string('model_name')->comment('model name'); 36 | $table->string('display_name')->comment('display name'); 37 | $table->string('creator')->comment('creator'); 38 | $table->json('configs')->comment('configs'); 39 | $table->timestamps(); 40 | }); 41 | 42 | Schema::create('laravel_generator_configs', function (Blueprint $table) { 43 | $table->increments('id'); 44 | $table->string('group')->comment('group'); 45 | $table->string('alias')->unique()->comment('alias'); 46 | $table->text('config')->comment('config'); 47 | $table->timestamps(); 48 | }); 49 | } 50 | 51 | /** 52 | * Reverse the migrations. 53 | */ 54 | public function down() 55 | { 56 | Schema::dropIfExists('laravel_generators'); 57 | Schema::dropIfExists('laravel_generator_types'); 58 | Schema::dropIfExists('laravel_generator_logs'); 59 | Schema::dropIfExists('laravel_generator_configs'); 60 | } 61 | }; 62 | -------------------------------------------------------------------------------- /resources/views/generator.blade.php: -------------------------------------------------------------------------------- 1 | 2 | @lang('laravel-generator::generator.generator') 3 | 4 | 5 | 6 | 7 | 8 | 9 |

@lang('laravel-generator::generator.modelDisplayNameDesc')

10 | 11 |
12 | 13 |
14 | 15 | 16 | Create migration 17 | Run migrate 18 | ide-helper:models 19 | 20 | 21 | {{-- 模板的数据 --}} 22 | 23 | 24 | 25 | 26 | 27 | {{-- 表字段/start --}} 28 | 29 | 30 | 31 | 32 |

@lang('laravel-generator::generator.fieldName')

33 | Field name 34 |
35 |
36 | 37 | 38 |

@lang('laravel-generator::generator.displayNameDesc')

39 | @lang('laravel-generator::generator.displayName') 40 |
41 |
42 | Type 43 | 44 | 45 |

@lang('laravel-generator::generator.youNeed')$table->decimal('amount', 5, 2),@lang('laravel-generator::generator.soAttach')

46 | attach 47 |
48 |
49 | Nullable 50 | Key 51 | Default Value 52 | Comment 53 | 54 | 55 |

@lang('laravel-generator::generator.showListsDesc')

56 | @lang('laravel-generator::generator.showLists') 57 |
58 |
59 | 60 | 61 |

@lang('laravel-generator::generator.canSearchDesc')

62 | @lang('laravel-generator::generator.canSearch') 63 |
64 |
65 | @lang('laravel-generator::generator.rule') 66 | @lang('laravel-generator::generator.action') 67 |
68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 118 | 123 | 124 | 125 | 126 | 127 | 128 |
129 | 130 | Add field 131 | 135 | 136 | 140 | 141 | 142 | {{-- 表字段/end --}} 143 | 144 | {{-- 添加外键关系/start --}} 145 | 146 | @lang('laravel-generator::generator.add') @lang('laravel-generator::generator.foreign') 147 | 148 | 149 | 150 | 151 | foreign 152 | 153 | 154 | references 155 | 156 | on 157 | 158 | onDelete 159 | 160 | 161 | onUpdate 162 | 163 | Action 164 | 165 | 166 | 167 | 169 | 176 | 177 | 178 | 179 | 180 | 182 | 187 | 188 | 189 | 190 | 191 | 194 | 199 | 200 | 201 | 202 | 203 | 204 | 209 | 210 | 211 | 212 | 213 | 214 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | {{-- 添加外键关系/end --}} 226 | 227 | {{-- 添加关联关系/start --}} 228 | 229 | @lang('laravel-generator::generator.add') @lang('laravel-generator::generator.relationship') 230 | 231 | 232 | 233 | relationship 234 | 235 | RelationModel 236 | 237 | 238 | foreign_key 239 | 240 | 241 | Reverse 242 | 243 | 244 | With 245 | 246 | 247 | Search 248 | 249 | Action 250 | 251 | 252 | 253 | 255 | 260 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | 270 | 273 | 278 | 279 | 280 | 281 | 282 | with 283 | 284 | 285 | search 286 | 287 | 288 | 289 | @{{ relationship.relation }} 290 | @{{ relationship.reverseRelation }} 291 | 292 | 293 | 294 | {{-- 添加关联关系/end --}} 295 | 296 | 297 | @lang('laravel-generator::generator.save') 298 | @lang('laravel-generator::generator.saveGenerate') 299 | 300 | 301 |
302 |
303 | -------------------------------------------------------------------------------- /resources/views/generator_logs.blade.php: -------------------------------------------------------------------------------- 1 | 2 | @lang('laravel-generator::generator.generateLog') 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | {{ trans('laravel-generator::generator.add') }} 19 | {{ trans('laravel-generator::generator.exist_table') }} 20 | 30 | 35 | 36 | 37 | 38 | 39 | 44 | 48 | 49 | 52 | 55 | 56 | 60 | 61 | 64 | 65 | 68 | 69 | 72 | 73 | 77 | 89 | 90 | 91 | 92 |
93 | @lang('laravel-generator::generator.total') @{{ pageInfo.total }} @lang('laravel-generator::generator.line') 94 | 100 | 101 |
102 |
103 | 104 |

105 |

106 | @lang('laravel-generator::generator.modelName'): 107 | @{{ logRow.modelName }} 108 | @lang('laravel-generator::generator.displayName'): @{{ logRow.modelDisplayName }} 109 |

110 |

111 |

112 | 113 | Create migration 114 | Run migrate 115 | ide-helper:models 116 | 117 |

118 |

119 |

Table fileds

120 |

121 | 122 | 123 | 124 | 125 | 126 | 127 | 130 | 131 | 132 | 133 | 134 | 135 | 138 | 139 | 140 | 143 | 144 | 145 | 146 |

147 | 151 | 152 | 156 | 157 | 158 |

159 | {{--

--}} 160 | {{--

@lang('laravel-generator::generator.foreign')

--}} 161 | {{--

--}} 162 |
163 | -------------------------------------------------------------------------------- /resources/views/generator_migrate.blade.php: -------------------------------------------------------------------------------- 1 | 2 | @lang('laravel-generator::generator.migrate') 3 | 4 | 5 | 6 | 7 | 8 | @{{migrateName}} 9 | 10 | 11 | 12 | Run migrate 13 | 14 | 15 | 16 | 17 | Field name 18 | 19 | Type 20 | 21 | 22 |

@lang('laravel-generator::generator.youNeed')$table->decimal('amount', 5, 2),@lang('laravel-generator::generator.soAttach')

23 | attach 24 |
25 |
26 | Nullable 27 | Key 28 | Default value 29 | Comment 30 | Change 31 | Action 32 |
33 | 34 | 35 | 36 | 37 | 38 | 39 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 |
77 | 78 | Add field 79 | 80 | 81 | 82 | submit 83 | 84 |
85 |
86 | -------------------------------------------------------------------------------- /resources/views/layout.blade.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | {{config('laravel-generator.name','Laravel Generator')}} 11 | 12 | 13 | 14 | 55 | 56 | 57 |
58 |
59 | 60 | 61 |
62 | 63 |

{{config('laravel-generator.name','Laravel Generator')}}

64 |
65 |
66 | 67 | GitHub 69 | 70 | 72 | 73 | 74 | 75 | 76 | @{{ language_value }} 77 | 78 | 79 | @{{ item.label }} 80 | 81 | 82 | 83 |
84 |
85 | 86 | @yield('content') 87 | 88 | ©{{config('laravel-generator.name','Laravel Generator')}} 89 |
90 |
91 |
92 | 93 | 94 | 95 | 96 | 148 | @yield('js') 149 | @yield('css') 150 | 151 | 152 | -------------------------------------------------------------------------------- /resources/views/template_lists.blade.php: -------------------------------------------------------------------------------- 1 | 2 | {{ trans('laravel-generator::generator.templates') }} 3 | 4 | 5 | 6 | 7 | 11 | 16 | 17 | 18 | 19 | 20 | {{ trans('laravel-generator::generator.add') }} 21 | 22 | 23 | 24 | 28 | 32 | 33 | 37 | 38 | 41 | 42 | 45 | 50 | 51 | 54 | 55 | 59 | 65 | 66 | 67 | 68 | 69 | -------------------------------------------------------------------------------- /routes/route.php: -------------------------------------------------------------------------------- 1 | group(function () { 9 | Route::get('{locale?}', [GeneratorController::class, 'index'])->name('generator.index')->where('locale', 'en|zh_CN'); 10 | Route::get('model/{name?}', [GeneratorController::class, 'dummyValues'])->name('generator.dummyValues'); 11 | Route::get('table/{table_name?}', [GeneratorController::class, 'createByTable'])->name('generator.create_by_table'); 12 | Route::post('/', [GeneratorController::class, 'store'])->name('generator.store'); 13 | Route::post('migrate', [GeneratorController::class, 'migrate'])->name('generator.migrate'); 14 | Route::get('template', [GeneratorTemplateController::class, 'index'])->name('generator.template.index'); 15 | Route::get('template/update/{locale?}', [GeneratorTemplateController::class, 'update'])->name('generator.template.update'); 16 | Route::post('template/save', [GeneratorTemplateController::class, 'save'])->name('generator.template.save'); 17 | Route::post('template/delete', [GeneratorTemplateController::class, 'delete'])->name('generator.template.delete'); 18 | Route::post('template/compile', [GeneratorTemplateController::class, 'compile'])->name('generator.template.compile'); 19 | Route::post('template/updateType', [GeneratorTemplateController::class, 'updateType'])->name('generator.template.updateType'); 20 | Route::get('logs', [GeneratorController::class, 'getLogs'])->name('generator.logs'); 21 | Route::post('log/delete', [GeneratorController::class, 'deleteLog'])->name('generator.deleteLog'); 22 | Route::get('assets/{path}', AssetController::class)->where('path', '.*'); 23 | }); 24 | 25 | -------------------------------------------------------------------------------- /src/Console/InstallCommand.php: -------------------------------------------------------------------------------- 1 | call('migrate'); 33 | //add default seeds 34 | $this->call('db:seed', ['--class' => GeneratorSeeder::class]); 35 | $this->info('Generator installed successfully.'); 36 | $host = config('app.url'); 37 | $this->info('🌐 access url: ' . route('generator.index')); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/Controllers/AssetController.php: -------------------------------------------------------------------------------- 1 | 'text/css', 27 | 'js' => 'application/javascript', 28 | 'woff2' => 'font/woff2', 29 | 'woff' => 'font/woff', 30 | 'ttf' => 'font/ttf', 31 | 'svg' => 'image/svg+xml', 32 | 'png' => 'image/png', 33 | 'jpg' => 'image/jpeg', 34 | 'jpeg' => 'image/jpeg', 35 | 'gif' => 'image/gif', 36 | ]; 37 | 38 | $mime = $mimeTypes[$extension] ?? 'application/octet-stream'; 39 | 40 | return Response::make(File::get($fullPath), 200, [ 41 | 'Content-Type' => $mime, 42 | ]); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/Controllers/GeneratorController.php: -------------------------------------------------------------------------------- 1 | get('tab', 'log'); 45 | // 获取所有的表 46 | $tables = GeneratorUtils::getTables(); 47 | // 获取可用的数据类型 48 | $dbTypes = GeneratorUtils::getDbTypes(); 49 | // 获取模板列表 50 | $template_types = $this->getTemplateTypes(); 51 | // 获取模型的信息 52 | $modelInfo = $this->getModelInfo(); 53 | // 获取可用的规则 54 | $rules = $this->getRules(); 55 | // 可用的假属性字段 56 | $dummyAttrs = GeneratorUtils::getDummyAttrs(); 57 | // 自定义变量 58 | $customDummys = config('laravel-generator.customDummys'); 59 | $language_value = $locale === 'en' ? 'English' : '简体中文'; 60 | 61 | return view('laravel-generator::index', compact('dbTypes', 'generator', 'language_value', 'locale', 62 | 'tab', 'template_types', 'dummyAttrs', 'tables', 'rules', 'modelInfo', 'customDummys')); 63 | } 64 | 65 | /** 66 | * 获取指定的名称的转换数据. 67 | * 68 | * 69 | * @return \Illuminate\Http\JsonResponse 70 | */ 71 | public function dummyValues($name) 72 | { 73 | if ($name) { 74 | return $this->success(GeneratorUtils::getDummyValues($name)); 75 | } 76 | 77 | return $this->error(trans('generator.error')); 78 | } 79 | 80 | /** 81 | * save data. 82 | * 83 | * 84 | * @return \Illuminate\Http\JsonResponse 85 | */ 86 | public function store(Request $request) 87 | { 88 | $paths = []; 89 | $data = $request->validate([ 90 | 'id' => 'required', 91 | 'modelName' => 'required', 92 | 'primary_key' => 'required', 93 | 'soft_deletes' => 'required', 94 | 'timestamps' => 'required', 95 | 'modelDisplayName' => 'required', 96 | 'submit_type' => 'required', 97 | 'relationships' => 'array', 98 | 'table_fields' => 'array', 99 | 'generator_templates' => 'array', 100 | ]); 101 | $model_name = $data['modelName']; 102 | $log = LaravelGeneratorLog::firstOrNew([ 103 | 'model_name' => $model_name, 104 | ]); 105 | $item['model_name'] = $model_name; 106 | $item['display_name'] = $data['modelDisplayName']; 107 | $item['creator'] = config('laravel-generator.custom_keys.author', ''); 108 | $item['configs'] = json_encode($request->except('id')); 109 | $log->fill($item); 110 | $res = $log->save(); 111 | if ($data['submit_type'] === 'save') { 112 | if ($res) { 113 | return $this->success(['save success']); 114 | } 115 | 116 | return $this->error('save error'); 117 | } 118 | // 获取模型的信息 119 | $modelInfo = $this->getModelInfo(); 120 | try { 121 | $table_fields = $request->get('table_fields'); 122 | // 生成数据 123 | $model_name = $data['modelName']; 124 | 125 | $create = $request->get('create', []); 126 | // 1. 是否运行 Create migration. 127 | if (\in_array('migration', $create, true)) { 128 | $table_name = Str::plural(Str::snake(class_basename($model_name))); 129 | $migrationName = 'create_'.$table_name.'_table'; 130 | 131 | $paths['migration'] = (new MigrationCreator(app('files'), database_path('migrations')))->buildBluePrint( 132 | $table_fields, 133 | 'id', 134 | $request->get('timestamps'), 135 | $request->get('soft_deletes'), 136 | $request->get('foreigns') 137 | )->create($migrationName, database_path('migrations'), $table_name); 138 | } 139 | // 2. 是否运行Run migrate. 140 | if (\in_array('migrate', $create, true)) { 141 | Artisan::call('migrate'); 142 | $message = Artisan::output(); 143 | $paths['migrate'] = $message; 144 | } 145 | // 4.生成模板文件 146 | $generator_templates = $data['generator_templates']; 147 | $file = app('files'); 148 | 149 | foreach ($generator_templates as $k => $template) { 150 | $file_real_name = $template['file_real_name']; 151 | $content = GeneratorUtils::compile($template['template'],$data); 152 | $path = base_path($file_real_name); 153 | if ($file->exists($path)) { 154 | // route special handling 155 | if (str_contains($file_real_name, 'routes/') && str_contains($file_real_name, '.php')) { 156 | $file->append($path, str_replace('create(); 163 | } 164 | } 165 | // 5.处理关联关系 166 | $relationships = $request->get('relationships'); 167 | $this->dealRelationShips($relationships, $model_name, $modelInfo); 168 | // 6.是否运行idea代码提示 169 | if (\in_array('ide-helper', $create, true)) { 170 | Artisan::call('ide-helper:models', [ 171 | '--write' => true, 172 | '--write-eloquent-helper' => true, 173 | 'model' => [ 174 | ucfirst(str_replace('/', '\\', $modelInfo->path).$model_name), 175 | ], 176 | ]); 177 | } 178 | } catch (\Exception $exception) { 179 | return $this->error($exception->getFile().'-'.$exception->getLine().':'.$exception->getMessage()); 180 | } 181 | 182 | return $this->success($paths); 183 | } 184 | 185 | public function migrate(Request $request) 186 | { 187 | $doMigrate = $request->get('doMigrate', []); 188 | $table_fields = $request->get('table_fields'); 189 | try { 190 | // 新增加迁移文件 191 | if (\in_array('migration', $doMigrate, true)) { 192 | $tableName = $request->get('tableName'); 193 | $migrationName = $request->get('prefix').'_'; 194 | if (count($table_fields) > 2) { 195 | $migrationName .= $table_fields[0]['field_name'].'AndMore'; 196 | } else { 197 | $migrationName .= collect($table_fields)->pluck('field_name')->implode('_'); 198 | } 199 | $migrationName .= '_'.$tableName.'_table'; 200 | $paths['migration'] = (new MigrationCreator(app('files'), database_path('migrations')))->buildBluePrint($table_fields, null, false) 201 | ->create($migrationName, database_path('migrations'), $tableName, false); 202 | // Run migrate. 203 | if (\in_array('migrate', $request->get('doMigrate'), true)) { 204 | Artisan::call('migrate'); 205 | $message = Artisan::output(); 206 | $paths['migrate'] = $message; 207 | } 208 | } 209 | } catch (\Exception $exception) { 210 | return $this->error($exception->getFile().'-'.$exception->getLine().':'.$exception->getMessage()); 211 | } 212 | 213 | return $this->success($paths); 214 | } 215 | private function dealRelationShips($relationships, $model_name, $modelInfo) 216 | { 217 | if ($relationships) { 218 | foreach ($relationships as $relationship) { 219 | // 替换相对模型的数据 220 | $file_name = base_path($modelInfo->path).$relationship['model'].'.php'; 221 | if ($relationship['reverse'] && file_exists($file_name)) { 222 | $oldData = file_get_contents($file_name); 223 | $oldData = str_replace(["\n\n\n", "\n \n"], ["\n\n", ''], substr($oldData, 0, -1)); 224 | $oldData = substr($oldData, 0, -1); 225 | if ($relationship['reverse'] === 'hasMany') { 226 | $funName = Str::snake(Str::plural($model_name)); 227 | } else { 228 | $funName = Str::camel($model_name); 229 | } 230 | $key = ''; 231 | if ($relationship['foreign_key']) { 232 | $key = ",'{$relationship['foreign_key']}'"; 233 | } 234 | $oldData .= " public function {$funName}(){\n"; 235 | $oldData .= " return \$this->{$relationship['reverse']}({$model_name}::class{$key});\n"; 236 | $oldData .= " }\n\n}"; 237 | file_put_contents($file_name, $oldData); 238 | } 239 | } 240 | } 241 | 242 | return true; 243 | } 244 | 245 | /** 246 | * 获取可用的规则. 247 | * 248 | * @return array 249 | */ 250 | private function getRules() 251 | { 252 | $rules = []; 253 | $configRules = config('laravel-generator.rules'); 254 | foreach ($configRules as $k => $rule) { 255 | $rules[$k]['label'] = $rule; 256 | $rules[$k]['value'] = $rule; 257 | } 258 | 259 | return $rules; 260 | } 261 | 262 | /** 263 | * 获取模型的信息. 264 | * 265 | * @return mixed 266 | */ 267 | private function getModelInfo() 268 | { 269 | $model = LaravelGenerator::whereHas('template_type', function ($query) { 270 | $query->whereName(LaravelGeneratorType::MODEL); 271 | })->first(); 272 | if (! $model) { 273 | throw new \RuntimeException('the template model not found'); 274 | } 275 | 276 | return $model; 277 | } 278 | 279 | /** 280 | * get the all template types. 281 | * 282 | * @return LaravelGeneratorType[]|\Illuminate\Database\Eloquent\Collection|\Illuminate\Support\Collection 283 | */ 284 | private function getTemplateTypes() 285 | { 286 | $data = LaravelGeneratorType::with('templates')->get(); 287 | $select = $data->map(function ($item) { 288 | $data = []; 289 | $data['label'] = $item->name; 290 | $data['value'] = $item->id; 291 | 292 | return $data; 293 | }); 294 | $data = $data->map(function ($item) { 295 | $item->checked = $item->templates->filter(function ($value) { 296 | return $value['is_checked']; 297 | })->pluck('id'); 298 | $item->templates = $item->templates->map(function ($temp) { 299 | $temp->file_real_name = $temp->path.$temp->file_name; 300 | 301 | return $temp; 302 | }); 303 | 304 | return $item; 305 | }); 306 | 307 | return [ 308 | 'datas' => $data, 309 | 'select' => $select, 310 | ]; 311 | } 312 | 313 | public function getLogs(Request $request) 314 | { 315 | $model_name = $request->get('model_name'); 316 | $display_name = $request->get('display_name'); 317 | $creator = $request->get('creator'); 318 | 319 | $datas = LaravelGeneratorLog::when($model_name, fn ($query) => $query->where('model_name', 'like', '%'.$model_name.'%')) 320 | ->when($display_name, fn ($query) => $query->where('display_name', 'like', '%'.$display_name.'%')) 321 | ->when($creator, fn ($query) => $query->where('creator', 'like', '%'.$creator.'%')) 322 | ->orderBy('id', 'desc') 323 | ->paginate(); 324 | 325 | return $this->success($datas); 326 | } 327 | 328 | public function deleteLog(Request $request) 329 | { 330 | $id = $request->get('id'); 331 | 332 | $res = LaravelGeneratorLog::whereId($id)->delete(); 333 | 334 | if ($res) { 335 | return $this->success('success'); 336 | } 337 | 338 | return $this->error('delete error'); 339 | } 340 | 341 | public function createByTable(string $table_name) 342 | { 343 | if (!$table_name) { 344 | return $this->error('table_name is required'); 345 | } 346 | try { 347 | $table_columns = GeneratorUtils::tableToForm($table_name); 348 | 349 | return $this->success($table_columns); 350 | 351 | }catch (Exception $exception){ 352 | return $this->error($exception->getMessage()); 353 | } 354 | } 355 | } 356 | -------------------------------------------------------------------------------- /src/Controllers/GeneratorTemplateController.php: -------------------------------------------------------------------------------- 1 | get('name'); 39 | $template_id = $request->get('template_id'); 40 | $query = LaravelGenerator::with('template_type'); 41 | if ($name) { 42 | $query = $query->where('name', 'like', '%'.$name.'%'); 43 | } 44 | if ($template_id) { 45 | $query = $query->where('template_id', $template_id); 46 | } 47 | 48 | return $this->success($query->get()); 49 | } 50 | 51 | /** 52 | * 更新操作. 53 | * 54 | * 55 | * @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View 56 | */ 57 | public function update(Request $request, ?string $locale = null) 58 | { 59 | $locale = $locale ?? config('app.locale', 'en'); 60 | if (! in_array($locale, ['en', 'zh_CN'])) { 61 | $locale = 'en'; 62 | } 63 | App::setLocale($locale); 64 | // do ajax request 65 | $id = (int) $request->get('id'); 66 | // 表单数据 67 | $form = $this->getForm($id); 68 | // 获取模板组列表 69 | $template_types = $this->getTemplateTypes(); 70 | // 提供的演示数据 71 | $laravel_generators = GeneratorUtils::getGenerators(); 72 | // 可用的假属性字段 73 | $dummyAttrs = GeneratorUtils::getDummyAttrs(); 74 | // 可用的函数 75 | $functions = GeneratorUtils::getFunctions(); 76 | // 自定义变量 77 | $customKeys = GeneratorUtils::getCustomKeys(); 78 | $tags = GeneratorUtils::getTags(); 79 | $language_value = $locale === 'en' ? 'English' : '简体中文'; 80 | 81 | return view('laravel-generator::template_update', compact('template_types', 'tags', 'locale', 'language_value', 82 | 'laravel_generators', 'dummyAttrs', 'functions', 'form', 'customKeys')); 83 | } 84 | 85 | /** 86 | * 删除操作. 87 | * 88 | * 89 | * @return \Illuminate\Http\JsonResponse 90 | */ 91 | public function delete(Request $request) 92 | { 93 | $id = (int) $request->get('id'); 94 | $generator = LaravelGenerator::with('template_type')->find($id); 95 | if ($generator) { 96 | if ($generator->template_type->name == LaravelGeneratorType::MODEL) { 97 | return $this->error(trans('laravel-generator::generator.modelNotDelete')); 98 | } 99 | if ($generator->delete()) { 100 | $count = LaravelGenerator::where('template_id', $generator->template_id)->count(); 101 | // 102 | if ($count == 0) { 103 | LaravelGeneratorType::whereId($generator->template_id)->delete(); 104 | } 105 | 106 | return $this->success(trans('laravel-generator::generator.deleteSuccess')); 107 | } 108 | } 109 | 110 | return $this->error(trans('laravel-generator::generator.deleteFailed')); 111 | } 112 | 113 | public function updateType(Request $request) 114 | { 115 | $name = $request->get('name'); 116 | $id = $request->get('id'); 117 | if ($id > 0) { 118 | $generator_type = LaravelGeneratorType::whereId($id)->first(); 119 | } else { 120 | $generator_type = new LaravelGeneratorType; 121 | } 122 | $generator_type->name = $name; 123 | $generator_type->save(); 124 | 125 | return $this->success($generator_type->toArray()); 126 | } 127 | 128 | /** 129 | * 保存数据. 130 | * 131 | * 132 | * @return \Illuminate\Http\JsonResponse 133 | */ 134 | public function save(Request $request) 135 | { 136 | $data = $request->validate([ 137 | 'id' => 'required|int', 138 | 'name' => 'required', 139 | 'template_id' => [ 140 | 'required', 141 | 'integer', 142 | Rule::exists('laravel_generator_types', 'id'), 143 | ], 144 | 'is_checked' => 'required|boolean', 145 | 'path' => 'required', 146 | 'file_name' => 'required', 147 | 'template' => 'required', 148 | ]); 149 | if (! $data['id']) { 150 | $generator = new LaravelGenerator; 151 | } else { 152 | $generator = LaravelGenerator::findOrFail($data['id']); 153 | } 154 | $generator->fill($data); 155 | 156 | if ($generator->save()) { 157 | return $this->success(trans('laravel-generator::generator.submitSuccess')); 158 | } 159 | 160 | return $this->error(trans('laravel-generator::generator.submitError')); 161 | } 162 | 163 | /** 164 | * 获取模板列表. 165 | * 166 | * @return LaravelGeneratorType[]|\Illuminate\Database\Eloquent\Collection|\Illuminate\Support\Collection 167 | */ 168 | private function getTemplateTypes() 169 | { 170 | return LaravelGeneratorType::all()->map(function ($item) { 171 | $data = []; 172 | $data['label'] = $item->name; 173 | $data['value'] = $item->id; 174 | 175 | return $data; 176 | }); 177 | } 178 | 179 | /** 180 | * 获取模板数据. 181 | * 182 | * 183 | * @return array 184 | */ 185 | private function getForm($id) 186 | { 187 | $form = [ 188 | 'id' => 0, 189 | 'is_checked' => true, 190 | 'template_id' => '', 191 | 'template' => '', 192 | 'path' => '', 193 | 'file_name' => '', 194 | ]; 195 | 196 | $generator = LaravelGenerator::find($id); 197 | if ($generator) { 198 | $form = $generator->toArray(); 199 | } 200 | 201 | return $form; 202 | } 203 | 204 | public function compile(Request $request) 205 | { 206 | $template = $request->get('template'); 207 | if(!$template){ 208 | return $this->success(['template'=>'']); 209 | } 210 | try { 211 | $result = GeneratorUtils::demo_compile($template); 212 | return $this->success(['template'=>$result]); 213 | }catch (\Exception $exception){ 214 | return $this->error($exception->getMessage()); 215 | } 216 | } 217 | } 218 | -------------------------------------------------------------------------------- /src/Database/GeneratorSeeder.php: -------------------------------------------------------------------------------- 1 | addModel(); 24 | // 添加控制器 25 | $this->addControllers(); 26 | // 添加视图 27 | $this->addViews(); 28 | // 添加路由 29 | $this->addRoute(); 30 | // add logs 31 | $this->addLogs(); 32 | } 33 | 34 | private function addLogs() 35 | { 36 | $count = LaravelGeneratorLog::count(); 37 | if ($count === 0) { 38 | $generators = GeneratorUtils::getGenerators(); 39 | LaravelGeneratorLog::create([ 40 | 'model_name' => 'User', 41 | 'display_name' => 'User List', 42 | 'creator' => 'system', 43 | 'configs' => json_encode([ 44 | 'modelName' => 'User', 45 | 'modelDisplayName' => 'User List', 46 | 'foreigns' => [], 47 | 'relationships' => [], 48 | 'templates' => [], 49 | 'create' => [ 50 | 'migration', 'migrate', 'ide-helper', 51 | ], 52 | 'primary_key' => 'id', 53 | 'timestamps' => true, 54 | 'soft_deletes' => false, 55 | 'table_fields' => $generators['tableFields'], 56 | ]), 57 | ]); 58 | } 59 | } 60 | 61 | private function addRoute() 62 | { 63 | $type = LaravelGeneratorType::firstOrCreate([ 64 | 'name' => LaravelGeneratorType::Route, 65 | ]); 66 | $generator = LaravelGenerator::firstOrNew([ 67 | 'name' => 'route', 68 | ]); 69 | if (! $generator->exists) { 70 | $generator->path = 'routes/'; 71 | $generator->file_name = 'admin.php'; 72 | $generator->is_checked = 1; 73 | $generator->template = $this->getRouteTemplate(); 74 | $generator->template_id = $type->id; 75 | $generator->save(); 76 | } 77 | } 78 | 79 | private function getRouteTemplate() 80 | { 81 | return <<<'stub' 82 | name('admin.DummySnakeClass.index'); 84 | Route::post('DummySnakeClass/update',[DummyClassController::class,'update'])->name('admin.DummySnakeClass.update'); 85 | Route::post('DummySnakeClass/delete',[DummyClassController::class,'delete'])->name('admin.DummySnakeClass.delete'); 86 | stub; 87 | 88 | } 89 | 90 | /** 91 | * add Model. 92 | */ 93 | private function addModel() 94 | { 95 | $type = LaravelGeneratorType::firstOrCreate([ 96 | 'name' => LaravelGeneratorType::MODEL, 97 | ]); 98 | $generator = LaravelGenerator::firstOrNew([ 99 | 'name' => 'model', 100 | ]); 101 | if (! $generator->exists) { 102 | $generator->path = 'app/Models'; 103 | $generator->file_name = 'DummyClass.php'; 104 | $generator->is_checked = 1; 105 | $generator->template = $this->getModelTemplate(); 106 | $generator->template_id = $type->id; 107 | $generator->save(); 108 | } 109 | } 110 | 111 | /** 112 | * add Controllers. 113 | */ 114 | private function addControllers() 115 | { 116 | $type = LaravelGeneratorType::firstOrCreate([ 117 | 'name' => LaravelGeneratorType::Controllers, 118 | ]); 119 | $generator = LaravelGenerator::firstOrNew([ 120 | 'name' => 'Admin Controller', 121 | ]); 122 | $controllerTemps = $this->getControllersTemplate(); 123 | if (! $generator->exists) { 124 | $generator->path = 'app/Http/Controllers/Admin/'; 125 | $generator->file_name = 'DummyClassController.php'; 126 | $generator->is_checked = 1; 127 | $generator->template = $controllerTemps['admin']; 128 | $generator->template_id = $type->id; 129 | $generator->save(); 130 | } 131 | } 132 | 133 | /** 134 | * add Views. 135 | */ 136 | private function addViews() 137 | { 138 | $type = LaravelGeneratorType::firstOrCreate([ 139 | 'name' => LaravelGeneratorType::Views, 140 | ]); 141 | 142 | $generator = LaravelGenerator::firstOrNew([ 143 | 'name' => 'index_view', 144 | ]); 145 | $viewTemp = $this->getViewsTemplate(); 146 | if (! $generator->exists) { 147 | $generator->path = 'resources/views/admin/DummySnakeClass/'; 148 | $generator->file_name = 'index.vue'; 149 | $generator->is_checked = 1; 150 | $generator->template = $viewTemp['index']; 151 | $generator->template_id = $type->id; 152 | $generator->save(); 153 | } 154 | $generator = LaravelGenerator::firstOrNew([ 155 | 'name' => 'update_view', 156 | ]); 157 | if (! $generator->exists) { 158 | $generator->path = 'resources/views/admin/DummySnakeClass/'; 159 | $generator->file_name = 'update.vue'; 160 | $generator->is_checked = 1; 161 | $generator->template = $viewTemp['update']; 162 | $generator->template_id = $type->id; 163 | $generator->save(); 164 | } 165 | } 166 | 167 | /** 168 | * @return array 169 | */ 170 | private function getControllersTemplate() 171 | { 172 | $homeTemp = <<get('create_start_time'); 194 | \$create_end_time = \$request->get('create_end_time'); 195 | @foreach(\$tableFields as \$field) 196 | @if(\$field['can_search']) 197 | \${{\$field['field_name'] }} = \$request->get('{{\$field['field_name'] }}'); 198 | @endif 199 | @endforeach 200 | \$data = DummyClass::orderByDesc('id') 201 | @foreach(\$tableFields as \$field) 202 | @if(\$field['can_search']) 203 | @if('numeric'==\$field['rule']) 204 | ->when(\${{\$field['field_name'] }}, fn (Builder \$query) => \$query->where('{{\$field['field_name'] }}', \${{\$field['field_name'] }})) 205 | @elseif('string'==\$field['rule']) 206 | ->when(\${{\$field['field_name'] }}, fn (Builder \$query) => \$query->where('{{\$field['field_name'] }}', 'like', "%\${{\$field['field_name'] }}%")) 207 | @else 208 | ->when(\${{\$field['field_name'] }}, fn (Builder \$query) => \$query->where('{{\$field['field_name'] }}', 'like', "%\${{\$field['field_name'] }}%")) 209 | @endif 210 | @endif 211 | @endforeach 212 | ->when(\$create_start_time, fn (Builder \$query) => \$query->where('created_at', '>=', \$create_start_time)) 213 | ->when(\$create_end_time, fn (Builder \$query) => \$query->where('created_at', '<=', \$create_end_time)) 214 | ->paginate(); 215 | 216 | \$data->getCollection()->transform(function (DummyClass \$DummySnakeClass){ 217 | //\$DummySnakeClass->setAttribute('id', 'ID'); 218 | 219 | return \$DummySnakeClass; 220 | }); 221 | 222 | return response()->json(['message' => 'success', 'errcode' => 0, 'data' => \$data->toArray()]); 223 | } 224 | 225 | public function update(Request \$request) 226 | { 227 | \$id = (int)\$request->get('id'); 228 | \$DummySnakeClass = null; 229 | if(\$id){ 230 | \$DummySnakeClass = DummyClass::whereId(\$id)->first(); 231 | } 232 | \$data=\$request->validate([ 233 | 'id' => 'required|int', 234 | @foreach(\$tableFields as \$field) 235 | @if('string'==\$field['rule'] && false==\$field['nullable']) 236 | '{{\$field['field_name'] }}' => 'required' 237 | @endif 238 | @endforeach 239 | ],[],[ 240 | 'id' => 'ID', 241 | @foreach(\$tableFields as \$field) 242 | @if('string'==\$field['rule'] && false==\$field['nullable']) 243 | '{{\$field['field_name'] }}' => '{{\$field['field_display_name'] }}' 244 | @endif 245 | @endforeach 246 | ]); 247 | 248 | if(!\$DummySnakeClass){ 249 | \$DummySnakeClass=new DummyClass(); 250 | } 251 | \$DummySnakeClass->fill(\$data); 252 | if(\$DummySnakeClass->save()){ 253 | return response()->json(['message' => '保存成功', 'errcode' => 0, 'data' => []]); 254 | } 255 | return response()->json(['message' => '保存失败', 'errcode' => 1, 'data' => []]); 256 | } 257 | 258 | public function delete(Request \$request) 259 | { 260 | \$id = (int)\$request->get('id'); 261 | \$DummySnakeClass = DummyClass::whereId(\$id)->first(); 262 | if(\$DummySnakeClass && \$DummySnakeClass->delete()){ 263 | return response()->json(['message' => '删除成功', 'errcode' => 0, 'data' => []]); 264 | } 265 | return response()->json(['message' => '删除失败', 'errcode' => 1, 'data' => []]); 266 | } 267 | } 268 | stub; 269 | 270 | return [ 271 | 'admin' => $homeTemp, 272 | ]; 273 | } 274 | 275 | /** 276 | * @return array 277 | */ 278 | private function getViewsTemplate() 279 | { 280 | $index_temp = <<<'stub' 281 | 348 | 355 | 357 | stub; 358 | $update_temp = <<<'stub' 359 | > 383 | 390 | 392 | stub; 393 | 394 | return [ 395 | 'index' => $index_temp, 396 | 'update' => $update_temp, 397 | ]; 398 | } 399 | 400 | /** 401 | * get the model template. 402 | * 403 | * @return string 404 | */ 405 | private function getModelTemplate() 406 | { 407 | return ' */ 422 | use HasFactory; 423 | @if($modelFields[\'soft_deletes\']) 424 | use SoftDeletes; 425 | @endif 426 | @if(!$modelFields[\'timestamps\']) 427 | public $timestamps = false; 428 | @endif 429 | 430 | @foreach($relationShips as $relationship) 431 | @if(\'hasMany\'==$relationship[\'relationship\']) 432 | public function {{$relationship[\'snake_plural_model\']}}(){ 433 | return $this->hasMany({{$relationship[\'model\']}}::class @if($relationship[\'foreign_key\']),\'{{$relationship[\'foreign_key\']}}\'@endif); 434 | } 435 | @else 436 | public function {{$relationship[\'snake_model\']}}(){ 437 | return $this->{{$relationship[\'relationship\']}}({{$relationship[\'model\']}}::class @if($relationship[\'foreign_key\']),\'{{$relationship[\'foreign_key\']}}\'@endif); 438 | } 439 | @endif 440 | @endforeach 441 | }'; 442 | } 443 | } 444 | -------------------------------------------------------------------------------- /src/FileCreator.php: -------------------------------------------------------------------------------- 1 | file_real_name = $file_real_name; 42 | $this->template = $template; 43 | $this->files = app('files'); 44 | } 45 | 46 | /** 47 | * @return string 48 | * 49 | * @throws \Exception 50 | */ 51 | public function create() 52 | { 53 | $path = base_path($this->file_real_name); 54 | 55 | if ($this->files->exists($path)) { 56 | throw new \Exception("file [$this->file_real_name] already exists!"); 57 | } 58 | 59 | if (!$this->files->isDirectory(dirname($path))) { 60 | try { 61 | $this->files->makeDirectory(dirname($path), 0755, true); 62 | } catch (\Exception $exception) { 63 | throw new \Exception($exception->getMessage().$path); 64 | } 65 | } 66 | $this->files->put($path, $this->template); 67 | 68 | return $path; 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/GeneratorServiceProvider.php: -------------------------------------------------------------------------------- 1 | loadViewsFrom(__DIR__.'/../resources/views', 'laravel-generator'); 18 | 19 | //the language 20 | $this->loadTranslationsFrom(__DIR__.'/../resources/lang', 'laravel-generator'); 21 | 22 | // load migrations 23 | $this->loadMigrationsFrom(__DIR__.'/../resources/migrations'); 24 | 25 | // Publishing generator files. 26 | $this->publishes([ 27 | __DIR__.'/../config/laravel-generator.php' => config_path('laravel-generator.php'), 28 | ],'laravel-generator'); 29 | 30 | //routes 31 | $this->loadRoutesFrom(__DIR__.'/../routes/route.php'); 32 | 33 | if ($this->app->runningInConsole()) { 34 | $this->commands([ 35 | InstallCommand::class, 36 | ]); 37 | } 38 | } 39 | 40 | /** 41 | * Register any package services. 42 | */ 43 | public function register() 44 | { 45 | $this->mergeConfigFrom( 46 | __DIR__.'/../config/laravel-generator.php', 'laravel-generator' 47 | ); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/GeneratorUtils.php: -------------------------------------------------------------------------------- 1 | $placeholder) { 42 | if (isset($laravel_generators[$key])) { 43 | $replacements[$placeholder] = $laravel_generators[$key]; 44 | } 45 | } 46 | $data = array_merge($laravel_generators,[ 47 | 'customKeys' => static::getCustomKeys() 48 | ]); 49 | try { 50 | $result = Blade::render($template,$data); 51 | $replacements['#php#'] = 'getMessage()." in line-".$exception->getLine()); 56 | } 57 | } 58 | public static function compile($template,array $data) :string 59 | { 60 | $template = str_replace(' $data['primary_key'], 67 | 'timestamps' => (bool)$data['timestamps'], 68 | 'soft_deletes' => (bool)$data['soft_deletes'], 69 | ]; 70 | // 可用的假属性字段 71 | $replacements = static::getDummyValues($data['modelName']); 72 | $replacements['DummyDisplayName'] = $data['modelDisplayName']; 73 | $generators = array_merge($generators,[ 74 | 'customKeys' => static::getCustomKeys() 75 | ]); 76 | try { 77 | $result = Blade::render($template,$generators); 78 | $replacements['#php#'] = 'getMessage()." in line-".$exception->getLine()); 83 | } 84 | } 85 | /** 86 | * get all of tables in the default database. 87 | */ 88 | public static function getTables(): array 89 | { 90 | $info = []; 91 | $driver = DB::getDriverName(); 92 | $database = DB::getConfig('database'); 93 | $prefix = DB::getConfig('prefix'); 94 | 95 | $tableNames = match ($driver) { 96 | 'sqlite' => array_map( 97 | fn ($row) => $row->name, 98 | DB::select("SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%'") 99 | ), 100 | 101 | 'mysql' => array_map( 102 | fn ($row) => $row->{"Tables_in_$database"}, 103 | DB::select('SHOW TABLES') 104 | ), 105 | 106 | 'pgsql' => array_map( 107 | fn ($row) => $row->tablename, 108 | DB::select("SELECT tablename FROM pg_tables WHERE schemaname = 'public'") 109 | ), 110 | 111 | default => throw new \RuntimeException("Unsupported DB driver: $driver"), 112 | }; 113 | $ignoreTables = static::ignoreTables(); 114 | foreach ($tableNames as $table) { 115 | if (in_array($table, $ignoreTables)) { 116 | continue; 117 | } 118 | $cleanName = $prefix ? str_replace($prefix, '', $table) : $table; 119 | $info[] = [ 120 | 'name' => $cleanName, 121 | 'columns' => Schema::getColumnListing($table), 122 | ]; 123 | } 124 | 125 | return $info; 126 | } 127 | 128 | public static function ignoreTables(): array 129 | { 130 | return [ 131 | 'migrations', 132 | 'laravel_generators', 133 | 'laravel_generator_configs', 134 | 'laravel_generator_logs', 135 | 'laravel_generator_types', 136 | ]; 137 | } 138 | /** 139 | * get general Engines. 140 | * 141 | * @return array 142 | */ 143 | public static function getGeneralEngines() 144 | { 145 | $engines = []; 146 | $dbEngines = ['InnoDB', 'MyISAM', 'MEMORY', 'ARCHIVE']; 147 | foreach ($dbEngines as $k => $engine) { 148 | $engines[$k]['label'] = $engine; 149 | $engines[$k]['value'] = $engine; 150 | } 151 | 152 | return $engines; 153 | } 154 | 155 | /** 156 | * get dbTypes. 157 | * 158 | * @return array 159 | */ 160 | public static function getDbTypes() 161 | { 162 | $dbTypes = []; 163 | $types = [ 164 | 'string', 'integer', 'text', 'float', 'double', 'decimal', 'boolean', 'date', 'time', 165 | 'dateTime', 'timestamp', 'char', 'mediumText', 'longText', 'tinyInteger', 'smallInteger', 166 | 'mediumInteger', 'bigInteger', 'unsignedTinyInteger', 'unsignedSmallInteger', 'unsignedMediumInteger', 167 | 'unsignedInteger', 'unsignedBigInteger', 'enum', 'json', 'jsonb', 'dateTimeTz', 'timeTz', 168 | 'timestampTz', 'nullableTimestamps', 'binary', 'ipAddress', 'macAddress', 169 | ]; 170 | foreach ($types as $k => $type) { 171 | $dbTypes[$k]['label'] = $type; 172 | $dbTypes[$k]['value'] = $type; 173 | } 174 | 175 | return $dbTypes; 176 | } 177 | 178 | /** 179 | * 获取可用的假字段. 180 | * 181 | * @return array 182 | */ 183 | public static function getDummyAttrs() 184 | { 185 | return [ 186 | 'className' => 'DummyClass', 187 | 'classDisplayName' => 'DummyDisplayName', 188 | 'camelClassName' => 'DummyCamelClass', 189 | 'snakeClassName' => 'DummySnakeClass', 190 | 'pluralClassName' => 'DummyPluralClass', 191 | 'snakePluralClassName' => 'DummySnakePluralClass', 192 | ]; 193 | } 194 | public static function getCustomKeys() 195 | { 196 | return config('laravel-generator.custom_keys', []); 197 | } 198 | 199 | public static function getTags():array 200 | { 201 | return [ 202 | [ 203 | 'name'=>'Controller', 204 | 'path'=>'app/Http/Controllers/Admin/', 205 | 'file'=>'DummyClassController.php', 206 | 'type'=>'primary', 207 | ], 208 | [ 209 | 'name'=>'Test', 210 | 'path'=>'tests/Unit', 211 | 'file'=>'DummyClassTest.php', 212 | 'type'=>'danger', 213 | ], 214 | [ 215 | 'name'=>'Vue', 216 | 'path'=>'resources/views/admin/DummySnakeClass/', 217 | 'file'=>'index.vue', 218 | 'type'=>'warning', 219 | ], 220 | [ 221 | 'name'=>'Request', 222 | 'path'=>'app/Http/Requests/', 223 | 'file'=>'DummyClassRequest.php', 224 | 'type'=>'success', 225 | ] 226 | ]; 227 | } 228 | 229 | /** 230 | * 根据名称获取转换的字段的值 231 | * 232 | * @param $modelName 名称 233 | * @return array 234 | */ 235 | public static function getDummyValues($modelName) 236 | { 237 | return [ 238 | 'DummyClass' => $modelName, 239 | 'DummyCamelClass' => Str::camel($modelName), 240 | 'DummySnakeClass' => Str::snake($modelName), 241 | 'DummyPluralClass' => Str::plural($modelName), 242 | 'DummySnakePluralClass' => Str::snake(Str::plural($modelName)), 243 | ]; 244 | } 245 | 246 | /** 247 | * 获取函数数据. 248 | * 249 | * @return array 250 | */ 251 | public static function getFunctions() 252 | { 253 | return [ 254 | // the if 255 | 'if' => '@if(true) 256 | 257 | @else 258 | 259 | @endif', 260 | // the elseif 261 | 'elseif' => '@if(true) 262 | 263 | @elseif 264 | 265 | @else 266 | 267 | @endif', 268 | // the tableFieldsFor 269 | 'for' => '@foreach($tableFields as $field) 270 | {{ $field[\'field_name\'] }} 271 | @endforeach', 272 | 273 | // the tableFieldsFor 274 | 'soft_deletes' => '@if($modelFields[\'soft_deletes\']) 275 | 276 | @endif 277 | ', 278 | 'timestamps' => '@if($modelFields[\'timestamps\']) 279 | 280 | @endif 281 | ', 282 | 'primary_key' => '{{ $modelFields[\'primary_key\']}}', 283 | // the tableFieldsFor 284 | 'fillable' => 'protected \$fillable = [@foreach($tableFields as $field) @if($field[\'field_name\']!=\'id\')\'{{ $field[\'field_name\'] }}\',@endif @endforeach];', 285 | 286 | // the rule 287 | 'rule' => '@foreach($tableFields as $field) 288 | @if(\'file\'==$field[\'rule\']) 289 | 290 | @endif 291 | @endforeach', 292 | 'relationships' => '@foreach($relationShips as $relationship) 293 | @if(\'hasMany\'==$relationship[\'relationship\']) 294 | public function {{$relationship[\'snake_plural_model\']}}(){ 295 | return $this->hasMany({{$relationship[\'model\']}}::class @if($relationship[\'foreign_key\']),\'{{$relationship[\'foreign_key\']}}\'@endif); 296 | } 297 | @else 298 | public function {{$relationship[\'snake_model\']}}(){ 299 | return $this->{{$relationship[\'relationship\']}}({{$relationship[\'model\']}}::class @if($relationship[\'foreign_key\']),\'{{$relationship[\'foreign_key\']}}\'@endif); 300 | } 301 | @endif 302 | @endforeach', 303 | ]; 304 | } 305 | 306 | /** 307 | * @return array 308 | */ 309 | public static function getGenerators() 310 | { 311 | return [ 312 | 'className' => 'LaravelGenerator', 313 | 'classDisplayName' => 'my laravel generator', 314 | 'camelClassName' => 'laravelGenerator', 315 | 'snakeClassName' => 'laravel_generator', 316 | 'pluralClassName' => 'LaravelGenerators', 317 | 'snakePluralClassName' => 'laravel_generators', 318 | 'tableFields' => [ 319 | [ 320 | 'field_name' => 'user_id', 321 | 'field_display_name' => 'User Id', 322 | 'type' => 'integer', 323 | 'attach' => '', 324 | 'nullable' => false, 325 | 'key' => '', 326 | 'is_list_display' => true, 327 | 'can_search' => true, 328 | 'rule' => 'numeric', 329 | ], 330 | [ 331 | 'field_name' => 'name', 332 | 'field_display_name' => trans('laravel-generator::generator.name'), 333 | 'type' => 'string', 334 | 'attach' => '255', 335 | 'nullable' => false, 336 | 'key' => 'unique', 337 | 'is_list_display' => true, 338 | 'can_search' => true, 339 | 'rule' => 'string', 340 | ], 341 | [ 342 | 'field_name' => 'add_time', 343 | 'field_display_name' => trans('laravel-generator::generator.addTime'), 344 | 'type' => 'timestamp', 345 | 'nullable' => true, 346 | 'key' => '', 347 | 'is_list_display' => true, 348 | 'can_search' => true, 349 | 'rule' => 'date', 350 | ], 351 | [ 352 | 'field_name' => 'upload_file', 353 | 'field_display_name' => trans('laravel-generator::generator.file'), 354 | 'type' => 'string', 355 | 'nullable' => true, 356 | 'key' => '', 357 | 'is_list_display' => true, 358 | 'can_search' => false, 359 | 'rule' => 'file', 360 | ], 361 | ], 362 | 'modelFields' => [ 363 | 'primary_key' => 'id', 364 | 'timestamps' => true, 365 | 'soft_deletes' => true, 366 | ], 367 | 'relationShips' => [ 368 | [ 369 | 'relationship' => 'belongsTo', 370 | 'model' => 'LaravelGeneratorType', 371 | 'camel_model' => 'laravelGeneratorType', 372 | 'snake_model' => 'laravel_generator_type', 373 | 'snake_plural_model' => 'laravel_generator_types', 374 | 'foreign_key' => 'template_id', 375 | 'reverse' => 'hasMany', 376 | 'with' => true, 377 | 'can_search' => true, 378 | ], 379 | ], 380 | ]; 381 | } 382 | 383 | public static function getDoctrineTable(string $tableName): Table 384 | { 385 | // 获取 Laravel 当前连接配置 386 | $config = config('database.connections.' . config('database.default')); 387 | 388 | // 转换为 Doctrine DBAL 配置 389 | $doctrineConfig = match ($config['driver']) { 390 | 'mysql' => [ 391 | 'dbname' => $config['database'], 392 | 'user' => $config['username'], 393 | 'password' => $config['password'], 394 | 'host' => $config['host'], 395 | 'port' => $config['port'] ?? 3306, 396 | 'driver' => 'pdo_mysql', 397 | 'charset' => $config['charset'] ?? 'utf8mb4', 398 | ], 399 | 'pgsql' => [ 400 | 'dbname' => $config['database'], 401 | 'user' => $config['username'], 402 | 'password' => $config['password'], 403 | 'host' => $config['host'], 404 | 'port' => $config['port'] ?? 5432, 405 | 'driver' => 'pdo_pgsql', 406 | ], 407 | 'sqlite' => [ 408 | 'driver' => 'pdo_sqlite', 409 | 'path' => $config['database'], 410 | ], 411 | default => throw new \RuntimeException('Unsupported driver: ' . $config['driver']), 412 | }; 413 | 414 | // 创建 Doctrine Connection 415 | $connection = DriverManager::getConnection($doctrineConfig); 416 | $schemaManager = $connection->createSchemaManager(); 417 | // 返回 Table 元信息 418 | return $schemaManager->introspectTable($tableName); 419 | } 420 | 421 | /** 422 | * get table columns by table name 423 | * @param $table table name 424 | * @return array columns 425 | * @throws \Doctrine\DBAL\Exception 426 | */ 427 | public static function getTableColumns($table): array 428 | { 429 | $table_name = config('database.connections.' . config('database.default').'.prefix').$table; 430 | $columns = static::getDoctrineTable($table_name)->getColumns(); 431 | $res = [ 432 | 'primary_key' => '', 433 | 'table_fields' => [], 434 | ]; 435 | $i = 0; 436 | foreach ($columns as $column) { 437 | $table_field = []; 438 | $is_auto_increment = $column->getAutoincrement(); 439 | if(!$is_auto_increment) { 440 | $type = $column->getType(); 441 | $table_field['field_name'] = $column->getName(); 442 | $table_field['field_display_name'] = $column->getName(); 443 | $type_name = $type::getTypeRegistry()->lookupName($type); 444 | $table_field['type'] = static::typeTransformer($type_name); 445 | $attach = ''; 446 | if ( $type instanceof DecimalType || $type instanceof FloatType) { 447 | $attach .= $column->getPrecision()??''; 448 | $attach .= $column->getScale()?','.$column->getScale():''; 449 | } else { 450 | $attach .= $column->getLength()??''; 451 | } 452 | $table_field['attach'] = $attach; 453 | $table_field['can_search'] = false; 454 | $table_field['key'] = ''; 455 | $table_field['rule'] = ''; 456 | $table_field['is_list_display'] = false; 457 | $table_field['nullable'] = $column->getNotnull(); 458 | $table_field['default'] = $column->getDefault(); 459 | $table_field['comment'] = $column->getComment(); 460 | $res['table_fields'][$i] = $table_field; 461 | $i++; 462 | }else{ 463 | $res['primary_key'] = $column->getName(); 464 | } 465 | 466 | } 467 | 468 | return $res; 469 | } 470 | 471 | public static function typeTransformer($type) 472 | { 473 | return match ($type) { 474 | 'datetime' => 'dateTime', 475 | 'datetimetz' => 'dateTimeTz', 476 | 'bigint' => 'bigInteger', 477 | default => $type, 478 | }; 479 | } 480 | 481 | /** 482 | * @throws Exception 483 | */ 484 | public static function tableToForm($table_name): array 485 | { 486 | $result = [ 487 | "id" => 0, 488 | "create" => [], 489 | "foreigns" =>[], 490 | "timestamps" =>false, 491 | "soft_deletes" =>false, 492 | "modelName" => static::modelFromTable($table_name), 493 | "templates" => [], 494 | "modelDisplayName" => $table_name, 495 | "relationships" => [] 496 | ]; 497 | $table_columns = static::getTableColumns($table_name); 498 | $fields = $table_columns['table_fields']; 499 | $hasCreatedAt = false; 500 | $hasUpdatedAt = false; 501 | foreach ($fields as $field) { 502 | if ($field['field_name'] === 'created_at') { 503 | $hasCreatedAt = true; 504 | } 505 | if ($field['field_name'] === 'updated_at') { 506 | $hasUpdatedAt = true; 507 | } 508 | } 509 | if ($hasCreatedAt && $hasUpdatedAt) { 510 | $table_columns['table_fields'] = array_values(array_filter($fields, function ($field) { 511 | return !in_array($field['field_name'], ['created_at', 'updated_at']); 512 | })); 513 | $result['timestamps'] = true; 514 | } 515 | 516 | return array_merge($result, $table_columns); 517 | 518 | } 519 | 520 | 521 | public static function modelFromTable(string $table): ?string { 522 | return Str::studly(Str::singular($table)); 523 | } 524 | } 525 | -------------------------------------------------------------------------------- /src/Message.php: -------------------------------------------------------------------------------- 1 | json(['message' => 'success', 'errcode' => 0, 'data' => $data]); 27 | } 28 | 29 | /** 30 | * 31 | * @param $message 32 | * @param int $errcode 33 | * @return \Illuminate\Http\JsonResponse 34 | */ 35 | public function error($message, $errcode=1) 36 | { 37 | return response()->json(['message' => $message, 'errcode' => $errcode, 'data' => []]); 38 | } 39 | } -------------------------------------------------------------------------------- /src/MigrationCreator.php: -------------------------------------------------------------------------------- 1 | ensureMigrationDoesntAlreadyExist($name); 31 | 32 | $path = $this->getPath($name, $path); 33 | $stub = $this->get_stub(); 34 | 35 | $this->isCreate = $create; 36 | $this->files->put($path, $this->replaceStub($name, $stub, $table)); 37 | 38 | return $path; 39 | } 40 | 41 | /** 42 | * Build the table blueprint. 43 | * 44 | * @param array $fields 45 | * @param string $keyName 46 | * @param bool|true $useTimestamps 47 | * @param bool|false $softDeletes 48 | * 49 | * @throws \Exception 50 | * 51 | * @return $this 52 | */ 53 | public function buildBluePrint($fields = [], $keyName = 'id', $useTimestamps = true, $softDeletes = false, $foreigns = []) 54 | { 55 | $fields = array_filter($fields, function ($field) { 56 | return isset($field['field_name']) && !empty($field['field_name']); 57 | }); 58 | 59 | if (empty($fields)) { 60 | throw new \Exception('Table fields can\'t be empty'); 61 | } 62 | 63 | //设置字段 64 | $this->fields = $fields; 65 | $rows = []; 66 | if (isset($keyName)) { 67 | $rows[] = "\$table->increments('$keyName');\n"; 68 | } 69 | foreach ($fields as $k => $field) { 70 | if (isset($field['attach'])) { 71 | $column = "\$table->{$field['type']}('{$field['field_name']}',{$field['attach']})"; 72 | } else { 73 | $column = "\$table->{$field['type']}('{$field['field_name']}')"; 74 | } 75 | 76 | if ($field['key']) { 77 | $column .= "->{$field['key']}()"; 78 | } 79 | 80 | if (isset($field['default']) && $field['default']) { 81 | $column .= "->default('{$field['default']}')"; 82 | } 83 | 84 | if (isset($field['comment']) && $field['comment']) { 85 | $column .= "->comment('{$field['comment']}')"; 86 | } 87 | 88 | if (isset($field['nullable']) && $field['nullable']) { 89 | $column .= '->nullable()'; 90 | } 91 | if (isset($field['change']) && $field['change']) { 92 | $column .= '->change()'; 93 | } 94 | $rows[] = $column.";\n"; 95 | } 96 | 97 | if ($useTimestamps) { 98 | $rows[] = "\$table->timestamps();\n"; 99 | } 100 | 101 | if ($softDeletes) { 102 | $rows[] = "\$table->softDeletes();\n"; 103 | } 104 | 105 | //添加关联关系 106 | if ($foreigns) { 107 | $rows[] = "\n"; 108 | foreach ($foreigns as $foreign) { 109 | $onDelete = ''; 110 | if (isset($foreign['onDelete']) && $foreign['onDelete']) { 111 | $onDelete = "->onDelete('{$foreign['onDelete']}')"; 112 | } 113 | $onUpdate = ''; 114 | if (isset($foreign['onUpdate']) && $foreign['onUpdate']) { 115 | $onUpdate = "->onUpdate('{$foreign['onUpdate']}')"; 116 | } 117 | $rows[] = "\$table->foreign('{$foreign['foreign']}')->references('{$foreign['references']}')->on('{$foreign['on']}'){$onDelete}{$onUpdate};\n"; 118 | } 119 | } 120 | 121 | $this->bluePrint = trim(implode(str_repeat(' ', 12), $rows), "\n"); 122 | 123 | return $this; 124 | } 125 | 126 | /** 127 | * Populate stub. 128 | * 129 | * @param string $name 130 | * @param string $stub 131 | * @param string $table 132 | * 133 | * @return mixed 134 | */ 135 | protected function replaceStub($name, $stub, $table) 136 | { 137 | $type = $this->isCreate ? 'create' : 'table'; 138 | if ($this->isCreate) { 139 | //删除表 140 | $down = "Schema::dropIfExists('{$table}');"; 141 | } else { 142 | //删除修改表的字段 143 | $down = "Schema::table('{$table}', function (Blueprint \$table) {\n"; 144 | foreach ($this->fields as $field) { 145 | if (!$field['change']) { 146 | $down .= " \$table->dropColumn('{$field['field_name']}');\n"; 147 | } 148 | } 149 | $down .= ' });'; 150 | } 151 | 152 | return str_replace( 153 | ['DummyClass', 'DummyTable', 'DummyStructure', 'create', 'DummyDownTable'], 154 | [$this->getClassName($name), $table, $this->bluePrint, $type, $down], 155 | $stub 156 | ); 157 | } 158 | 159 | private function get_stub() 160 | { 161 | return << 'boolean', 18 | ]; 19 | 20 | /** 21 | * 设置路径 22 | * 23 | * @param $value 24 | */ 25 | public function setPathAttribute($value) 26 | { 27 | $this->attributes['path'] = trim($value,'/').'/'; 28 | } 29 | 30 | /** 31 | * 设置文件名 32 | * 33 | * @param $value 34 | */ 35 | public function setFileNameAttribute($value) 36 | { 37 | $this->attributes['file_name'] = trim($value,'/'); 38 | } 39 | /** 40 | * 41 | * @return \Illuminate\Database\Eloquent\Relations\BelongsTo 42 | */ 43 | public function template_type() 44 | { 45 | return $this->belongsTo(LaravelGeneratorType::class,'template_id','id'); 46 | } 47 | 48 | /** 49 | * 设置是否选中 50 | * 51 | * @param $value 52 | */ 53 | public function setIsCheckedAttribute($value) 54 | { 55 | $this->attributes['is_checked'] = (int)$value; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/Models/LaravelGeneratorConfig.php: -------------------------------------------------------------------------------- 1 | |LaravelGeneratorConfig newModelQuery() 17 | * @method static \Illuminate\Database\Eloquent\Builder|LaravelGeneratorConfig newQuery() 18 | * @method static \Illuminate\Database\Eloquent\Builder|LaravelGeneratorConfig query() 19 | * @method static \Illuminate\Database\Eloquent\Builder|LaravelGeneratorConfig whereAlias($value) 20 | * @method static \Illuminate\Database\Eloquent\Builder|LaravelGeneratorConfig whereConfig($value) 21 | * @method static \Illuminate\Database\Eloquent\Builder|LaravelGeneratorConfig whereCreatedAt($value) 22 | * @method static \Illuminate\Database\Eloquent\Builder|LaravelGeneratorConfig whereId($value) 23 | * @method static \Illuminate\Database\Eloquent\Builder|LaravelGeneratorConfig whereUpdatedAt($value) 24 | * @mixin \Eloquent 25 | */ 26 | class LaravelGeneratorConfig extends Model 27 | { 28 | 29 | protected $fillable = ['alias','config','group',]; 30 | 31 | 32 | 33 | 34 | 35 | } 36 | -------------------------------------------------------------------------------- /src/Models/LaravelGeneratorLog.php: -------------------------------------------------------------------------------- 1 | format('Y-m-d H:i:s'); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/Models/LaravelGeneratorType.php: -------------------------------------------------------------------------------- 1 | hasMany(LaravelGenerator::class,'template_id','id'); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /tests/GeneratorUtilsTest.php: -------------------------------------------------------------------------------- 1 | getPrimaryKey(); 9 | dump($res); 10 | expect($res)->not->toBeEmpty(); 11 | }); 12 | test('get table columns', function () { 13 | $table = 'laravel_generators'; 14 | $columns = GeneratorUtils::getTableColumns($table); 15 | dump($columns); 16 | expect($columns)->not->toBeEmpty(); 17 | }); 18 | 19 | test('table to form', function () { 20 | $table = 'laravel_generators'; 21 | $columns = GeneratorUtils::tableToForm($table); 22 | expect($columns)->not->toBeEmpty(); 23 | }); 24 | -------------------------------------------------------------------------------- /tests/LaravelGeneratorLogTest.php: -------------------------------------------------------------------------------- 1 | count(5)->create(); 8 | expect(LaravelGeneratorLog::count())->toBeGreaterThan(0); 9 | }); 10 | --------------------------------------------------------------------------------