├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── ISSUE_TEMPLATE.md
├── LICENSE
├── Laravel
├── .idea
│ ├── LAM.iml
│ ├── encodings.xml
│ ├── misc.xml
│ ├── modules.xml
│ ├── vcs.xml
│ └── workspace.xml
├── AutoMakeFileParser.php
├── Maker
│ ├── BaseMaker.php
│ ├── CommonMaker.php
│ ├── MigrationMaker.php
│ ├── RepositoryMaker.php
│ ├── TableMaker.php
│ └── exampleRaw.json
├── Parser
│ ├── BaseParser.php
│ ├── CommonParser.php
│ ├── RepositoryParser.php
│ └── TableParser.php
├── Scalpel
│ ├── BaseScalpel.php
│ └── ControllerScalpel.php
├── _config.yml
├── exampleRaw.txt
└── stubs
│ ├── create.migration.plain.stub
│ ├── ctrl_func.plain.stub
│ ├── model.plain.stub
│ ├── res.plain.stub
│ └── serv.plain.stub
└── README.md
/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | # Code of Conduct
2 |
3 | ## 1. Purpose
4 |
5 | A primary goal of Lam is to be inclusive to the largest number of contributors, with the most varied and diverse backgrounds possible. As such, we are committed to providing a friendly, safe and welcoming environment for all, regardless of gender, sexual orientation, ability, ethnicity, socioeconomic status, and religion (or lack thereof).
6 |
7 | This code of conduct outlines our expectations for all those who participate in our community, as well as the consequences for unacceptable behavior.
8 |
9 | We invite all those who participate in Lam to help us create safe and positive experiences for everyone.
10 |
11 | ## 2. Open Source Citizenship
12 |
13 | A supplemental goal of this Code of Conduct is to increase open source citizenship by encouraging participants to recognize and strengthen the relationships between our actions and their effects on our community.
14 |
15 | Communities mirror the societies in which they exist and positive action is essential to counteract the many forms of inequality and abuses of power that exist in society.
16 |
17 | If you see someone who is making an extra effort to ensure our community is welcoming, friendly, and encourages all participants to contribute to the fullest extent, we want to know.
18 |
19 | ## 3. Expected Behavior
20 |
21 | The following behaviors are expected and requested of all community members:
22 |
23 | * Participate in an authentic and active way. In doing so, you contribute to the health and longevity of this community.
24 | * Exercise consideration and respect in your speech and actions.
25 | * Attempt collaboration before conflict.
26 | * Refrain from demeaning, discriminatory, or harassing behavior and speech.
27 | * Be mindful of your surroundings and of your fellow participants. Alert community leaders if you notice a dangerous situation, someone in distress, or violations of this Code of Conduct, even if they seem inconsequential.
28 | * Remember that community event venues may be shared with members of the public; please be respectful to all patrons of these locations.
29 |
30 | ## 4. Unacceptable Behavior
31 |
32 | The following behaviors are considered harassment and are unacceptable within our community:
33 |
34 | * Violence, threats of violence or violent language directed against another person.
35 | * Sexist, racist, homophobic, transphobic, ableist or otherwise discriminatory jokes and language.
36 | * Posting or displaying sexually explicit or violent material.
37 | * Posting or threatening to post other people’s personally identifying information ("doxing").
38 | * Personal insults, particularly those related to gender, sexual orientation, race, religion, or disability.
39 | * Inappropriate photography or recording.
40 | * Inappropriate physical contact. You should have someone’s consent before touching them.
41 | * Unwelcome sexual attention. This includes, sexualized comments or jokes; inappropriate touching, groping, and unwelcomed sexual advances.
42 | * Deliberate intimidation, stalking or following (online or in person).
43 | * Advocating for, or encouraging, any of the above behavior.
44 | * Sustained disruption of community events, including talks and presentations.
45 |
46 | ## 5. Consequences of Unacceptable Behavior
47 |
48 | Unacceptable behavior from any community member, including sponsors and those with decision-making authority, will not be tolerated.
49 |
50 | Anyone asked to stop unacceptable behavior is expected to comply immediately.
51 |
52 | If a community member engages in unacceptable behavior, the community organizers may take any action they deem appropriate, up to and including a temporary ban or permanent expulsion from the community without warning (and without refund in the case of a paid event).
53 |
54 | ## 6. Reporting Guidelines
55 |
56 | If you are subject to or witness unacceptable behavior, or have any other concerns, please notify a community organizer as soon as possible. 510331882@qq.com.
57 |
58 |
59 |
60 | Additionally, community organizers are available to help community members engage with local law enforcement or to otherwise help those experiencing unacceptable behavior feel safe. In the context of in-person events, organizers will also provide escorts as desired by the person experiencing distress.
61 |
62 | ## 7. Addressing Grievances
63 |
64 | If you feel you have been falsely or unfairly accused of violating this Code of Conduct, you should notify Taoism Coder with a concise description of your grievance. Your grievance will be handled in accordance with our existing governing policies.
65 |
66 |
67 |
68 | ## 8. Scope
69 |
70 | We expect all community participants (contributors, paid or otherwise; sponsors; and other guests) to abide by this Code of Conduct in all community venues–online and in-person–as well as in all one-on-one communications pertaining to community business.
71 |
72 | This code of conduct and its related procedures also applies to unacceptable behavior occurring outside the scope of community activities when such behavior has the potential to adversely affect the safety and well-being of community members.
73 |
74 | ## 9. Contact info
75 |
76 | 510331882@qq.com
77 |
78 | ## 10. License and attribution
79 |
80 | This Code of Conduct is distributed under a [Creative Commons Attribution-ShareAlike license](http://creativecommons.org/licenses/by-sa/3.0/).
81 |
82 | Portions of text derived from the [Django Code of Conduct](https://www.djangoproject.com/conduct/) and the [Geek Feminism Anti-Harassment Policy](http://geekfeminism.wikia.com/wiki/Conference_anti-harassment/Policy).
83 |
84 | Retrieved on November 22, 2016 from [http://citizencodeofconduct.org/](http://citizencodeofconduct.org/)
85 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | 1. 下载本项目
2 | 2. 将所有文件复制到 Helpers/LAM 文件夹中,文件夹请自行创建
3 | 3. 在控制器中或其他地方调用 `use App\Helpers\LAM\AutoMakeFileParser;`
4 | 4. 实例化并使用 LAM :
5 | ``` php
6 | ...
7 | // Sotrage 获取文件的默认路径为 `storage\app\`
8 | $content = Storage::get('exampleRaw.txt');
9 | $autoMaker = new AutoMakeFileParser();
10 | $autoMaker->parse($content)->makeFiles();
11 | ...
12 | ```
13 | ```php
14 | // 一句话写法
15 | $rst = (new AutoMakeFileParser())->parse(Storage::get('exampleRaw.txt'))->makeFiles();
16 | ```
17 |
--------------------------------------------------------------------------------
/ISSUE_TEMPLATE.md:
--------------------------------------------------------------------------------
1 | ### 问题简介:
2 | ### 问题描述:
3 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2017 TaoismCoder
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/Laravel/.idea/LAM.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/Laravel/.idea/encodings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/Laravel/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/Laravel/.idea/modules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/Laravel/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/Laravel/.idea/workspace.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 | false
45 |
46 | false
47 | false
48 | true
49 |
50 |
51 | true
52 | DEFINITION_ORDER
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 | Internationalization issues
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 | 1510496789781
129 |
130 |
131 | 1510496789781
132 |
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
153 |
154 |
155 |
156 |
157 |
158 |
159 |
160 |
161 |
162 |
163 |
164 |
165 |
166 |
167 |
168 |
169 |
170 |
171 |
172 |
--------------------------------------------------------------------------------
/Laravel/AutoMakeFileParser.php:
--------------------------------------------------------------------------------
1 | https://github.com/taoismCoder/LAM
12 | */
13 | class AutoMakeFileParser extends CommonParser
14 | {
15 | /**
16 | * 解析后的格式化数据
17 | * @var array
18 | */
19 | protected $parsed = [];
20 |
21 | /**
22 | * 要生成的文件类型
23 | * @var string
24 | */
25 | protected $makeType = '';
26 |
27 | /**
28 | * 表与关联的模型的数据
29 | * @var array
30 | */
31 | protected $modelTableRelation = [];
32 |
33 | /**
34 | * 匹配类型为 explode
35 | * @var string
36 | */
37 | const TYPE_EXPLODE = 'explode';
38 |
39 | /**
40 | * 匹配类型为正则
41 | * @var string
42 | */
43 | const TYPE_PREG = 'preg';
44 |
45 | /**
46 | * AutoMakeFileParser constructor.
47 | */
48 | public function __construct()
49 | {
50 | parent::__construct();
51 | }
52 |
53 | /**
54 | * 解析器配置列表
55 | * @return array
56 | */
57 | public function dispatcherConfig($type)
58 | {
59 | $configList = [
60 | 'table' => [
61 | 'parser' => Parser\TableParser::class,
62 | 'maker' => Maker\TableMaker::class,
63 | ],
64 | 'repository' => [
65 | 'parser' => Parser\TableParser::class,
66 | 'maker' => Maker\RepositoryMaker::class
67 | ]
68 | ];
69 | return isset($configList[$type]) ? $configList[$type] : [];
70 | }
71 |
72 | /**
73 | * 表信息解析器
74 | * @return \Illuminate\Foundation\Application|Parser\TableParser
75 | */
76 | protected function getTableParser()
77 | {
78 | return app('App\Helpers\LAM\Parser\TableParser');
79 | }
80 |
81 | /**
82 | * Migration 生成器
83 | * @return \Illuminate\Foundation\Application|\App\Helpers\LAM\Maker\MigrationMaker
84 | */
85 | protected function getMigrationMaker()
86 | {
87 | return app('App\Helpers\LAM\Maker\MigrationMaker');
88 | }
89 |
90 | /**
91 | * 设置解析后的格式化值
92 | * @param $parsed
93 | */
94 | protected function setParsed($parsed)
95 | {
96 | $this->parsed = $parsed;
97 | }
98 |
99 | /**
100 | * 获取解析后的格式化值
101 | * @return array
102 | */
103 | protected function getParsed()
104 | {
105 | return $this->parsed;
106 | }
107 |
108 | /**
109 | * 设置要生成的文件类型
110 | * @param $type
111 | */
112 | protected function setMakeType($type)
113 | {
114 | $this->makeType = $type;
115 | }
116 |
117 | /**
118 | * 要生成的文件类型
119 | * @param string $type
120 | * @return string
121 | * @todo 该方法基本可以废除,由分配方法将解析后的内容分配到各自的生成器和解析器
122 | */
123 | protected function getMakeType($type = '')
124 | {
125 | $rst = '';
126 | $type = $type ?: $this->makeType;
127 | switch ($type){
128 | case 'route':
129 | $rst = 'Route';
130 | break;
131 | case 'ctrl':
132 | $rst = 'Controller';
133 | break;
134 | case 'serv':
135 | $rst = 'Service';
136 | break;
137 | case 'res':
138 | $rst = 'Repository';
139 | break;
140 | case 'table':
141 | $rst = 'Table';
142 | break;
143 | case 'model':
144 | $rst = 'Model';
145 | break;
146 | };
147 | return $rst;
148 | }
149 |
150 | /**
151 | * 解析源文件内容
152 | * @return AutoMakeFileParser|array
153 | */
154 | public function parse($raw)
155 | {
156 | // 重置生成类型
157 | $this->setMakeType('');
158 |
159 | $parsed = [];
160 | list($headings, $data) = $this->parseRawData($raw);
161 |
162 | if ( ! is_array($headings)) {
163 | return $parsed;
164 | }
165 | foreach ($headings as $key => $heading) {
166 |
167 | $parsed[] = [
168 | 'type' => $heading,
169 | 'intro' => $this->parseIntro($heading, $data[$key])
170 | ];
171 | };
172 |
173 | unset($headings, $data);
174 |
175 | $this->setParsed($parsed);
176 |
177 | return $this;
178 | }
179 |
180 | /**
181 | * 根据解析结果生成对应的文件
182 | * @todo 该方法触发所有类型的生成器方法,未来可以改为可选择并可以提示下一步
183 | */
184 | public function makeFiles()
185 | {
186 | $fileParseRst = $this->getParsed();
187 | foreach ($fileParseRst as $item){
188 | $this->makeSingleFile($item['type'], $item['intro']);
189 | // $this->dispatcher($item['type'], $item['intro']);
190 | }
191 | }
192 |
193 | /**
194 | * 开始执行调度器
195 | * @param $type
196 | * @param $intro
197 | * @return \Illuminate\Foundation\Application|\App\Helpers\LAM\Maker\BaseMaker
198 | */
199 | public function dispatcher($type, $intro)
200 | {
201 | $config = $this->dispatcherConfig($type);
202 | if(!empty($config)){
203 | // 执行解析器,获取解析结果
204 | $parserResult = app($config['parser'])->setRawIntro($intro)->beginParser()->getResult();
205 | // 执行生成器,生成文件
206 | return app($config['maker'])->setParsed($parserResult)->makeFile();
207 | }
208 | }
209 |
210 | /**
211 | * 生成单个文件
212 | * @param $type
213 | * @param $intro
214 | * @return bool
215 | * @todo 将此方法重构为分配到不同的生成器中,尽量一句话
216 | * @todo 可以参考 laravel 中登录 $this->guide($guide)->login() 这种
217 | */
218 | public function makeSingleFile($type, $intro)
219 | {
220 | $this->setMakeType($type);
221 |
222 | if($type == 'route'){
223 | return $this->makeRoute($intro);
224 | }
225 |
226 | if($type == 'serv'){
227 | return $this->makeService($intro);
228 | }
229 |
230 | // 会同时生成 res 中包含的 model
231 | if($type == 'res'){
232 | return $this->makeRepository($intro);
233 | }
234 |
235 | // table 中的 model 不在此列,此处只生成与 table 无关的model
236 | if($type == 'model'){
237 | return $this->makeModel($intro);
238 | }
239 |
240 | // 生成 table 对应的 migration
241 | if($type == 'table'){
242 | return $this->makeMigration($intro);
243 | }
244 | }
245 |
246 | /**
247 | * 生成路由及控制器
248 | * @todo 生成路由暂时忽略, 此方法改为自定义模板生成,不依赖 Artisan
249 | * @param $intro
250 | * @return bool
251 | */
252 | private function makeRoute($intro)
253 | {
254 | $type = 'ctrl';
255 |
256 | foreach ($intro as $filePathName => $item){
257 | $needMakeFilePath = $this->getNeedMakeFilePath($filePathName, $type);
258 | if(!file_exists($needMakeFilePath)){
259 | // 生成基础 Controller
260 | Artisan::call('make:controller', ['name' => $filePathName]);
261 | echo $filePathName.' '.Artisan::output().' ';
262 | // 修改基础 Controller, 追加 route 信息中存在的方法, 一个文件只会执行一次
263 | if(file_exists($needMakeFilePath)){
264 | echo '
开始装入需要的函数 ';
265 | // 要添加的函数列表
266 | $funcArr = array_column($item, 'action');
267 | // 当前文件的文本内容
268 | $fileContents = $this->getFileContents($needMakeFilePath);
269 | // 函数模版内容
270 | $funcStubContent = $this->getFileContents($this->getStub('ctrl_func'));
271 | // 替换模版中的值,并追加到控制器内容中
272 | $this->appendFuncToFileContent($needMakeFilePath, $funcArr, $fileContents, $funcStubContent, false);
273 | }
274 | }else{
275 | echo $filePathName.' 文件已存在 ';
276 | return false;
277 | }
278 |
279 | }
280 | return true;
281 | }
282 |
283 | /**
284 | * 生成服务类
285 | * @param $intro
286 | * @return bool
287 | * @todo 改为调用服务生成器 ServiceMaker
288 | */
289 | private function makeService($intro)
290 | {
291 | $type = 'serv';
292 | $needMakeFiles = $intro;
293 | foreach ($needMakeFiles as $filePathName){
294 | // 判断文件是否存在,其中包含根据服务名称获取要生成的文件路径
295 | $fileIsExists = $this->alreadyExists($filePathName, $type);
296 |
297 | if(!$fileIsExists){
298 | // 获取 Service 生成模版
299 | $stubPath = $this->getStub($type);
300 | if (file_exists($stubPath)){
301 | // 根据服务名称获取需要生成的文件路径
302 | $needMakeFilePath = $this->getNeedMakeFilePath($filePathName, $type);
303 | // 需要创建的文件夹路径
304 | $needMakeDir = str_replace(class_basename($filePathName).'.php', '', $needMakeFilePath);
305 | // 需要替换的 tag 及对应的值
306 | $replaceArr = [
307 | 'DummyNamespace' => $this->getFileNamespace($filePathName),
308 | 'DummyClass' => class_basename($filePathName),
309 | 'DummyUsePath' => $filePathName,
310 | 'DummyMore' => ''
311 | ];
312 | // 替换模版中的名称及相关信息
313 | $finalContents = $this->replaceStubTags($stubPath, $replaceArr);
314 | // 判断文件夹是否存在,不存在则创建文件夹
315 | if(!is_dir($needMakeDir)){
316 | mkdir($needMakeDir, 0755, true);
317 | }
318 | // 输出文件
319 | $this->put($needMakeFilePath, $finalContents);
320 | echo $needMakeFilePath.' 执行完毕 ';
321 | }else{
322 | echo $filePathName.' 模板不存在 ';
323 | return false;
324 | }
325 | }else{
326 | echo $filePathName.' 文件已存在 ';
327 | return false;
328 | }
329 |
330 | }
331 | return true;
332 | }
333 |
334 | /**
335 | * 生成仓库类
336 | * @param $intro
337 | * @return bool
338 | * @todo 改为仓库生成器 RepositoryMaker
339 | */
340 | protected function makeRepository($intro)
341 | {
342 | $type = $this->makeType;
343 | $needMakeFiles = $intro;
344 | foreach ($needMakeFiles as $filePathName){
345 | // 判断文件是否存在,其中包含根据服务名称获取要生成的文件路径
346 | $fileIsExists = $this->alreadyExists($filePathName, $type);
347 | if(!$fileIsExists){
348 | // 获取 Repository 生成模版
349 | $stubPath = $this->getStub($type);
350 | if (file_exists($stubPath)){
351 | // 根据服务名称获取需要生成的文件路径
352 | $needMakeFilePath = $this->getNeedMakeFilePath($filePathName, $type);
353 | // 需要创建的文件夹路径
354 | $needMakeDir = $this->getPathDir($needMakeFilePath);
355 | // 基础类名 或 对应的 Model 名称
356 | $baseClassName = class_basename($filePathName);
357 | // 当前文件完整的类名
358 | $dummyClassName = $baseClassName.$this->getMakeType();
359 |
360 | // 需要替换 model 模版的 tag 及对应的值
361 | $tableModelNamespace = $this->getFileNamespace($filePathName, 'model');
362 | $replaceModelArr = [
363 | 'DummyNamespace' => $tableModelNamespace,
364 | 'DummyClassName' => $baseClassName,
365 | 'DummyUsePath' => $tableModelNamespace.'\\'.$baseClassName,
366 | 'DummyFilePathName' => $filePathName
367 | ];
368 | // 生成相关 Model, 传入生成的 Model 路径
369 | $this->makeTableModel($this->getNeedMakeFilePath($filePathName, 'model'), $replaceModelArr);
370 |
371 | // 需要替换 res 模版的 tag 及对应的值
372 | $replaceArr = [
373 | 'DummyNamespace' => $this->getFileNamespace($filePathName),
374 | 'DummyClassName' => $dummyClassName,
375 | 'DummyUsePath' => $this->getFileNamespace($filePathName).'\\'.$dummyClassName,
376 | 'DummyModelName' => $baseClassName,
377 | 'DummyModelUsePath' => $this->getFileNamespace($filePathName, 'model').'\\'.$baseClassName,
378 | 'DummyFilePathName' => $filePathName,
379 | 'DummyMore' => ''
380 | ];
381 | // 替换模版中的名称及相关信息
382 | $finalContents = $this->replaceStubTags($stubPath, $replaceArr);
383 |
384 | // 判断文件夹是否存在,不存在则创建文件夹
385 | if(!is_dir($needMakeDir)){
386 | mkdir($needMakeDir, 0755, true);
387 | }
388 | // 输出文件
389 | $this->put($needMakeFilePath, $finalContents);
390 | echo $needMakeFilePath.' 执行完毕 ';
391 | }else{
392 | echo $filePathName.' 模板不存在 ';
393 | return false;
394 | }
395 | }else{
396 | echo $filePathName.' 文件已存在 ';
397 | return false;
398 | }
399 |
400 | }
401 | return true;
402 | }
403 |
404 | /**
405 | * 生成与数据表无关的模型类
406 | * @param $intro
407 | * @return bool
408 | * @todo 改为模型生成器
409 | */
410 | protected function makeModel($intro)
411 | {
412 | $type = $this->makeType;
413 | $needMakeFiles = $intro;
414 | //todo 等待
415 | return true;
416 | }
417 |
418 | /**
419 | * 生成 Migration
420 | * @param $intro
421 | * @return bool
422 | * @todo 改为Migration生成器
423 | */
424 | protected function makeMigration($intro)
425 | {
426 | //$tableParsedArray = $this->getModelTableRelation();
427 | //TODO 建议:$migrationParsed = $this->getMigrationParser($this->parsed);
428 | //$this->getMigrationMaker()->setRawTxtArray($tableParsedArray)->makeMigration();
429 | //TODO 建议:$this->getMigrationMaker()->setParsed($migrationParsed)->make();
430 | return true;
431 | }
432 |
433 | /**
434 | * 生成数据表模型类
435 | * @param $realFilePath
436 | * @param $replaceModelArr
437 | * @return bool
438 | * @todo 改为表模型生成器
439 | */
440 | protected function makeTableModel($realFilePath, $replaceModelArr)
441 | {
442 | // 判断当前文件是否存在
443 | if($this->alreadyExists($realFilePath, 'model')){
444 | echo $realFilePath.' 文件已存在 ';
445 | return false;
446 | }
447 | // 需要创建的文件夹路径
448 | $needMakeDir = $this->getPathDir($realFilePath);
449 | // 检查模版路径是否存在
450 | $stubPath = $this->getStub('model');
451 | if (!file_exists($stubPath)){
452 | echo $stubPath.' 模板不存在 ';
453 | return false;
454 | }
455 | // 判断文件夹是否存在,不存在则创建文件夹
456 | if(!is_dir($needMakeDir)){
457 | // 创建文件夹
458 | mkdir($needMakeDir, 0755, true);
459 | }
460 |
461 | // 替换模版中的名称及相关信息
462 | $finalContents = $this->replaceStubTags($stubPath, $replaceModelArr, 'model');
463 | // 输出文件
464 | if($this->put($realFilePath, $finalContents)){
465 | echo $realFilePath.' 执行完毕 ';
466 | return true;
467 | }else{
468 | echo $realFilePath.' 执行失败 ';
469 | return false;
470 | }
471 |
472 | }
473 |
474 | /**
475 | * 替换模版中的标签
476 | * @param $filePath
477 | * @param $replaceArr
478 | * @param $type
479 | * @return bool
480 | * @todo 替换字符等操作应放置在各自的解析器中,路径应该再生成器中,从解析器中的 getReplaceOption() 中获得需要替换的内容
481 | */
482 | protected function replaceStubTags($filePath, $replaceArr, $type = '')
483 | {
484 | if(empty($replaceArr)){
485 | return false;
486 | }
487 | $type = !empty($type) ? $type : $this->makeType;
488 | $filePathName = $replaceArr['DummyFilePathName'] ?? '';
489 | $fileContents = file_get_contents($filePath);
490 | $baseReplace = str_replace(
491 | array_keys($replaceArr), array_values($replaceArr), $fileContents
492 | );
493 | if($this->makeType == 'res'){
494 | $modelTableRelation = $this->getModelTableRelation()[$filePathName] ?? [];
495 | $baseReplace = $this->getTableParser()->replaceTableEach($modelTableRelation, $baseReplace, $type);
496 | }
497 |
498 | return $baseReplace;
499 | }
500 |
501 | /**
502 | * 获取文件的命名空间
503 | * @param $rawFileName
504 | * @param string $type
505 | * @return string
506 | * @todo 各自的解析器负责组成各自文件的命名空间
507 | */
508 | protected function getFileNamespace($rawFileName, $type = '')
509 | {
510 | if(empty($type)) $type = $this->makeType;
511 | $rootNamespace = trim(app()->getNamespace(), '\\');
512 | $targetNamespace = 'get'.$this->getMakeType($type).'Namespace';
513 | $serviceRootNamespace = $this->$targetNamespace($rootNamespace);
514 | return $serviceRootNamespace.'\\'.str_replace('/'.class_basename($rawFileName), '', $rawFileName);
515 | }
516 |
517 | /**
518 | * 获取真实路径
519 | * @param string $name
520 | * @return string
521 | * @todo 各自的生成器负责组成各自文件的真实路径
522 | */
523 | protected function getPath($name)
524 | {
525 | // 如果存在 App\ 则去除 App\
526 | $name = str_replace_first(app()->getNamespace(), '', $name);
527 | return app('path').'/'.str_replace('\\', '/', $name).'.php';
528 | }
529 |
530 | /**
531 | * 检查文件是否存在
532 | * @param string $rawName
533 | * @param $type
534 | * @return bool
535 | * @todo 此处理方法应防止在生成器通用方法类(CommonMaker)中
536 | */
537 | protected function alreadyExists($rawName, $type)
538 | {
539 | return file_exists($this->getNeedMakeFilePath($rawName, $type));
540 | }
541 |
542 | /**
543 | * 根据原始名称获取对应类型的生成路径
544 | * @param $rawName
545 | * @param $type
546 | * @return string
547 | * @todo 此方法放置在各自的生成器中
548 | */
549 | protected function getNeedMakeFilePath($rawName, $type)
550 | {
551 | return $this->getPath($this->parseName($rawName, $type));
552 | }
553 |
554 | /**
555 | * 根据源文件中的相对命名空间获取完整准确的命名空间
556 | * @param string $name
557 | * @param string $type
558 | * @return string
559 | * @todo 此方法放置在搁置的解析器中
560 | */
561 | protected function parseName($name, $type)
562 | {
563 | $rootNamespace = app()->getNamespace();
564 | if (Str::startsWith($name, $rootNamespace)) {
565 | return $name;
566 | }
567 |
568 | if (Str::contains($name, '/')) {
569 | $name = str_replace('/', '\\', $name);
570 | }
571 |
572 | $rootNamespace = trim($rootNamespace, '\\');
573 | $suffix = '';
574 | switch ($type){
575 | case 'ctrl':
576 | $rootNamespace = $this->getControllersNamespace($rootNamespace);
577 | break;
578 | case 'res':
579 | // 如果书写生成文档时不包含后缀,则追加
580 | if(mb_strpos($name, 'Repository') === false){
581 | // 补充的后缀,方便简写
582 | $suffix = 'Repository';
583 | }
584 | $rootNamespace = $this->getRepositoryNamespace($rootNamespace);
585 | break;
586 | case 'serv':
587 | $rootNamespace = $this->getServiceNamespace($rootNamespace);
588 | break;
589 | case 'model':
590 | $rootNamespace = $this->getModelNamespace($rootNamespace);
591 | break;
592 | }
593 |
594 | return $this->parseName($rootNamespace.'\\'.$name.$suffix, $type);
595 | }
596 |
597 | /**
598 | * 获取控制器的根命名空间
599 | * @param string $rootNamespace
600 | * @return string
601 | * @todo 放置在 Controller 解析器的 getRootNamespace() 中
602 | */
603 | protected function getControllersNamespace($rootNamespace)
604 | {
605 | return $rootNamespace.'\Http\Controllers';
606 | }
607 |
608 | /**
609 | * 获取仓库类的根命名空间
610 | * @param string $rootNamespace
611 | * @return string
612 | * @todo 放置在 Repository 解析器的 getRootNamespace() 中
613 | */
614 | protected function getRepositoryNamespace($rootNamespace)
615 | {
616 | return $rootNamespace.'\Repository';
617 | }
618 |
619 | /**
620 | * 获取服务类的根命名空间
621 | * @param string $rootNamespace
622 | * @return string
623 | * @todo 放置在 Service 解析器的 getRootNamespace() 中
624 | */
625 | protected function getServiceNamespace($rootNamespace)
626 | {
627 | return $rootNamespace.'\Service';
628 | }
629 |
630 | /**
631 | * 获取模型类的根命名空间
632 | * @param string $rootNamespace
633 | * @return string
634 | * @todo 放置在 Model 解析器的 getRootNamespace() 中
635 | */
636 | protected function getModelNamespace($rootNamespace)
637 | {
638 | return $rootNamespace.'\Models';
639 | }
640 |
641 | /**
642 | * 初步解析源文件,将解析结果分组
643 | * @return array
644 | */
645 | private function parseRawData($raw)
646 | {
647 | // 匹配 - - 中的内容
648 | $pattern = "/-(.*?)-/";
649 | preg_match_all($pattern, $raw, $headings);
650 | $data = preg_split($pattern, $raw);
651 |
652 | if ($data[0] < 1) {
653 | $trash = array_shift($data);
654 | unset($trash);
655 | }
656 | // 清除第一个数组,-xxx-,只需要第二个就可以了
657 | array_shift($headings);
658 |
659 | return [end($headings), $data];
660 | }
661 |
662 | /**
663 | * 解析对应类型的内容到数组
664 | * 路由,控制器,仓库,服务
665 | * @param $type
666 | * @param $introRaw
667 | * @return mixed
668 | * @todo 此方法在入口方法用新的方式实现后就可以删除了
669 | */
670 | private function parseIntro($type, $introRaw)
671 | {
672 | switch ($type){
673 | case 'route':
674 | return $this->parseRouteIntro($introRaw);
675 | break;
676 | case 'ctrl':
677 | return $this->parseCtrlIntro($introRaw);
678 | break;
679 | case 'res':
680 | return $this->parseResIntro($introRaw);
681 | break;
682 | case 'serv':
683 | return $this->parseServIntro($introRaw);
684 | break;
685 | case 'table':
686 | return $this->parseTableIntro($introRaw);
687 | break;
688 | default:
689 | return [];
690 | break;
691 | }
692 | }
693 |
694 | /**
695 | * 通过初步分组的解析值再解析出生成路由用的格式化值
696 | * @param $introRaw
697 | * @return array
698 | * @todo 此方法交由 RouteParser 路由解析器类负责
699 | */
700 | private function parseRouteIntro($introRaw)
701 | {
702 | $pattern = "/(.*?),/";
703 | preg_match_all($pattern, $introRaw, $routes);
704 | array_shift($routes);
705 |
706 | $routeUrlPattern = "/(.*?)=>/";
707 |
708 | $routes = end($routes);
709 | $parseRoutes = [];
710 |
711 | foreach ($routes as $row){
712 | // 清除制表符
713 | $row = preg_replace('/[\t\r\n\s]/', '', $row);
714 | preg_match_all($routeUrlPattern, $row, $routeUrl);
715 | $routeActionAndName = preg_split($routeUrlPattern, $row);
716 |
717 | array_shift($routeUrl);
718 | $routeUrl = current(end($routeUrl));
719 |
720 | if ($routeActionAndName[0] < 1) {
721 | $trash = array_shift($routeActionAndName);
722 | unset($trash);
723 | }
724 |
725 | list($ctrlAndAction, $routeName) = explode('->', current($routeActionAndName));
726 |
727 | list($ctrl, $action) = explode('@', $ctrlAndAction);
728 |
729 | $parseRoutes[$ctrl][] = [
730 | 'url' => $routeUrl,
731 | 'action' => $action.'@'.$routeName,
732 | 'name' => $routeName
733 | ];
734 |
735 | }
736 |
737 | return $parseRoutes;
738 | }
739 |
740 | /**
741 | * 解析出 仓库 相关列表
742 | * @param $introRaw
743 | * @return array
744 | * @todo 由仓库类解析器负责
745 | */
746 | private function parseResIntro($introRaw)
747 | {
748 | $rst = $this->pregRaw(self::TYPE_EXPLODE, ',', $introRaw);
749 | return array_filter($rst);
750 | }
751 |
752 | /**
753 | * 解析 控制器 相关列表
754 | * @param $introRaw
755 | * @return array
756 | * @todo 由控制器类解析器负责
757 | */
758 | private function parseCtrlIntro($introRaw)
759 | {
760 | return [];
761 | }
762 |
763 | /**
764 | * 解析出 服务 相关列表
765 | * @param $introRaw
766 | * @return array
767 | * @todo 由服务类解析器负责
768 | */
769 | private function parseServIntro($introRaw)
770 | {
771 | $rst = array_filter($this->pregRaw(self::TYPE_EXPLODE, ',', $introRaw));
772 | return $rst;
773 | }
774 |
775 | /**
776 | * 解析出 数据表 相关列表
777 | * @param $introRaw
778 | * @return array
779 | * @todo 由表信息解析器负责
780 | */
781 | private function parseTableIntro($introRaw)
782 | {
783 | $rst = [];
784 | $baseParse = array_filter($this->pregRaw(self::TYPE_EXPLODE, '=>', $introRaw));
785 |
786 | foreach ($baseParse as $row){
787 |
788 | // 获取第一个 : 出现的位置并且提取该子串
789 | $titleStr = mb_substr($row, 0, mb_stripos($row, ':'));
790 |
791 | // 获得剩余的字符串, 需要把坐标+1, 排除掉第一个 :
792 | $row = mb_substr($row, mb_stripos($row, ':') + 1);
793 |
794 | // 将数据表标题分解到指定的变量
795 | list($tableModel, $tableName, $comment) = explode('|', $titleStr);
796 |
797 | // 去除表注释中包含的引号
798 | $comment = str_replace("'", '', $comment);
799 |
800 | // 获取表字段基础数组, 并去除空元素
801 | $baseFieldsStr = array_filter(explode('->', $row));
802 |
803 | // 循环表字段, 拆分为相应的数组
804 | $finalFields = [];
805 | foreach ($baseFieldsStr as $line){
806 | // 分割字段名和字段属性
807 | list($field, $detail) = explode('|', $line);
808 | // 分割字段属性
809 | $parseDetail = explode(',', $detail);
810 | $finalDetail = [];
811 | foreach ($parseDetail as $detailLine){
812 | list($lineName, $lineValue) = explode(':', $detailLine);
813 | // 如果属性值存在如 string@128 这种结构,那么留给后续生成的时候处理即可
814 | $finalDetail[$lineName] = $lineValue;
815 | }
816 | $finalFields[$field] = $finalDetail;
817 | }
818 | $rst[$tableModel] = [
819 | 'name' => $tableName,
820 | 'fields' => $finalFields,
821 | 'model' => $tableModel,
822 | 'comment' => $comment
823 | ];
824 | }
825 | $this->setModelTableRelation($rst);
826 | return $rst;
827 | }
828 |
829 | /**
830 | * 数据表解析后,放入到关联关系属性中,以便 res , model 生成循环时调用
831 | * @param $parsedData
832 | * @todo 由表信息解析器负责
833 | */
834 | protected function setModelTableRelation($parsedData)
835 | {
836 | $this->modelTableRelation = $parsedData;
837 | }
838 |
839 | /**
840 | * 获取解析后的表信息格式化值
841 | * @return array
842 | * @todo 由表信息解析器负责
843 | */
844 | protected function getModelTableRelation()
845 | {
846 | return $this->modelTableRelation;
847 | }
848 |
849 | }
850 |
--------------------------------------------------------------------------------
/Laravel/Maker/BaseMaker.php:
--------------------------------------------------------------------------------
1 | https://github.com/taoismCoder/LAM
8 | */
9 | class CommonMaker implements BaseMaker{
10 |
11 | public function __construct(){}
12 |
13 | /**
14 | * 获取模板文件路径
15 | * @return mixed
16 | */
17 | public function getStub()
18 | {
19 | // TODO: Implement getStub() method.
20 | }
21 |
22 | /**
23 | * 设置由解析器解析的数组
24 | * @param $parsed
25 | * @return self
26 | */
27 | public function setParsed($parsed)
28 | {
29 | // TODO: Implement setParsed() method.
30 | }
31 |
32 | /**
33 | * 获取解析器解析过的数组
34 | * @return array
35 | */
36 | public function getParsed()
37 | {
38 | // TODO: Implement getParsed() method.
39 | }
40 |
41 | /**
42 | * 开始生成文件
43 | * @return bool
44 | */
45 | public function makeFile()
46 | {
47 | // TODO: Implement makeFile() method.
48 | }
49 |
50 | /**
51 | * 检查文件是否存在
52 | * @param string $rawName
53 | * @return bool
54 | */
55 | protected function alreadyExists($rawName)
56 | {
57 | return file_exists($this->getNeedMakeFilePath($rawName));
58 | }
59 | }
--------------------------------------------------------------------------------
/Laravel/Maker/MigrationMaker.php:
--------------------------------------------------------------------------------
1 | setRawJsonFileName('exampleRaw.json')->makeMigration();
11 | * (new MigrationMaker())->setRawTxtArray($txtArr)->makeMigration();
12 | *
13 | * Class MigrationMaker
14 | * @package App\Helpers\LAM\Maker
15 | */
16 | class MigrationMaker extends CommonMaker{
17 | /**
18 | * 生成migration数据来源
19 | * json文件 或 txt转化的数组 二选一
20 | * @var
21 | */
22 | protected $dataFrom = 'json';
23 |
24 | /**
25 | * The Composer instance.
26 | *
27 | * @var \Illuminate\Support\Composer
28 | */
29 | protected $composer;
30 |
31 | /**
32 | * 表字段详情
33 | * @var
34 | */
35 | protected $tableDetail;
36 |
37 | /**
38 | * 原生json文件名(二选一)
39 | * 用来生成migration
40 | * @var
41 | */
42 | protected $rawJsonFileName;
43 |
44 | /**
45 | * 原生txt文件格式后的表数据数组(二选一)
46 | * 用来生成migration
47 | * @var
48 | */
49 | protected $rawTxtArray;
50 |
51 | /**
52 | * MigrationMaker constructor.
53 | */
54 | public function __construct() {
55 | }
56 |
57 | /**
58 | * @return \Illuminate\Foundation\Application|mixed|\Illuminate\Support\Composer
59 | */
60 | public function getComposer() {
61 | static $composer;
62 | if ($composer) {
63 | return $composer;
64 | }
65 | return $composer = app('Illuminate\Support\Composer')->setWorkingPath('../');
66 | }
67 |
68 | /**
69 | * @param $fileName
70 | * @return $this
71 | */
72 | public function setRawJsonFileName($fileName) {
73 | $this->rawJsonFileName = $fileName;
74 | $this->dataFrom = 'json';
75 | return $this;
76 | }
77 |
78 | /**
79 | * @return string
80 | */
81 | public function getRawJsonFileName() {
82 | return $this->rawJsonFileName;
83 | }
84 |
85 | /**
86 | * @param $rawArr
87 | * @return $this
88 | */
89 | public function setRawTxtArray($rawArr) {
90 | $this->rawTxtArray = $rawArr;
91 | $this->dataFrom = 'txtArr';
92 | return $this;
93 | }
94 |
95 | /**
96 | * @return array
97 | */
98 | public function getRawTxtArray() {
99 | return $this->rawTxtArray;
100 | }
101 |
102 | /**
103 | * 格式化数据表源文件
104 | * 相当于parser操作
105 | * @return $this
106 | */
107 | public function setTableDetail() {
108 | $sourceData = $this->getMigrationSourceData();
109 |
110 | foreach ($sourceData as $tableName => $tableInfo) {
111 | //dd($tableName, $tableInfo, $tableInfo['field']);
112 | $tableFieldDetail = "";
113 | $tableFieldDetail .= "$" . "table->engine = 'InnoDB';" . PHP_EOL;
114 |
115 | $IndexArr = [];
116 | foreach ($tableInfo['field'] as $tableField) {
117 | $tempArr = [];
118 | $tempIndexArr = [];
119 | $tableFields = explode(',', $tableField);
120 | //dd($tableFields);
121 | if (in_array('type:index', $tableFields) || in_array('type:unique', $tableFields)) {
122 | foreach ($tableFields as $field) {
123 | list($key, $val) = explode(':', $field);
124 | $tempIndexArr[$key] = $val;
125 | }
126 | $IndexArr[] = $tempIndexArr;
127 | continue;
128 | }
129 | foreach ($tableFields as $field) {
130 | list($key, $val) = explode(':', $field);
131 | $tempArr[$key] = $val;
132 | }
133 | //dump($tempArr);
134 |
135 | $tableFieldDetail .= $this->makeField($tempArr);
136 | }
137 | foreach ($IndexArr as $indexField) {
138 | $tableFieldDetail .= $this->makeField($indexField);
139 | }
140 | //dd($tableDetail);
141 |
142 | $tableInfo['field'] = $tableFieldDetail;
143 | $this->tableDetail[$tableName] = $tableInfo;
144 | }
145 | //dump($this->getTableDetail());
146 | //return $this;
147 | //dd($this->getTableDetail());
148 | }
149 |
150 | /**
151 | * @return mixed
152 | */
153 | public function getTableDetail() {
154 | return $this->tableDetail;
155 | }
156 |
157 | /**
158 | * 返回格式
159 | * [
160 | * "table_name_1" => [
161 | * "field" => [
162 | * 0 => "key:product_id,type:increments,comment:'自增ID'"
163 | * 1 => "key:product_name,type:string,length:128,default:'',comment:'产品名称'"
164 | * 2 => "key:product_price,type:decimal,length:6,places:2,default:0,unsigned:1,comment:'price'"
165 | * 3 => "key:created_at,type:integer,default:0,unsigned:1,comment:'添加时间'"
166 | * ],
167 | * "migration" => "create_table_name_1_table",
168 | * "model" => "ParentDir/ModelName",
169 | * "repository" => "ParentDir/RepositoryName"
170 | * ],
171 | *
172 | * "table_name_2" => [...]
173 | * ]
174 | *
175 | * @return array
176 | */
177 | public function getMigrationSourceData() {
178 | $distArr = [];
179 | if ($this->dataFrom == 'json') {
180 | $content = json_decode(Storage::get($this->getRawJsonFileName()), true);
181 | //dd($content, $content['table']);
182 | $distArr = $content['table'];
183 | } elseif ($this->dataFrom == 'txtArr') {
184 | $txtArr = $this->getRawTxtArray();
185 | foreach ($txtArr as $tableItem) {
186 | $tableName = $tableItem['name'];
187 | $tempArr = [
188 | "migration" => "create_" . $tableName . "_table",
189 | "model" => $tableItem['model'],
190 | "repository" => $tableItem['model'] . 'Repository',
191 | ];
192 |
193 | foreach ($tableItem['fields'] as $keyName => $keyInfo) {
194 | $tempKeyInfo = "key:" . $keyName;
195 | foreach ($keyInfo as $k => $v) {
196 | $tempKeyInfo .= "," . $k . ":" . $v;
197 | }
198 | $tempArr['field'][] = $tempKeyInfo;
199 | }
200 |
201 | $distArr[$tableName] = $tempArr;
202 | }
203 | }
204 |
205 | return $distArr;
206 | }
207 |
208 |
209 | public function makeMigration() {
210 | $this->setTableDetail();
211 | $this->writeMigration();
212 | return true;
213 | }
214 |
215 | /**
216 | * Write the migration file to disk.
217 | *
218 | * @return string
219 | */
220 | protected function writeMigration() {
221 | $path = $this->getMigrationPath();
222 |
223 | foreach ($this->getTableDetail() as $tableName => $tableDetail) {
224 | $file = pathinfo($this->create($tableDetail['migration'], $path, $tableName), PATHINFO_FILENAME);
225 | $this->getComposer()->dumpAutoloads();
226 | echo $file . '生成完成 ';
227 | }
228 |
229 | }
230 |
231 |
232 | protected function create($name, $path, $table) {
233 | if (class_exists($className = $this->getClassName($name))) {
234 | throw new InvalidArgumentException("A $className migration already exists.");
235 | }
236 | //dd($this->getClassName($name), class_exists($className = $this->getClassName($name)));
237 | $path = $this->getPath($name, $path);
238 |
239 | $stub = $this->getMigrationStub();
240 | //dd($stub);
241 |
242 | $this->put($path, $this->populateStub($name, $stub, $table));
243 |
244 | return $path;
245 | }
246 |
247 |
248 | /**
249 | * Get migration path (either specified by '--path' option or default location).
250 | *
251 | * @return string
252 | */
253 | public function getMigrationPath() {
254 | return app()->databasePath() . DIRECTORY_SEPARATOR . 'migrations';
255 | }
256 |
257 | /**
258 | * Get the full path name to the migration.
259 | *
260 | * @param string $name
261 | * @param string $path
262 | * @return string
263 | */
264 | protected function getPath($name, $path) {
265 | return $path . '/' . $this->getDatePrefix() . '_' . $name . '.php';
266 | }
267 |
268 | /**
269 | * Get the date prefix for the migration.
270 | *
271 | * @return string
272 | */
273 | protected function getDatePrefix() {
274 | return date('Y_m_d_His');
275 | }
276 |
277 | /**
278 | * 根据类型和需要生成的模版类型获取模版路径
279 | * @return string
280 | */
281 | protected function getMigrationStub() {
282 | return __DIR__ . '/../stubs/create.migration.plain.stub';
283 | }
284 |
285 | public function getClassName($name) {
286 | return studly_case($name);
287 | }
288 |
289 | /**
290 | * Write the contents of a file.
291 | *
292 | * @param string $path
293 | * @param string $contents
294 | * @param bool $lock
295 | * @return int
296 | */
297 | protected function put($path, $contents, $lock = false) {
298 | // 如果直接使用put方法创建文件,为了防止意外覆盖旧文件,则先备份旧文件
299 | if (file_exists($path)) {
300 | copy($path, $path . '_' . date('Y_m_d_H_i_s', time()));
301 | }
302 | return file_put_contents($path, $contents, $lock ? LOCK_EX : 0);
303 | }
304 |
305 | /**
306 | * Populate the place-holders in the migration stub.
307 | *
308 | * @param string $name
309 | * @param string $stub
310 | * @param string $table
311 | * @return string
312 | */
313 | public function populateStub($name, $stub, $table) {
314 | $fileContents = file_get_contents($stub);
315 | $fileContents = str_replace('DummyClass', $this->getClassName($name), $fileContents);
316 | $fileContents = str_replace('DummyTable', $table, $fileContents);
317 | $fileContents = str_replace('DummyDetail', $this->getTableDetail()[$table]['field'], $fileContents);
318 |
319 | return $fileContents;
320 | }
321 |
322 | /**
323 | * 根据格式化数据表源文件,生成migration语句
324 | * @param $fieldProps
325 | * @return string
326 | */
327 | protected function makeField($fieldProps) {
328 | if ($fieldProps['type'] == 'int') {
329 | $fieldProps['type'] = 'integer';
330 | } elseif ($fieldProps['type'] == 'tinyInt') {
331 | $fieldProps['type'] = 'tinyInteger';
332 | } elseif ($fieldProps['type'] == 'pk') {
333 | $fieldProps['type'] = 'increments';
334 | }
335 |
336 | $hasOneLengthField = [
337 | 'char', 'string'
338 | ];
339 |
340 | $hasTwoLengthField = [
341 | 'decimal', 'double', 'float'
342 | ];
343 |
344 | $numberField = [
345 | 'integer', 'bigInteger', 'decimal', 'float', 'double'
346 | ];
347 |
348 | $indexField = [
349 | 'index', 'unique'
350 | ];
351 |
352 | $field = '';
353 |
354 | if (in_array($fieldProps['type'], $indexField)){
355 | if (strpos($fieldProps['key'], '+')) {
356 | $indexField = implode('\',\'', explode('+', $fieldProps['key']));
357 | $fieldProps['key'] = "['" . $indexField . "']";
358 | $field .= "$" . "table->" . $fieldProps['type'] . "(" . $fieldProps['key'] . ")";
359 | } else {
360 | $field .= "$" . "table->" . $fieldProps['type'] . "('" . $fieldProps['key'] . "')";
361 | }
362 | } else {
363 | if (in_array($fieldProps['type'], $hasOneLengthField) && $fieldProps['length']) {
364 | $field .= "$" . "table->" . $fieldProps['type'] . "('" . $fieldProps['key'] . "'," . $fieldProps['length'] . ")";
365 | } elseif (in_array($fieldProps['type'], $hasTwoLengthField) && $fieldProps['length'] && $fieldProps['places']) {
366 | $field .= "$" . "table->" . $fieldProps['type'] . "('" . $fieldProps['key'] . "'," . $fieldProps['length'] . "," . $fieldProps['places'] . ")";
367 | } else {
368 | $field .= "$" . "table->" . $fieldProps['type'] . "('" . $fieldProps['key'] . "')";
369 | }
370 | }
371 | unset($fieldProps['type'], $fieldProps['key']);
372 |
373 | if (array_key_exists('default', $fieldProps)) {
374 | $field .= "->default(" . $fieldProps['default'] . ")";
375 | unset($fieldProps['default']);
376 | }
377 |
378 | if (array_key_exists('unsigned', $fieldProps) && in_array($fieldProps['type'], $numberField)) {
379 | if ($fieldProps['unsigned']) {
380 | $field .= "->unsigned()";
381 | }
382 | }
383 | unset($fieldProps['unsigned']);
384 |
385 | foreach ($fieldProps as $remainKey => $remainField) {
386 | if ($remainKey == 'comment') continue;
387 | $field .= "->" . $remainKey. "('" . $remainField . "')";
388 | }
389 |
390 | if (array_key_exists('comment', $fieldProps)) {
391 | $field .= "->comment(" . $fieldProps['comment'] . ")";
392 | }
393 |
394 | $field .= ";" . PHP_EOL;
395 | return $field;
396 | }
397 |
398 | }
--------------------------------------------------------------------------------
/Laravel/Maker/RepositoryMaker.php:
--------------------------------------------------------------------------------
1 | https://github.com/taoismCoder/LAM
8 | */
9 | class RepositoryMaker extends CommonMaker {
10 |
11 | public function __construct()
12 | {
13 | parent::__construct();
14 | }
15 |
16 | /**
17 | * 获取仓库类的根命名空间
18 | * @return string
19 | */
20 | protected function getRepositoryNamespace()
21 | {
22 | $rootNamespace = trim(app()->getNamespace(), '\\');
23 | return $rootNamespace.'\Repository';
24 | }
25 |
26 | public function makeFile()
27 | {
28 | foreach ($this->getParsed() as $filePathName){
29 | // 判断文件是否存在,其中包含根据服务名称获取要生成的文件路径
30 | $fileIsExists = $this->alreadyExists($filePathName);
31 | if(!$fileIsExists){
32 | // 获取 Repository 生成模版
33 | $stubPath = $this->getStub($this);
34 |
35 | // 根据服务名称获取需要生成的文件路径
36 | $needMakeFilePath = $this->getNeedMakeFilePath($filePathName);
37 | // 需要创建的文件夹路径
38 | $needMakeDir = $this->getPathDir($needMakeFilePath);
39 |
40 | // 替换模版中的名称及相关信息
41 | $finalContents = $this->replaceStubTags($stubPath, $this->getParsed());
42 | // 判断文件夹是否存在,不存在则创建文件夹
43 | if(!is_dir($needMakeDir)){
44 | mkdir($needMakeDir, 0755, true);
45 | }
46 | // 输出文件
47 | $this->put($needMakeFilePath, $finalContents);
48 | echo $needMakeFilePath.' 执行完毕 ';
49 | }
50 | }
51 |
52 | }
53 | }
--------------------------------------------------------------------------------
/Laravel/Maker/TableMaker.php:
--------------------------------------------------------------------------------
1 | https://github.com/taoismCoder/LAM
8 | */
9 | class TableMaker extends CommonMaker {
10 |
11 | public function __construct()
12 | {
13 | parent::__construct();
14 | }
15 | }
--------------------------------------------------------------------------------
/Laravel/Maker/exampleRaw.json:
--------------------------------------------------------------------------------
1 | {
2 | "table": {
3 | "erp_product": {
4 | "field": [
5 | "key:product_id,type:increments,comment:'自增ID'",
6 | "key:product_name,type:string,length:128,default:'',comment:'产品名称'",
7 | "key:product_price,type:decimal,length:6,places:2,default:0,unsigned:1,comment:'price'",
8 | "key:created_at,type:integer,default:0,unsigned:1,comment:'添加时间'"
9 | ],
10 | "migration": "create_erp_product_table",
11 | "model": "Erp/Product",
12 | "repository": "Erp/ProductRepository"
13 | },
14 | "erp_product_sku": {
15 | "field": [
16 | "key:product_sku_id,type:increments,comment:'自增ID'",
17 | "key:product_id,type:integer,default:'',comment:'产品ID'",
18 | "key:created_at,type:integer,default:0,unsigned:1,comment:'添加时间'"
19 | ],
20 | "migration": "create_erp_product_sku_table",
21 | "model": "Erp/ErpProductSku",
22 | "repository": "Erp/ErpProductSkuRepository"
23 | }
24 | }
25 | }
--------------------------------------------------------------------------------
/Laravel/Parser/BaseParser.php:
--------------------------------------------------------------------------------
1 | https://github.com/taoismCoder/LAM
8 | */
9 | class CommonParser implements BaseParser {
10 |
11 | /**
12 | * 基础模板名称
13 | * @var string
14 | */
15 | protected $baseStubName = '';
16 |
17 | /**
18 | * 要解析的数组
19 | * @var array
20 | */
21 | protected $rawIntro = [];
22 | /**
23 | * 要返回的解析结果数组
24 | * @var array
25 | */
26 | protected $result = [];
27 |
28 | public function __construct(){}
29 |
30 | /**
31 | * 清除制表符
32 | * @param $raw
33 | * @param string $custom
34 | * @return mixed
35 | */
36 | protected function clearTabs($raw, $custom = '\t\r\n\s')
37 | {
38 | $raw = preg_replace('/['.$custom.']/', '', $raw);
39 | return $raw;
40 | }
41 |
42 | /**
43 | * 下划线命名法转驼峰命名法
44 | * @param $str
45 | * @return mixed
46 | */
47 | protected function UnderlineToCamelCase($str)
48 | {
49 | // 去除空格(单词首字母大写(将下划线替换为空格))
50 | return preg_replace('# #', '', ucwords(str_replace('_', ' ', $str)));
51 | }
52 |
53 | /**
54 | * 获取文件内容
55 | * @param $path
56 | * @return string
57 | */
58 | protected function getFileContents($path)
59 | {
60 | return file_get_contents($path);
61 | }
62 |
63 | /**
64 | * Write the contents of a file.
65 | *
66 | * @param string $path
67 | * @param string $contents
68 | * @param bool $lock
69 | * @return int
70 | */
71 | protected function put($path, $contents, $lock = false)
72 | {
73 | // 如果直接使用put方法创建文件,为了防止意外覆盖旧文件,则先备份旧文件
74 | if(file_exists($path)){
75 | copy($path, $path.'_'.date('Y_m_d_H_i_s', time()));
76 | }
77 | return file_put_contents($path, $contents, $lock ? LOCK_EX : 0);
78 | }
79 |
80 | /**
81 | * 追加方法函数到文件内容中
82 | * @param $path
83 | * @param array $funcArr
84 | * @param $contents
85 | * @param $funcStubContent
86 | * @param bool $isBackup
87 | * @return string
88 | */
89 | protected function appendFuncToFileContent($path, $funcArr = [], $contents, $funcStubContent, $isBackup = true)
90 | {
91 | $insertContents = '';
92 | foreach ($funcArr as $_action){
93 | list($_funcName, $_viewName) = explode('@', $_action);
94 | // 如果为首页方法名,则 view 名称增加默认 .index
95 | if($_funcName == 'index'){
96 | $_viewName .= '.index';
97 | }
98 | $insertContents .= str_replace(['FunctionName', 'ViewName'], [$_funcName, $_viewName], $funcStubContent);
99 | }
100 | $finalContents = str_replace('//', $insertContents, $contents);
101 | if ($isBackup){
102 | return $this->put($path, $finalContents);
103 | }else{
104 | return file_put_contents($path, $finalContents);
105 | }
106 | }
107 |
108 | /**
109 | * 指定位置插入字符串
110 | * @param string $str 原字符串
111 | * @param int $i 插入位置
112 | * @param string $insertStr 插入字符串
113 | * @return string 处理后的字符串
114 | */
115 | function insertToStr($str, $i, $insertStr){
116 | //TODO
117 | return $str;
118 | }
119 |
120 | /**
121 | * 根据类型和需要生成的模版类型获取模版路径
122 | * @param $type
123 | * @param string $stubType
124 | * @return string
125 | */
126 | protected function getStub($type, $stubType = 'plain')
127 | {
128 | return __DIR__ . '/../stubs/' .$type.'.'.$stubType.'.stub';
129 | }
130 |
131 | /**
132 | * 获取默认模板的内容
133 | * @return \PHPUnit_Framework_Constraint_FileExists
134 | */
135 | protected function getStubContents()
136 | {
137 | if ($this->baseStubName){
138 | return fileExists($this->getStub($this->baseStubName));
139 | }
140 | }
141 |
142 | /**
143 | * 根据正则匹配对应的结果
144 | * @param string $parseType
145 | * @param $pattern
146 | * @param $raw
147 | * @return mixed
148 | */
149 | protected function pregRaw($parseType = 'preg', $pattern, $raw)
150 | {
151 | $parseRst = [];
152 |
153 | if ($parseType == 'preg'){
154 | preg_match_all($pattern, $raw, $parseRst);
155 | array_shift($parseRst);
156 | }elseif($parseType == 'explode'){
157 | $raw = $this->clearTabs($raw);
158 | $parseRst = explode($pattern, $raw);
159 | }
160 |
161 | return $parseRst;
162 | }
163 |
164 | /**
165 | * 获取文件的文件夹路径
166 | * @param $realPath
167 | * @return string
168 | */
169 | protected function getPathDir($realPath)
170 | {
171 | return mb_substr($realPath, 0, strripos($realPath, '/'));
172 | }
173 |
174 | /**
175 | * 设置要解析的内容
176 | * @param $intro
177 | * @return self
178 | */
179 | public function setRawIntro($intro)
180 | {
181 | $this->rawIntro = $intro;
182 | return $this;
183 | }
184 |
185 | /**
186 | * 获取解析后的数组
187 | * @return array
188 | */
189 | public function getResult()
190 | {
191 | return $this->result;
192 | }
193 |
194 | /**
195 | * 设置解析后的值
196 | * @param $result
197 | * @return array
198 | */
199 | public function setResult($result)
200 | {
201 | $this->result = $result;
202 | }
203 |
204 | /**
205 | * 获取设置的解析内容
206 | * @return mixed
207 | */
208 | public function getRawIntro()
209 | {
210 | return $this->rawIntro;
211 | }
212 | }
--------------------------------------------------------------------------------
/Laravel/Parser/RepositoryParser.php:
--------------------------------------------------------------------------------
1 | https://github.com/taoismCoder/LAM
9 | */
10 | class RepositoryParser extends CommonParser
11 | {
12 | /**
13 | * 仓库模版默认名称
14 | * @var string
15 | */
16 | protected $baseStubName = 'res';
17 |
18 | /**
19 | * RepositoryParser constructor.
20 | */
21 | public function __construct()
22 | {
23 | parent::__construct();
24 | }
25 |
26 | /**
27 | * 开始解析
28 | */
29 | public function beginParser()
30 | {
31 | $replaceArr = [];
32 | foreach ($this->getRawIntro() as $filePathName){
33 | // 根据服务名称获取需要生成的文件路径
34 | $needMakeFilePath = $this->getNeedMakeFilePath($filePathName);
35 | // 需要创建的文件夹路径
36 | $needMakeDir = $this->getPathDir($needMakeFilePath);
37 | // 基础类名 或 对应的 Model 名称
38 | $baseClassName = class_basename($filePathName);
39 | // 当前文件完整的类名
40 | $dummyClassName = $baseClassName.$this->getMakeType();
41 |
42 | // 需要替换 model 模版的 tag 及对应的值
43 | $tableModelNamespace = $this->getFileNamespace($filePathName, 'model');
44 | $replaceModelArr = [
45 | 'DummyNamespace' => $tableModelNamespace,
46 | 'DummyClassName' => $baseClassName,
47 | 'DummyUsePath' => $tableModelNamespace.'\\'.$baseClassName,
48 | 'DummyFilePathName' => $filePathName
49 | ];
50 | // 生成相关 Model, 传入生成的 Model 路径
51 | $this->makeTableModel($this->getNeedMakeFilePath($filePathName, 'model'), $replaceModelArr);
52 |
53 | // 需要替换 res 模版的 tag 及对应的值
54 | $replaceArr[] = [
55 | 'DummyNamespace' => $this->getFileNamespace($filePathName),
56 | 'DummyClassName' => $dummyClassName,
57 | 'DummyUsePath' => $this->getFileNamespace($filePathName).'\\'.$dummyClassName,
58 | 'DummyModelName' => $baseClassName,
59 | 'DummyModelUsePath' => $this->getFileNamespace($filePathName, 'model').'\\'.$baseClassName,
60 | 'DummyFilePathName' => $filePathName,
61 | 'DummyMore' => ''
62 | ];
63 |
64 | }
65 | $this->setResult($replaceArr);
66 | return $this;
67 | }
68 | }
--------------------------------------------------------------------------------
/Laravel/Parser/TableParser.php:
--------------------------------------------------------------------------------
1 | https://github.com/taoismCoder/LAM
8 | */
9 | class TableParser extends CommonParser{
10 |
11 | public function __construct()
12 | {
13 | parent::__construct();
14 | }
15 |
16 | /**
17 | * 模版中循环语句替换对应的数据
18 | * @example
19 | * DummyTBForeach
20 | * if (Request::has('DummyTable.DTableFiled')) {
21 | * $query = $query->where('DummyTable.DTableFiled', '=', Request::input('DTableFiled'));
22 | * }
23 | * EndDummyTBForeach
24 | * @param array $tableArr 要生成的数据表信息
25 | * @param $replacedIntro
26 | * @param $type
27 | * @return string
28 | */
29 | public function replaceTableEach($tableArr, $replacedIntro, $type = '')
30 | {
31 | // 获取DummyTBForeach区间的内容模版,暂时只支持一个 DummyTBForeach
32 | $pa = '/DummyTBForeach.*?EndDummyTBForeach/s';
33 | preg_match($pa, $replacedIntro, $match);
34 |
35 | // 获取表名
36 | $tableName = $tableArr['name'] ?? '';
37 | // 获取表中的所有字段
38 | $tableFields = $tableArr['fields'] ?? [];
39 | // 获取表注释
40 | $tableComment = $tableArr['comment'] ?? '';
41 |
42 | // 去除循环模版标记
43 | $temp = str_replace(['EndDummyTBForeach','DummyTBForeach'], '', $match)[0];
44 | $finalFieldsRow = '';
45 | $tablePk = '';
46 |
47 | foreach ($tableFields as $k => $v){
48 | $_fieldType = '';
49 |
50 | if(isset($v['pk']) && $v['pk']){
51 | $tablePk = $k;
52 | $_fieldType = 'int';
53 | }
54 |
55 | if($type == 'res' && $k == 'created_at'){
56 | continue;
57 | }
58 |
59 | if(isset($v['type'])){
60 | $_fieldType = str_replace('@', ' ',$v['type']);
61 | }
62 |
63 | $_temp = [
64 | 'DTableUTCCField' => $this->UnderlineToCamelCase($k),
65 | 'DummyTable' => $tableName,
66 | 'DTableField' => $k,
67 | 'DTFieldType' => $_fieldType
68 | ];
69 |
70 | if(!isset($v['comment'])){
71 | var_dump($k);
72 | }else{
73 | $_temp['DTFieldComment'] = str_replace("'", '', $v['comment']);
74 | }
75 |
76 | $finalFieldsRow .= str_replace(array_keys($_temp), array_values($_temp), $temp);
77 | }
78 |
79 | // 将生成的所有字段的基础查询语句输出到模版中
80 | $replacedIntro = preg_replace($pa, $finalFieldsRow, $replacedIntro);
81 | // 将循环外部的 DummyTable 替换成表名
82 | $modelReplace = [
83 | 'DummyTable' => $tableName,
84 | 'DummyTPk' => $tablePk,
85 | 'DummyTComment' => $tableComment,
86 | ];
87 | $replacedIntro = str_replace(array_keys($modelReplace), array_values($modelReplace), $replacedIntro);
88 |
89 | // 将所有字段替换为内容模版并输出到内容区间中
90 | return $replacedIntro;
91 | }
92 |
93 | public function beginParser()
94 | {
95 | $intro = $this->getRawIntro();
96 | dd($intro);
97 | // 解析出表数组数据
98 | // 得到表名
99 | // 生成 Migration 需要的数组结构
100 | // 主类中的 dispatcher 会把解析后的数组交由对应的 Maker 类接手处理
101 | $result = '';
102 | $this->setResult($result);
103 | return $this;
104 | }
105 |
106 | /**
107 | * 获取表名
108 | * @return mixed
109 | */
110 | public function getTableName()
111 | {
112 | return $this->getTableArray()['name'];
113 | }
114 |
115 | /**
116 | * 获取表数组
117 | * @return array
118 | */
119 | public function getTableArray()
120 | {
121 | return [];
122 | }
123 | }
--------------------------------------------------------------------------------
/Laravel/Scalpel/BaseScalpel.php:
--------------------------------------------------------------------------------
1 | Some/Something@index -> some.something,
3 | post@some/something/upgrade => Some/Something@upgrade -> some.something.upgrade,
4 | -srv-
5 | Some/SomeService,
6 | -res-
7 | Some/TableOne,
8 | -model-
9 | Some/TableOne,
10 | -table-
11 | =>Some/TableOne|table_one|'产品表':
12 | ->table_one_id|comment:'主键ID',type:pk
13 | ->table_one_name|comment:'表2字段名称',type:string,length:128,default:''
14 | ->created_at|comment:'创建时间',type:int,default:0
15 | ->updated_at|comment:'更新时间',type:int,default:0
16 | ->deleted_at|comment:'删除时间',type:int,default:0
17 | =>Some/TableTwo|table_two|'表2':
18 | ->table_two_id|comment:'主键ID',type:pk
19 | ->table_two_name|comment:'表2字段名称',type:string,length:128,default:''
20 | ->table_two_other|type:double,length:10,place:2,default:0
21 | ->table_two_fk|type:foreign,references:table_one_id,on:table_one
22 | ->status|comment:'状态',type:tinyInt,default:0
23 | ->created_at|comment:'创建时间',type:int,default:0
24 | ->updated_at|comment:'更新时间',type:int,default:0
25 | ->table_two_name|type:unique
26 | ->status+created_at|type:index
27 |
--------------------------------------------------------------------------------
/Laravel/stubs/create.migration.plain.stub:
--------------------------------------------------------------------------------
1 | DTableField;
34 | }
35 | EndDummyTBForeach
36 |
37 | }
--------------------------------------------------------------------------------
/Laravel/stubs/res.plain.stub:
--------------------------------------------------------------------------------
1 | model;
32 | DummyTBForeach
33 | // DTFieldComment
34 | if (Request::has('DTableField')) {
35 | $query = $query->where('DummyTable.DTableField', '=', Request::input('DTableField'));
36 | }
37 | EndDummyTBForeach
38 | if (Request::has('created_at')) {
39 | $arrTime = explode('|', Request::input('created_at'));
40 | if (count($arrTime) == 2) {
41 | $startTime = strtotime($arrTime[0]);
42 | $endTime = strtotime($arrTime[1] . ' 23:59:59');
43 | $query = $query->where('DummyTable.created_at', '>', $startTime)->where('DummyTable.created_at', '<', $endTime);
44 | }
45 | }
46 |
47 | return $query;
48 | }
49 |
50 | /**
51 | * 根据ID获取记录
52 | * @param $DummyTPk
53 | * @return mixed
54 | */
55 | public function getById($DummyTPk)
56 | {
57 | return $this->model->where('DummyTPk', '=', $DummyTPk)->first();
58 | }
59 | }
--------------------------------------------------------------------------------
/Laravel/stubs/serv.plain.stub:
--------------------------------------------------------------------------------
1 | parse(Storage::get('exampleRaw.txt'))->makeFiles();
14 | ```
15 |
16 | ### 推荐的结构分层
17 | -. Controller
18 | -. Service
19 | -. Repository
20 | -. Models
21 |
22 | ### 分支说明
23 | * master: 最新代码会在master,所以master是最新的,但是不保证稳定。且有一些公司自用的东西,所以提交记录可以参考,但不能直接使用master分支。
24 | * release:是相对稳定的最新代码分支,也是LAM对外打包的分支
25 | * 其它分支:根据开发需要,大的版本会以版本号为分支名,打一些临时分支。
26 |
27 | ### 最新Release (目前正在着手重构代码,未来发布1.0基础版本)
28 | * [Release](https://github.com/taoismCoder/LAM/releases)
29 |
30 | ### 其它LINKS
31 | * 我想查找详细的文档资料 => [文档/手册](https://github.com/taoismCoder/LAM/wiki)
32 | * [如何使用LAM](https://github.com/taoismCoder/LAM/wiki/LAM%E4%BD%BF%E7%94%A8%E6%89%8B%E5%86%8C)
33 | * [LAM开发实例](https://github.com/taoismCoder/LAM/wiki/LAM_Example)
34 | * 我要反馈问题 => [Issues](https://github.com/taoismCoder/LAM/issues)
35 |
36 | ## About
37 |
38 | @version v0.0.1
39 | @author TaoismCoder
40 | @license MIT
41 |
42 | ## Contact
43 |
44 | @问题反馈 https://github.com/taoismCoder/LAM/issues (推荐)
45 | @QQ群 283932057
46 |
47 | ## Contributors List 贡献者
48 |
49 | [Contributors Details](https://github.com/taoismCoder/LAM/graphs/contributors)
50 |
51 | ## 新版待定 idea
52 | - YAML ?
53 | - Vue2 ?
54 | - Python 客户端界面 ?
55 | - composer laravel 模块 ?
56 |
57 | ## 暂定计划
58 | - Web在线生成相关代码
59 | - 基于 vue-electron GUI
60 |
--------------------------------------------------------------------------------