目的
代码自动生成工具(Alpaca-Builder)目的用来快速的编写代码,减少一些重复的工作。使用Alpaca-Laravel框架开发项目时,可以利用代码生成工具,快速的生成代码,减少工作时间,提高工作效率。
Alpaca-Spa-Laravel 是 前后端分离 开发的一个后台管理系统的DEMO。Laravel用来实现后端功能,Alpaca-Spa用来实现前端功能,前后端之间通过Json交换数据。
详情请阅读《Alpaca-Laravel 框架(一) --- 概述,前后分离的后台管理系统》。
Alpaca-Builder使用灵活的模版格式来配置代码生成文件,如果现有的代码模版不能满足需求,只需要修改代码模版,或者新增代码模版即可。
主要内容
主要功能是根据输入的数据库表名声生成一下内容:
1 生成后端实体类
2 生成后端控制器
3 生成后端路由
4 生成前端JS控制器
5 生成前端编辑页面
6 生成前端列表页面
7 生成配置接口url
8 选择是否复制到对应页面
访问方式,浏览器中输入地址:你的域名\builder
(注意: 只有当配置文件中APP_ENV=local时,才容许访问)
实现原理
- 根据输入的数据库表名称,查询数据库,获取表结构,字段、字段类型、字段注释等。
- 格式化字段内容
- 读取相应的代码模版,将字段内容填充到代码模版中,输出文件,生成完毕
一个简单的视图模版引擎
你当然直接使用Laravel的视图模板, 在这里,我们编写一个简单的视图模版引擎,
主要的功能能,读取指定模版文件,将数据渲染到模版指定位置。
代码如下:
<?php
namespace Builder\View;
/**
* View - 视图模板
* @author Chengcheng
* @date 2017-2-27 15:50:00
*/
class View
{
//单例
private static $instance = null;
//数据
private $data;
//模板
private $template;
//布局模板
public $layout = null;
/**
* 创建视图
* @author Chengcheng
* @param string $template 视图模板名字
* @param array $data 视图数据
* @date 2016年11月5日 14:47:40
* @return static
*/
public static function tbl($template, $data = null)
{
//实例化新的对象
$newTpl = new static;
//设置视图
$newTpl->template = __DIR__ . '/Template/' . $template . ".php";
//数据
$newTpl->data = $data;
//layout
$newTpl->layout = static::layoutTpl();
//返回
return $newTpl;
}
/**
* 创建layout视图
* @author Chengcheng
* @param string $template 视图模板名字
* @param array $data 视图数据
* @date 2016年11月5日 14:47:40
* @return static
*/
public static function layoutTpl($template = 'layout', $data = null)
{
//实例化新的对象
$newTpl = new static;
//设置视图
$newTpl->template = __DIR__ . '/Template/' . $template . ".php";
//数据
$newTpl->data = $data;
//返回
return $newTpl;
}
/**
* 创建视图
* @author Chengcheng
* @param null $data
* @return null|string
* @throws \Exception
*/
public function html($data = null)
{
//加载自己的模板
if (!empty($this->data)) {
foreach ($this->data as $key => $value) {
$this->$key = $value;
}
}
//参数中的数据
if (!empty($data)) {
foreach ($data as $key => $value) {
$this->$key = $value;
}
}
//读取模板中的信息
$html = null;
try {
ob_start();
require $this->template;
$html = ob_get_clean();
} catch (\Exception $ex) {
ob_end_clean();
throw $ex;
}
//加载layout的模板
if ($this->layout) {
$html = $this->layout->html(['content' => $html]);
}
//返回信息
return $html;
}
}
交互页面
编写一个交互页面,用来接受用户输入的数据库表名,生成的内容等。
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,initial-scale=1,user-scalable=0">
<title>Alpaca - Builder</title>
<link rel="stylesheet" href="//g.alicdn.com/msui/sm/0.6.2/css/sm.min.css">
<link rel="stylesheet" href="//g.alicdn.com/msui/sm/0.6.2/css/??sm.min.css,sm-extend.min.css">
<script type='text/javascript' src='//g.alicdn.com/sj/lib/zepto/zepto.min.js' charset='utf-8'></script>
<script type='text/javascript' src='//g.alicdn.com/msui/sm/0.6.2/js/sm.min.js' charset='utf-8'></script>
<script type='text/javascript' src='//g.alicdn.com/msui/sm/0.6.2/js/??sm.min.js,sm-extend.min.js' charset='utf-8'></script>
<style>
.line-right {
text-align: right;
}
</style>
</head>
<body ontouchstart>
<div class="page-group">
<div class="page page-current">
<!-- 你的html代码 -->
<header class="bar bar-nav">
<a class="button button-link button-nav pull-left" href="" data-transition='slide-out'>
<span class="icon icon-left"></span>
返回
</a>
<h1 class="title">Alpaca-Builder</h1>
</header>
<nav class="bar bar-tab">
<a class="tab-item active" href="#">
<span class="icon icon-home"></span>
<span class="tab-label">首页</span>
</a>
<a class="tab-item" href="#">
<span class="icon icon-me"></span>
<span class="tab-label">我</span>
</a>
<a class="tab-item" href="#">
<span class="icon icon-settings"></span>
<span class="tab-label">设置</span>
</a>
</nav>
<div class="content">
<form action="/builder/create/builder" id="sub-form">
<div class="content-block-title">配置</div>
<div class="list-block">
<ul>
<!-- Text inputs -->
<li>
<div class="item-content">
<div class="item-media"><i class="icon icon-form-name"></i></div>
<div class="item-inner">
<div class="item-title label">数据表名称</div>
<div class="item-input">
<input type="text" placeholder="请输入数据表名称" name="table_name">
</div>
</div>
</div>
</li>
<li>
<div class="item-content">
<div class="item-media"><i class="icon icon-form-email"></i></div>
<div class="item-inner">
<div class="item-title label">表名前缀</div>
<div class="item-input">
<input type="text" placeholder="E-mail" value="tb_" name="table_prefix">
</div>
</div>
</div>
</li>
<li>
<div class="item-content">
<div class="item-media"><i class="icon icon-form-email"></i></div>
<div class="item-inner">
<div class="item-title label">中文名称</div>
<div class="item-input">
<input type="text" placeholder="中文名称" value="" name="table_name_cn">
</div>
</div>
</div>
</li>
<li>
<div class="item-content">
<div class="item-media"><i class="icon icon-form-email"></i></div>
<div class="item-inner">
<div class="item-title label">二级模块</div>
<div class="item-input">
<input type="text" placeholder="二级模块" value="" name="sub_module_name">
</div>
</div>
</div>
</li>
<li>
<div class="item-content">
<div class="item-media"><i class="icon icon-form-email"></i></div>
<div class="item-inner">
<div class="item-title label">后端模块</div>
<div class="item-input">
<input type="text" placeholder="后端模块" value="manage" name="b_module_name">
</div>
</div>
</div>
</li>
<li>
<div class="item-content">
<div class="item-media"><i class="icon icon-form-email"></i></div>
<div class="item-inner">
<div class="item-title label">前端模块</div>
<div class="item-input">
<input type="text" placeholder="前端模块" value="admin" name="f_module_name">
</div>
</div>
</div>
</li>
</ul>
</div>
<div class="content-block-title">后端</div>
<div class="list-block">
<ul>
<!-- Switch (Checkbox) -->
<li>
<div class="item-content">
<div class="item-media"><i class="icon icon-form-toggle"></i></div>
<div class="item-inner">
<div class="item-title label">生成Model</div>
<div class="item-input line-right">
<label class="label-switch">
<input type="checkbox" checked name="is_init_model">
<div class="checkbox"></div>
</label>
</div>
</div>
</div>
</li>
<li>
<div class="item-content">
<div class="item-media"><i class="icon icon-form-toggle"></i></div>
<div class="item-inner">
<div class="item-title label">生成Controller</div>
<div class="item-input line-right">
<label class="label-switch">
<input type="checkbox" checked name="is_init_b_controller">
<div class="checkbox"></div>
</label>
</div>
</div>
</div>
</li>
<li>
<div class="item-content">
<div class="item-media"><i class="icon icon-form-toggle"></i></div>
<div class="item-inner">
<div class="item-title label">配置Router</div>
<div class="item-input line-right">
<label class="label-switch">
<input type="checkbox" checked name="is_init_b_router">
<div class="checkbox"></div>
</label>
</div>
</div>
</div>
</li>
</ul>
</div>
<div class="content-block-title">前端</div>
<div class="list-block">
<ul>
<li>
<div class="item-content">
<div class="item-media"><i class="icon icon-form-toggle"></i></div>
<div class="item-inner">
<div class="item-title label">List页面</div>
<div class="item-input line-right">
<label class="label-switch">
<input type="checkbox" checked name="is_init_list">
<div class="checkbox"></div>
</label>
</div>
</div>
</div>
</li>
<li>
<div class="item-content">
<div class="item-media"><i class="icon icon-form-toggle"></i></div>
<div class="item-inner">
<div class="item-title label">Edit页面</div>
<div class="item-input line-right">
<label class="label-switch">
<input type="checkbox" checked name="is_init_edit">
<div class="checkbox"></div>
</label>
</div>
</div>
</div>
</li>
<li>
<div class="item-content">
<div class="item-media"><i class="icon icon-form-toggle"></i></div>
<div class="item-inner">
<div class="item-title label">前端Controller</div>
<div class="item-input line-right">
<label class="label-switch">
<input type="checkbox" checked name="is_init_f_controller">
<div class="checkbox"></div>
</label>
</div>
</div>
</div>
</li>
<li>
<div class="item-content">
<div class="item-media"><i class="icon icon-form-toggle"></i></div>
<div class="item-inner">
<div class="item-title label">配置接口url</div>
<div class="item-input line-right">
<label class="label-switch">
<input type="checkbox" checked name="is_init_inter">
<div class="checkbox"></div>
</label>
</div>
</div>
</div>
</li>
</ul>
</div>
<div class="content-block-title">自动复制到对应目录</div>
<div class="list-block">
<ul>
<li>
<div class="item-content">
<div class="item-media"><i class="icon icon-form-toggle"></i></div>
<div class="item-inner">
<div class="item-title label">复制到对应目录</div>
<div class="item-input line-right">
<label class="label-switch">
<input type="checkbox" checked name="is_auto_copy">
<div class="checkbox"></div>
</label>
</div>
</div>
</div>
</li>
</ul>
</div>
<div class="content-block">
<div class="row">
<div class="col-50"><a href="#" class="button button-big button-fill button-danger">取消</a></div>
<div class="col-50"><a href="#" class="button button-big button-fill button-success" onclick="submit()">提交</a></div>
</div>
</div>
</form>
</div>
</div>
</div>
<script>
var submit = function () {
$('#sub-form').submit();
}
</script>
</body>
</html>
解析表结构
根据输入的表名称,查询数据库,获取表结构,解析字段内容,然后格式化字段内容。
public function builder()
{
$table_name = request()->input('table_name');
$table_prefix = request()->input('table_prefix', '');
$table_name_cn = request()->input('table_name_cn');
$sub_module_name = request()->input('sub_module_name');
$b_module_name = request()->input('b_module_name', 'manage');
$f_module_name = request()->input('f_module_name', 'admin');
$is_init_model = request()->input('is_init_model');
$is_init_b_controller = request()->input('is_init_b_controller');
$is_init_b_router = request()->input('is_init_b_router');
$is_init_list = request()->input('is_init_list');
$is_init_edit = request()->input('is_init_edit');
$is_init_f_controller = request()->input('is_init_f_controller');
$is_init_inter = request()->input('is_init_inter');
$is_auto_copy = request()->input('is_auto_copy', false);
if (empty($table_name)) {
die('Table Name Is Null!');
}
if (empty($table_name_cn)) {
$table_name_cn = $table_name;
}
$orgName = $table_name;
$table = str_replace(' ', '', $orgName);
$className = ucwords(str_replace(array('.', '-', '_'), ' ', $table));
$moduleName = explode(',', $className)[0];
if (!empty($sub_module_name)) {
$moduleName = $sub_module_name;
}
$b_module_name = ucwords($b_module_name);
$f_module_name = ucwords($f_module_name);
$b_module_name_lc = lcfirst($b_module_name);
$f_module_name_lc = lcfirst($f_module_name);
$className = str_replace(' ', '', $className);
$classNameLc = lcfirst($className);
$moduleNameLc = lcfirst($moduleName);
$result = DB::select("SHOW FULL COLUMNS FROM " . $table_prefix . $table);
$fields = [];
foreach ($result as $r) {
if (in_array($r->Field, $this->ignoreField)) {
continue;
}
$field = [];
$field['db_type'] = $r->Type;
$field['field'] = $r->Field;
$field['comment'] = $r->Comment;
$field['in_type'] = 'string';
$field['in_name'] = $r->Comment;
$comment = str_replace(',', ',', $r->Comment);
$comment = preg_replace("# #", '', $comment);
$array = explode(',', $comment);
$field['in_common'] = $array[0];
if (!empty($array[1])) {
$typeContent = explode('|', $array[1]);
switch ($typeContent[0]) {
case '枚举':
$field['in_type'] = 'enum';
$in_value = [];
foreach ($typeContent as $index => $typeContentValue) {
if ($index == 0) {
continue;
}
$key = explode('-', $typeContentValue);
$value = [];
$value['value'] = $key[0];
$value['label'] = (isset($key[1]) ? $key[1] : $key[0]);
$value['key'] = $field['field'] . "_" . (isset($key[2]) ? $key[2] : (isset($key[1]) ? $key[1] : $key[0]));
$value['common'] = '//' . $field['in_common'] . ":" . (isset($key[1]) ? $key[1] : $key[0]);
$value['const'] = 'const ' . strtoupper($value['key']) . ' = ' . $key[0] . '; ' . $value['common'];
array_push($in_value, $value);
}
$field['in_value'] = $in_value;
break;
case '开关':
$field['in_type'] = 'switch';
$in_value = [];
foreach ($typeContent as $index => $typeContentValue) {
if ($index == 0) {
continue;
}
$key = explode('-', $typeContentValue);
$value = [];
$value['value'] = $key[0];
$value['label'] = (isset($key[1]) ? $key[1] : $key[0]);
$value['key'] = $field['field'] . "_" . (isset($key[2]) ? $key[2] : (isset($key[1]) ? $key[1] : $key[0]));
$value['common'] = '//' . $field['in_common'] . ":" . (isset($key[1]) ? $key[1] : $key[0]);
$value['const'] = 'const ' . strtoupper($value['key']) . ' = ' . $key[0] . '; ' . $value['common'];
array_push($in_value, $value);
}
$field['in_value'] = $in_value;
break;
case '富文本':
$field['in_type'] = 'rtext';
break;
default: {
$field['in_type'] = 'string';
break;
}
}
}
array_push($fields, $field);
}
}
生成代码
读取相应的代码模版,填充数据字段内容,生成代码文件
$viewResult = [];
//创建目录
$filePath = base_path() . "\\bootstrap\\builder\\output\\{$table}\\";
if (!is_dir($filePath)) {
mkdir($filePath, 0777, true);
}
$data = [];
$data['tableName'] = "tb_" . $table;
$data['fields'] = $fields;
$data['className'] = $className;
$data['moduleName'] = $moduleName;
$data['classNameLc'] = $classNameLc;
$data['moduleNameLc'] = $moduleNameLc;
$data['b_module_name'] = $b_module_name;
$data['f_module_name'] = $f_module_name;
$data['b_module_name_lc'] = $b_module_name_lc;
$data['f_module_name_lc'] = $f_module_name_lc;
$data['orgName'] = $orgName;
//创建 - model
if (!empty($is_init_model)) {
$view = View::tbl('model', $data);
$view->layout = false;
$html = $view->html();
$fileName = $filePath . $className . '_M';
$resultItem = [];
$resultItem['title'] = 'model-后端';
$resultItem['fileName'] = $fileName;
array_push($viewResult, $resultItem);
file_put_contents($fileName, $html, LOCK_EX);
/*拷贝到指定目录*/
/*后端模型,目录*/
if ($is_auto_copy) {
$models_dir = base_path() . "\\app\\Models\\";
$models_file_name = $className;
if (!file_exists($models_dir . $models_file_name . '.php')) {
copy($fileName, $models_dir . $models_file_name . '.php');
} else {
copy($fileName, $models_dir . $models_file_name . '');
}
}
}
//创建 - controller
if (!empty($is_init_b_controller)) {
$view = View::tbl('controller', $data);
$view->layout = false;
$html = $view->html();
$fileName = $filePath . $className . '_Controller';
$resultItem = [];
$resultItem['title'] = 'Controller-后端';
$resultItem['fileName'] = $fileName;
array_push($viewResult, $resultItem);
file_put_contents($fileName, $html, LOCK_EX);
/*拷贝到指定目录*/
/*后端Controller,目录*/
if ($is_auto_copy) {
$controller_dir = base_path() . "\\app\\Modules\\{$b_module_name}\\Controllers\\";
$controller_file_name = $className . 'Controller';
if (!file_exists($controller_dir . $controller_file_name . '.php')) {
copy($fileName, $controller_dir . $controller_file_name . '.php');
} else {
copy($fileName, $controller_dir . $controller_file_name . '');
}
}
}
//创建 - list
if (!empty($is_init_list)) {
//创建 - list_view
$view = View::tbl('list_view', $data);
$view->layout = false;
$html = $view->html();
$fileName = $filePath . $className . '_ListView';
$resultItem = [];
$resultItem['title'] = 'list页面-前端';
$resultItem['fileName'] = $fileName;
array_push($viewResult, $resultItem);
file_put_contents($fileName, $html, LOCK_EX);
/*拷贝到指定目录*/
/*前端Controller,目录*/
if ($is_auto_copy) {
$display_dir = base_path() . "\\public\\{$f_module_name_lc}\\main\\view\\" . $classNameLc . "\\";
if (!is_dir($display_dir)) {
mkdir($display_dir, 0777, true);
}
$display_file_name = $classNameLc . 'ListView';
if (!file_exists($display_dir . $display_file_name . '.html')) {
copy($fileName, $display_dir . $display_file_name . '.html');
} else {
copy($fileName, $display_dir . $display_file_name . '');
}
}
//创建 - list_display
$view = View::tbl('list_display', $data);
$view->layout = false;
$html = $view->html();
$fileName = $filePath . $className . '_ListDisplay';
$resultItem = [];
$resultItem['title'] = 'list-table页面-前端';
$resultItem['fileName'] = $fileName;
array_push($viewResult, $resultItem);
file_put_contents($fileName, $html, LOCK_EX);
/*拷贝到指定目录*/
/*前端Controller,目录*/
if ($is_auto_copy) {
$display_dir = base_path() . "\\public\\{$f_module_name_lc}\\main\\view\\" . $classNameLc . "\\";
if (!is_dir($display_dir)) {
mkdir($display_dir, 0777, true);
}
$display_file_name = $classNameLc . 'ListDisplay';
if (!file_exists($display_dir . $display_file_name . '.html')) {
copy($fileName, $display_dir . $display_file_name . '.html');
} else {
copy($fileName, $display_dir . $display_file_name . '');
}
}
}
//创建 - Edit-html
if (!empty($is_init_edit)) {
$view = View::tbl('edit_html', $data);
$view->layout = false;
$html = $view->html();
$fileName = $filePath . $className . '_EditView';
$resultItem = [];
$resultItem['title'] = 'edit-编辑页面-前端';
$resultItem['fileName'] = $fileName;
array_push($viewResult, $resultItem);
file_put_contents($fileName, $html, LOCK_EX);
/*拷贝到指定目录*/
/*前端Controller,目录*/
if ($is_auto_copy) {
$edit_dir = base_path() . "\\public\\{$f_module_name_lc}\\main\\view\\" . $classNameLc . "\\";
if (!is_dir($edit_dir)) {
mkdir($edit_dir, 0777, true);
}
$edit_file_name = $classNameLc . 'EditView';
if (!file_exists($edit_dir . $edit_file_name . '.html')) {
copy($fileName, $edit_dir . $edit_file_name . '.html');
} else {
copy($fileName, $edit_dir . $edit_file_name . '');
}
}
}
//创建 - Controller - JS
if (!empty($is_init_f_controller)) {
$view = View::tbl('controller_js', $data);
$view->layout = false;
$html = $view->html();
$fileName = $filePath . $className . '_Controller_JS';
$resultItem = [];
$resultItem['title'] = 'controller_js-前端';
$resultItem['fileName'] = $fileName;
array_push($viewResult, $resultItem);
file_put_contents($fileName, $html, LOCK_EX);
/*拷贝到指定目录*/
/*前端Controller,目录*/
if ($is_auto_copy) {
$controller_js_dir = base_path() . "\\public\\{$f_module_name_lc}\\main\\controller\\";
$controller_js_file_name = $classNameLc . '';
if (!file_exists($controller_js_dir . $controller_js_file_name . '.js')) {
copy($fileName, $controller_js_dir . $controller_js_file_name . '.js');
} else {
copy($fileName, $controller_js_dir . $controller_js_file_name . '');
}
}
}
//创建 - 后台路由
if (!empty($is_init_b_router)) {
//创建 - Router
$view = View::tbl('router', $data);
$view->layout = false;
$html = $view->html();
$fileName = $filePath . $className . '_Router';
$resultItem = [];
$resultItem['title'] = 'router_路由-后端';
$resultItem['fileName'] = $fileName;
array_push($viewResult, $resultItem);
file_put_contents($fileName, $html, LOCK_EX);
/*拷贝到指定目录*/
/*前端Controller,目录*/
if ($is_auto_copy) {
$router_name = base_path() . "\\app\\Modules\\{$b_module_name}\\router.php";;
if (!file_exists($router_name)) {
} else {
file_put_contents($router_name, $html, FILE_APPEND | LOCK_EX);
}
}
}
//创建 - config_js
if (!empty($is_init_inter)) {
$view = View::tbl('config_js', $data);
$view->layout = false;
$html = $view->html();
$fileName = $filePath . $className . '_Config_js';
$resultItem = [];
$resultItem['title'] = 'config_接口配置文件-前端';
$resultItem['fileName'] = $fileName;
array_push($viewResult, $resultItem);
file_put_contents($fileName, $html, LOCK_EX);
}
//设置菜单
$result['result'] = $viewResult;
$view = View::tbl('result',$result);
$view->layout = false;
echo $view->html();
die();
详细内容请参考源代码
主页 (Alpaca-Spa): http://www.tkc8.com
后台(Alpaca-Spa-Laravel) : http://full.tkc8.com
手机端sui(Alpaca-Spa-Sui) : http://full.tkc8.com/app
代码 (oschina ): http://git.oschina.net/cc-sponge/Alpaca-Spa-Laravel
代码 (github ): https://github.com/big-sponge/Alpaca-Spa-Laravel
联系我们
QQ群: 298420174
作者: Sponge 邮箱: [email protected]