$model) {
// 模型拥有的字段数目
$models[$key]['fields_count'] = count($model['fields']);
// 记录行数
$rows = D('Common')->getTableRows($model['tbl_name']);
$models[$key]['rows'] = $rows;
}
return $models;
}
/**
* 得到所有的模型
* @param string $fields 查询字段
* @param string $order 排序
* @return array
*/
public function getAll($fields, $order) {
return $this->getPagination(null, $fields, $order, null, null);
}
/**
* 按id得到model数据
* @param int $id
* @return array
*/
public function getById($id) {
$model = $this->getD()->relation(true)->getById($id);
if (empty($model)) {
return null;
}
$model['fields_count'] = count($model['fields']);
$model['rows'] = $this->getD()->getTableRows($model['tbl_name']);
return $model;
}
/**
* 添加模型并创建数据表
* @param array $model
* @return array
*/
public function add($model) {
$Model = $this->getD();
$model = array_map('trim', $model);
$model['tbl_name'] = C('DB_PREFIX') . $model['tbl_name'];
$Model->startTrans();
$model = $Model->create($model);
$addStatus = $Model->add($model);
// 创建数据表
$createTblStatus = $Model->createTable($model['tbl_name'],
$model['has_pk'],
$model['tbl_engine'],
$model['description']);
// 添加系统字段
$addFieldStatus = $this->addSystemFields($Model->getLastInsID());
// 生成菜单项
if (isset($model['is_inner']) && 0 == $model['is_inner']) {
$this->addMenuItem($model);
}
// 生成节点
$ctrlName = $this->getCtrlName($model['tbl_name']);
$nodeService = D('Node', 'Service');
$amns = $nodeService->addModuleNodes($model['menu_name'], $ctrlName);
if (false === $addStatus
|| false === $createTblStatus
|| false === $addFieldStatus
|| false === $amns) {
$Model->rollback();
return $this->resultReturn(false);
}
$Model->commit();
return $this->resultReturn(true);
}
/**
* 添加旧的数据表
*
COLUMNS
当前数据库中当前用户可以访问的每一个列在该视图中占一行。INFORMATION_SCHEMA.COLUMNS 视图以 sysobjects、spt_data type_info、systypes、syscolumns、syscomments、sysconfigures 以及 syscharsets 系统表为基础。
若要从这些视图中检索信息,请指定完全合格的 INFORMATION_SCHEMA view_name 名称。
列名 数据类型 描述
TABLE_CATALOG nvarchar(128) 表限定符。
TABLE_SCHEMA nvarchar(128) 表所有者。
TABLE_NAME nvarchar(128) 表名。
COLUMN_NAME nvarchar(128) 列名。
ORDINAL_POSITION smallint 列标识号。
COLUMN_DEFAULT nvarchar(4000) 列的默认值。
IS_NULLABLE varchar(3) 列的为空性。如果列允许 NULL,那么该列返回 YES。否则,返回 NO。
DATA_TYPE nvarchar(128) 系统提供的数据类型。
CHARACTER_MAXIMUM_LENGTH smallint 以字符为单位的最大长度,适于二进制数据、字符数据,或者文本和图像数据。否则,返回 NULL。有关更多信息,请参见数据类型。
CHARACTER_OCTET_LENGTH smallint 以字节为单位的最大长度,适于二进制数据、字符数据,或者文本和图像数据。否则,返回 NULL。
NUMERIC_PRECISION tinyint 近似数字数据、精确数字数据、整型数据或货币数据的精度。否则,返回 NULL。
NUMERIC_PRECISION_RADIX smallint 近似数字数据、精确数字数据、整型数据或货币数据的精度基数。否则,返回 NULL。
NUMERIC_SCALE tinyint 近似数字数据、精确数字数据、整数数据或货币数据的小数位数。否则,返回 NULL。
DATETIME_PRECISION smallint datetime 及 SQL-92 interval 数据类型的子类型代码。对于其它数据类型,返回 NULL。
CHARACTER_SET_CATALOG varchar(6) 如果列是字符数据或 text 数据类型,那么返回 master,指明字符集所在的数据库。否则,返回 NULL。
CHARACTER_SET_SCHEMA varchar(3) 如果列是字符数据或 text 数据类型,那么返回 DBO,指明字符集的所有者名称。否则,返回 NULL。
CHARACTER_SET_NAME nvarchar(128) 如果该列是字符数据或 text 数据类型,那么为字符集返回唯一的名称。否则,返回 NULL。
COLLATION_CATALOG varchar(6) 如果列是字符数据或 text 数据类型,那么返回 master,指明在其中定义排序次序的数据库。否则此列为 NULL。
COLLATION_SCHEMA varchar(3) 返回 DBO,为字符数据或 text 数据类型指明排序次序的所有者。否则,返回 NULL。
COLLATION_NAME nvarchar(128) 如果列是字符数据或 text 数据类型,那么为排序次序返回唯一的名称。否则,返回 NULL。
DOMAIN_CATALOG nvarchar(128) 如果列是一种用户定义数据类型,那么该列是某个数据库名称,在该数据库名中创建了这种用户定义数据类型。否则,返回 NULL。
DOMAIN_SCHEMA nvarchar(128) 如果列是一种用户定义数据类型,那么该列是这种用户定义数据类型的创建者。否则,返回 NULL。
DOMAIN_NAME nvarchar(128) 如果列是一种用户定义数据类型,那么该列是这种用户定义数据类型的名称。否则,返回 NULL。
* @param $model
* @return array
*/
public function add_old_table($model) {
$Model = $this->getD();
$model = array_map ( 'trim', $model );
$new_db = $model ['db_name'];
$new_table = $model ['tbl_name_old'];
$model ['tbl_name'] = $model ['db_name'].'.'.$model ['tbl_name_old'];
$Model->startTrans ();
$model = $Model->create ( $model );
$addStatus = $Model->add ( $model );
$sql = "SELECT `COLUMN_NAME`,DATA_TYPE,COLUMN_COMMENT,COLUMN_TYPE FROM information_schema.columns WHERE table_schema ='".$new_db."' AND table_name = '".$new_table."'";
$table_attribute = $Model->query ( $sql );
$modelId = $Model->getLastInsID ();
$fieldService = D ( 'Field', 'Service' );
$inputService = D('input', 'Service');
//查询此表主键
$qu_key['name'] = $model ['tbl_name'];
foreach ( $table_attribute as $ke => $val ) {
$column_name = $val ['COLUMN_NAME'];
$comment = $val ['COLUMN_COMMENT'];
$model_name = $model ['tbl_name'];
$length = "";
if(preg_match('/\d+/',$val['COLUMN_TYPE'], $arr)){
$length = $arr[0];
}
$field = array (
'model_id' => $modelId,
'name' => $column_name,
'comment' => $comment,
'type' => $val ['DATA_TYPE'],
'length'=> $length,
'is_requier' => 0,
'is_unique' => 0,
'is_index' => 0,
'is_system' => 0,
'auto_fill' =>'',
'created_at' => time (),
'updated_at' => time (),
'is_old' => 1
);
$addFieldStatus = $fieldService->just_add_field ( $field );
$input = array (
'field_id' => $addFieldStatus ['data'] ['id'],
'is_show' => 1,
'label' => $column_name,
'remark' => $comment,
'type' => 'text',
'width' => 20,
'height' => 0,
'opt_value' => '',
'value' => '',
'editor' => 'all',
'html' => "",
'show_order' => 1,
'created_at' => time (),
'updated_at' => time ()
);
$inputService->add ( $input );
}
// 生成菜单项
if (isset($model['is_inner']) && 0 == $model['is_inner']) {
$this->addMenuItem($model);
}
// 生成节点
$ctrlName = $this->getCtrlName($model['tbl_name']);
$nodeService = D('Node', 'Service');
$amns = $nodeService->addModuleNodes($model['menu_name'], $ctrlName);
if (false === $addStatus
|| false === $addFieldStatus
|| false === $amns) {
$Model->rollback();
return $this->resultReturn(false);
}
$Model->commit();
return $this->resultReturn(true);
}
/**
* 更新模型并更新数据表
* @param array $model
* @return array
*/
public function update($model) {
$Model = $this->getD();
$model = array_map('trim', $model);
$model['tbl_name'] = C('DB_PREFIX') . $model['tbl_name'];
// 取出旧数据
$old = $Model->field('tbl_name')->getById($model['id']);
$Model->startTrans();
$model = $Model->create($model);
// 更新数据
$updateStatus = $Model->save($model);
// 更新数据表名
if ($model['tbl_name'] != $old['tbl_name']) {
$utnStatus = $Model->updateTableName($old['tbl_name'],
$model['tbl_name']);
}
// 更新数据表注释
if ($model['description'] != $old['description']) {
$utcStatus = $Model->updateTableComment($model['tbl_name'],
$model['description']);
}
// 更新菜单
if (($model['menu_name'] != $old['menu_name']
|| $model['tbl_name'] != $old['tbl_name'])
&& 0 == $old['is_inner']) {
$this->replaceMenuItem($model, $old);
}
if (false === $updateStatus
|| false === $utnStatus
|| false === $utcStatus) {
$Model->rollback();
// 撤回更新
if (0 == $old['is_inner']) {
$this->replaceMenuItem($old, $model);
}
return $this->resultReturn(false);
}
$Model->commit();
return $this->resultReturn(true);
}
/**
* 删除模型并且删除数据表
* @param int $id 需要删除模型的id
* @return boolean
*/
public function delete($id) {
$Model = $this->getD();
$model = $Model->getById($id);
if (empty($model)) {
return $this->resultReturn(false);
}
$ctrlName = $this->getCtrlName($model['tbl_name']);
$Model->startTrans();
// 删除数据表
$dropStatus = $Model->dropTable($model['tbl_name']);
// 删除模型数据
$delStatus = $Model->delete($id);
// 删除菜单项
$this->delMenuItem($ctrlName);
// 删除节点
D('Node', 'Service')->deleteModuleNodes($ctrlName);
if (false === $dropStatus || false === $delStatus) {
$Modle->rollback();
// 还原菜单项
if (0 == $model['is_inner']) {
$this->addMenuItem($model);
}
return $this->resultReturn(false);
}
$Model->commit();
return $this->resultReturn(true);
}
/**
* 检查模型名称是否可用
* @param string $name 模型名称
* @param int $id 需要更新模型的id
* @return array
*/
public function checkModelName($name, $id) {
$Model = $this->getD();
$model['name'] = trim($name);
if ($Model->isValidModelName($model, $id)) {
return $this->resultReturn(true);
}
return $this->errorResultReturn($Model->getError());
}
/**
* 检查数据表名称是否可用
* @param string $name 数据表名称
* @param int $id 需要更新模型的id
* @return array
*/
public function checkTblName($name, $id) {
$Model = $this->getD();
$model['tbl_name'] = trim($name);
// 验证表名是否空
if (empty($model['tbl_name'])) {
return $this->errorResultReturn('数据表名不能为空');
}
// 验证表名是否已存在
$model['tbl_name'] = C('DB_PREFIX') . $model['tbl_name'];
if ($Model->isValidTblName($model, $id)) {
return $this->resultReturn(true);
}
return $this->errorResultReturn($Model->getError());
}
/**
* 检查菜单名称是否可用
* @param string $name 菜单名称
* @param int $id 需要更新模型的id
* @return array
*/
public function checkMenuName($name, $id) {
$Model = $this->getD();
$model['menu_name'] = trim($name);
if ($Model->isValidMenuName($model, $id)) {
return $this->resultReturn(true);
}
return $this->errorResultReturn($Model->getError());
}
/**
* 检查模型是否可用
* @param array $model 需要检查的模型
* @param int $id 需要更新模型的id
* @return array
*/
public function checkModel($model, $id) {
$Model = $this->getD();
// 检查表名是否合法
$model = array_map('trim', $model);
$resutl = $this->checkTblName($model['tbl_name'], $id);
// // 需要检查的模型id
// $_SESSION['update_id'] = $model['id'];
if (false === $resutl['status']) {
return $this->errorResultReturn($resutl['data']['error']);
}
if ($Model->isValid($model, $id)) {
return $this->resultReturn(true);
}
return $this->errorResultReturn($Model->getError());
}
/**
* 检查模型是否存在
* @param int $modeId
* @return boolean
*/
public function existModel($modeId) {
if ($this->getM()->where("id = {$modeId}")->count() > 0) {
return true;
}
return false;
}
/**
* 检查模型是否存在指定的字段
* @param int $modelId 模型id
* @param int $fieldId 字段id
* @return boolean
*/
public function hasField($modelId, $fieldId) {
$where = array('model_id' => $modelId, 'id' => $fieldId);
if (null == M('Field')->where($where)->find()) {
return false;
}
return true;
}
/**
* 添加系统字段:id、created_at、updated_at
* @param int $modelId 模型id
* @return boolean 是否添加成功
*/
private function addSystemFields($modelId) {
$Field = D('Field');
// id字段
$id = array('model_id' => $modelId,
'name' => 'id',
'comment' => '表主键',
'type' => 'INT',
'is_requier' => 1,
'is_unique' => 1,
'is_index' => 1,
'is_system' => 1);
$id = $Field->create($id);
$status = false !== $Field->add($id) ? true : false;
// created_at updated_at
$timestamp = array('model_id' => $modelId,
'type' => 'INT',
'is_index' => 1,
'is_system' => 1,
'auto_fill' => 'time');
$timestamp = $Field->create($timestamp);
// created_at字段
$timestamp['name'] = 'created_at';
$timestamp['comment'] = '创建时间';
$timestamp['fill_time'] = 'insert';
$status = false !== $Field->add($timestamp) ? true : false;
// updated_at字段
$timestamp['name'] = 'updated_at';
$timestamp['comment'] = '更新时间';
$timestamp['fill_time'] = 'both';
$status = false !== $Field->add($timestamp) ? true : false;
return $status;
}
/**
* 添加菜单项
* @param array $model
*/
private function addMenuItem(array $model) {
$modelLogic = D('Model', 'Logic');
// 得到模型的控制器名称
$ctrlName = $this->getCtrlName($model['tbl_name']);
// 生成菜单项
$item = $modelLogic->genMenuItem($model['menu_name'], $ctrlName);
// 添加菜单项
$menu = $modelLogic->addMenuItem($item);
}
/**
* 删除菜单项
* @param string $ctrlName 菜单项对应的控制器名称
* @return mixed
*/
private function delMenuItem($ctrlName) {
return D('Model', 'Logic')->delMenuItem($ctrlName);
}
/**
* 替换菜单项
* @param array $model
* @param array $old
* @return array
*/
private function replaceMenuItem($model, $old) {
$modelLogic = D('Model', 'Logic');
$oldCtrlName = $this->getCtrlName($old['tbl_name']);
$ctrlName = $this->getCtrlName($model['tbl_name']);
// 生成新菜单项
$item = $modelLogic->genMenuItem($model['menu_name'], $ctrlName);
// 替换旧的菜单项
return $modelLogic->replaceMenuItem($item, $oldCtrlName);
}
/**
* 以数据表名得到控制器名称
* @param string $tblName
* @return string
*/
public function getCtrlName($tblName) {
if (strpos($tblName, '.') === false) {
// 去掉表前缀
$tblName = substr($tblName, strpos($tblName, '_') + 1);
$tblName = str_replace('_', ' ', $tblName);
// 单词首字母转为大写
$tblName = ucwords($tblName);
$tblName = str_replace(' ', '', $tblName);
}
return $tblName;
}
/**
* 以控制器名得到表名称
* @param string $ctrlName 控制器名
* @return string
*/
public function getTblName($ctrlName) {
$tblName = '';
$ctrArr = explode('.', $ctrlName);
if (strpos($ctrlName, '.') === false || (strpos($ctrlName, '.') && $ctrArr[0] == C('DB_NAME') && $ctrlName = $ctrArr[1])) {
for ($i = 0, $len = strlen($ctrlName); $i < $len; $i++) {
if (strtoupper($ctrlName[$i]) === $ctrlName[$i]) {
// 大写字母
$tblName .= '_' . strtolower($ctrlName[$i]);
continue ;
}
$tblName .= $ctrlName[$i];
}
$tblName = C('DB_PREFIX') . substr($tblName, 1);
} else {
$tblName = $ctrlName;
}
return $tblName;
}
protected function getModelName() {
return 'Model';
}
public function diff_table($model_id, $tbl_name, $sync = false) {
$m = $this->getM();
$db_name = C("DB_NAME");
$tmp_name = $tbl_name;
if(strpos($tbl_name, ".") !== false) {
$tmp = explode(".", $tbl_name);
$db_name = $tmp[0];
$tbl_name = $tmp[1];
}
$model_field = $m->query("select name,type,length,value,comment,id from ea_field where model_id= $model_id");
$table_field = $m->query("SELECT `COLUMN_NAME`,DATA_TYPE,COLUMN_TYPE,COLUMN_COMMENT FROM information_schema.columns WHERE table_schema ='".$db_name."' AND table_name = '".$tbl_name."'" );
$result = [];
$field_names = [];
$diff_result = [];
if($table_field) {
foreach ( $table_field as $tk => $tv) {
$length = "";
if(preg_match('/\d+/',$tv['COLUMN_TYPE'], $arr)){
$length = $arr[0];
}
$tmp = [
'name'=>$tv['COLUMN_NAME'],
'type'=>$tv['DATA_TYPE'],
'length'=>$length,
'comment'=>$tv['COLUMN_COMMENT']
];
$field_names[] = $tmp['name'];
$result[$tmp['name']] = $tmp;
}
}
if($model_field) {
foreach ( $model_field as $mk => $mv ) {
if( !in_array($mv['name'], $field_names)) {
} else {
$tmp = $result[$mv['name']];
$tmp['id'] = $mv['id'];
unset($result[$mv['name']]);
foreach($tmp as $k=>$v) {
if(strtolower($mv[$k]) != strtolower($v)) {
unset($mv['value']);
$diff_result['diff'][$mv['name']] = [$tmp, $mv];
break;
}
}
}
}
}
if($result) {
$diff_result['new'] = $result;
}
if($sync) {
foreach($diff_result['new'] as $dv) {
$r = $this->update_field(0, $model_id, $tmp_name, $dv['name'], $dv['comment'], $dv['type'], $dv['length']);
}
foreach($diff_result['diff'] as $dv) {
$dv = $dv[0];
$r = $this->update_field($dv['id'], $model_id, $tmp_name, $dv['name'], $dv['comment'], $dv['type'], $dv['length']);
}
return true;
}
return $diff_result;
}
protected function update_field($field_id, $model_id, $tablename, $name, $comment, $type, $len) {
//获取目标表字段到本库
$fieldService = D ( 'Field', 'Service' );
$inputService = D('input', 'Service');
$field = array (
'model_id' => $model_id,
'name' => $name,
'comment' => $comment,
'type' => $type,
'length'=> $len,
'is_requier' => 0,
'is_unique' => 0,
'is_index' => 0,
'is_system' => 0,
'created_at' => time(),
'updated_at' => time(),
'is_old' => 1
);
$is_update = false;
if($field_id) {
$is_update = true;
$field['id'] = $field_id;
$retField = $fieldService->update($field);
}else {
$retField = $fieldService->add($field);
$field_id = $retField['data'] ['id'];
}
//插入input
$input = array (
'field_id' => $field_id,
'is_show' => 1,
'label' => $comment,
'remark' => $comment,
'type' => 'text',
'width' => 20,
'height' => 0,
'opt_value' => '',
'value' => '',
'editor' => 'all',
'html' => "",
'show_order' => 1,
'created_at' => time (),
'updated_at' => time ()
);
if($is_update) {
$id = M('Input')->where(['field_id' => $field_id])->getField('id');
$input['id'] = $id;
$retInput = $inputService->update($input);
} else {
$retInput = $inputService->add($input);
}
if($retField['status'] && $retInput['status']) {
return true;
} else {
return false;
}
}
}