MergeModel.class.php 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408
  1. <?php
  2. /**
  3. * 洛阳赤炎鹰网络科技有限公司
  4. * https://www.cyyvip.com
  5. * Copyright (c) 2022 赤店商城 All rights reserved.
  6. */
  7. // +----------------------------------------------------------------------
  8. // | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
  9. // +----------------------------------------------------------------------
  10. // | Copyright (c) 2006-2014 http://thinkphp.cn All rights reserved.
  11. // +----------------------------------------------------------------------
  12. // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
  13. // +----------------------------------------------------------------------
  14. // | Author: liu21st <liu21st@gmail.com>
  15. // +----------------------------------------------------------------------
  16. namespace Think\Model;
  17. use Think\Model;
  18. /**
  19. * ThinkPHP 聚合模型扩展
  20. */
  21. class MergeModel extends Model {
  22. protected $modelList = array(); // 包含的模型列表 第一个必须是主表模型
  23. protected $masterModel = ''; // 主模型
  24. protected $joinType = 'INNER'; // 聚合模型的查询JOIN类型
  25. protected $fk = ''; // 外键名 默认为主表名_id
  26. protected $mapFields = array(); // 需要处理的模型映射字段,避免混淆 array( id => 'user.id' )
  27. /**
  28. * 架构函数
  29. * 取得DB类的实例对象 字段检查
  30. * @access public
  31. * @param string $name 模型名称
  32. * @param string $tablePrefix 表前缀
  33. * @param mixed $connection 数据库连接信息
  34. */
  35. public function __construct($name='',$tablePrefix='',$connection=''){
  36. parent::__construct($name,$tablePrefix,$connection);
  37. // 聚合模型的字段信息
  38. if(empty($this->fields) && !empty($this->modelList)){
  39. $fields = array();
  40. foreach($this->modelList as $model){
  41. // 获取模型的字段信息
  42. $result = $this->db->getFields(M($model)->getTableName());
  43. $_fields = array_keys($result);
  44. // $this->mapFields = array_intersect($fields,$_fields);
  45. $fields = array_merge($fields,$_fields);
  46. }
  47. $this->fields = $fields;
  48. }
  49. // 设置第一个模型为主表模型
  50. if(empty($this->masterModel) && !empty($this->modelList)){
  51. $this->masterModel = $this->modelList[0];
  52. }
  53. // 主表的主键名
  54. $this->pk = M($this->masterModel)->getPk();
  55. // 设置默认外键名 仅支持单一外键
  56. if(empty($this->fk)){
  57. $this->fk = strtolower($this->masterModel).'_id';
  58. }
  59. }
  60. /**
  61. * 得到完整的数据表名
  62. * @access public
  63. * @return string
  64. */
  65. public function getTableName() {
  66. if(empty($this->trueTableName)) {
  67. $tableName = array();
  68. $models = $this->modelList;
  69. foreach($models as $model){
  70. $tableName[] = M($model)->getTableName().' '.$model;
  71. }
  72. $this->trueTableName = implode(',',$tableName);
  73. }
  74. return $this->trueTableName;
  75. }
  76. /**
  77. * 自动检测数据表信息
  78. * @access protected
  79. * @return void
  80. */
  81. protected function _checkTableInfo() {}
  82. /**
  83. * 新增聚合数据
  84. * @access public
  85. * @param mixed $data 数据
  86. * @param array $options 表达式
  87. * @param boolean $replace 是否replace
  88. * @return mixed
  89. */
  90. public function add($data='',$options=array(),$replace=false){
  91. if(empty($data)) {
  92. // 没有传递数据,获取当前数据对象的值
  93. if(!empty($this->data)) {
  94. $data = $this->data;
  95. // 重置数据
  96. $this->data = array();
  97. }else{
  98. $this->error = L('_DATA_TYPE_INVALID_');
  99. return false;
  100. }
  101. }
  102. // 启动事务
  103. $this->startTrans();
  104. // 写入主表数据
  105. $result = M($this->masterModel)->strict(false)->add($data);
  106. if($result){
  107. // 写入外键数据
  108. $data[$this->fk] = $result;
  109. $models = $this->modelList;
  110. array_shift($models);
  111. // 写入附表数据
  112. foreach($models as $model){
  113. $res = M($model)->strict(false)->add($data);
  114. if(!$res){
  115. $this->rollback();
  116. return false;
  117. }
  118. }
  119. // 提交事务
  120. $this->commit();
  121. }else{
  122. $this->rollback();
  123. return false;
  124. }
  125. return $result;
  126. }
  127. /**
  128. * 对保存到数据库的数据进行处理
  129. * @access protected
  130. * @param mixed $data 要操作的数据
  131. * @return boolean
  132. */
  133. protected function _facade($data) {
  134. // 检查数据字段合法性
  135. if(!empty($this->fields)) {
  136. if(!empty($this->options['field'])) {
  137. $fields = $this->options['field'];
  138. unset($this->options['field']);
  139. if(is_string($fields)) {
  140. $fields = explode(',',$fields);
  141. }
  142. }else{
  143. $fields = $this->fields;
  144. }
  145. foreach ($data as $key=>$val){
  146. if(!in_array($key,$fields,true)){
  147. unset($data[$key]);
  148. }elseif(array_key_exists($key,$this->mapFields)){
  149. // 需要处理映射字段
  150. $data[$this->mapFields[$key]] = $val;
  151. unset($data[$key]);
  152. }
  153. }
  154. }
  155. // 安全过滤
  156. if(!empty($this->options['filter'])) {
  157. $data = array_map($this->options['filter'],$data);
  158. unset($this->options['filter']);
  159. }
  160. $this->_before_write($data);
  161. return $data;
  162. }
  163. /**
  164. * 保存聚合模型数据
  165. * @access public
  166. * @param mixed $data 数据
  167. * @param array $options 表达式
  168. * @return boolean
  169. */
  170. public function save($data='',$options=array()){
  171. // 根据主表的主键更新
  172. if(empty($data)) {
  173. // 没有传递数据,获取当前数据对象的值
  174. if(!empty($this->data)) {
  175. $data = $this->data;
  176. // 重置数据
  177. $this->data = array();
  178. }else{
  179. $this->error = L('_DATA_TYPE_INVALID_');
  180. return false;
  181. }
  182. }
  183. if(empty($data)){
  184. // 没有数据则不执行
  185. $this->error = L('_DATA_TYPE_INVALID_');
  186. return false;
  187. }
  188. // 如果存在主键数据 则自动作为更新条件
  189. $pk = $this->pk;
  190. if(isset($data[$pk])) {
  191. $where[$pk] = $data[$pk];
  192. $options['where'] = $where;
  193. unset($data[$pk]);
  194. }
  195. $options['join'] = '';
  196. $options = $this->_parseOptions($options);
  197. // 更新操作不使用JOIN
  198. $options['table'] = $this->getTableName();
  199. if(is_array($options['where']) && isset($options['where'][$pk])){
  200. $pkValue = $options['where'][$pk];
  201. }
  202. if(false === $this->_before_update($data,$options)) {
  203. return false;
  204. }
  205. $result = $this->db->update($data,$options);
  206. if(false !== $result) {
  207. if(isset($pkValue)) $data[$pk] = $pkValue;
  208. $this->_after_update($data,$options);
  209. }
  210. return $result;
  211. }
  212. /**
  213. * 删除聚合模型数据
  214. * @access public
  215. * @param mixed $options 表达式
  216. * @return mixed
  217. */
  218. public function delete($options=array()){
  219. $pk = $this->pk;
  220. if(empty($options) && empty($this->options['where'])) {
  221. // 如果删除条件为空 则删除当前数据对象所对应的记录
  222. if(!empty($this->data) && isset($this->data[$pk]))
  223. return $this->delete($this->data[$pk]);
  224. else
  225. return false;
  226. }
  227. if(is_numeric($options) || is_string($options)) {
  228. // 根据主键删除记录
  229. if(strpos($options,',')) {
  230. $where[$pk] = array('IN', $options);
  231. }else{
  232. $where[$pk] = $options;
  233. }
  234. $options = array();
  235. $options['where'] = $where;
  236. }
  237. // 分析表达式
  238. $options['join'] = '';
  239. $options = $this->_parseOptions($options);
  240. if(empty($options['where'])){
  241. // 如果条件为空 不进行删除操作 除非设置 1=1
  242. return false;
  243. }
  244. if(is_array($options['where']) && isset($options['where'][$pk])){
  245. $pkValue = $options['where'][$pk];
  246. }
  247. $options['table'] = implode(',',$this->modelList);
  248. $options['using'] = $this->getTableName();
  249. if(false === $this->_before_delete($options)) {
  250. return false;
  251. }
  252. $result = $this->db->delete($options);
  253. if(false !== $result) {
  254. $data = array();
  255. if(isset($pkValue)) $data[$pk] = $pkValue;
  256. $this->_after_delete($data,$options);
  257. }
  258. // 返回删除记录个数
  259. return $result;
  260. }
  261. /**
  262. * 表达式过滤方法
  263. * @access protected
  264. * @param string $options 表达式
  265. * @return void
  266. */
  267. protected function _options_filter(&$options) {
  268. if(!isset($options['join'])){
  269. $models = $this->modelList;
  270. array_shift($models);
  271. foreach($models as $model){
  272. $options['join'][] = $this->joinType.' JOIN '.M($model)->getTableName().' '.$model.' ON '.$this->masterModel.'.'.$this->pk.' = '.$model.'.'.$this->fk;
  273. }
  274. }
  275. $options['table'] = M($this->masterModel)->getTableName().' '.$this->masterModel;
  276. $options['field'] = $this->checkFields(isset($options['field'])?$options['field']:'');
  277. if(isset($options['group']))
  278. $options['group'] = $this->checkGroup($options['group']);
  279. if(isset($options['where']))
  280. $options['where'] = $this->checkCondition($options['where']);
  281. if(isset($options['order']))
  282. $options['order'] = $this->checkOrder($options['order']);
  283. }
  284. /**
  285. * 检查条件中的聚合字段
  286. * @access protected
  287. * @param mixed $data 条件表达式
  288. * @return array
  289. */
  290. protected function checkCondition($where) {
  291. if(is_array($where)) {
  292. $view = array();
  293. foreach($where as $name=>$value){
  294. if(array_key_exists($name,$this->mapFields)){
  295. // 需要处理映射字段
  296. $view[$this->mapFields[$name]] = $value;
  297. unset($where[$name]);
  298. }
  299. }
  300. $where = array_merge($where,$view);
  301. }
  302. return $where;
  303. }
  304. /**
  305. * 检查Order表达式中的聚合字段
  306. * @access protected
  307. * @param string $order 字段
  308. * @return string
  309. */
  310. protected function checkOrder($order='') {
  311. if(is_string($order) && !empty($order)) {
  312. $orders = explode(',',$order);
  313. $_order = array();
  314. foreach ($orders as $order){
  315. $array = explode(' ',trim($order));
  316. $field = $array[0];
  317. $sort = isset($array[1])?$array[1]:'ASC';
  318. if(array_key_exists($field,$this->mapFields)){
  319. // 需要处理映射字段
  320. $field = $this->mapFields[$field];
  321. }
  322. $_order[] = $field.' '.$sort;
  323. }
  324. $order = implode(',',$_order);
  325. }
  326. return $order;
  327. }
  328. /**
  329. * 检查Group表达式中的聚合字段
  330. * @access protected
  331. * @param string $group 字段
  332. * @return string
  333. */
  334. protected function checkGroup($group='') {
  335. if(!empty($group)) {
  336. $groups = explode(',',$group);
  337. $_group = array();
  338. foreach ($groups as $field){
  339. // 解析成聚合字段
  340. if(array_key_exists($field,$this->mapFields)){
  341. // 需要处理映射字段
  342. $field = $this->mapFields[$field];
  343. }
  344. $_group[] = $field;
  345. }
  346. $group = implode(',',$_group);
  347. }
  348. return $group;
  349. }
  350. /**
  351. * 检查fields表达式中的聚合字段
  352. * @access protected
  353. * @param string $fields 字段
  354. * @return string
  355. */
  356. protected function checkFields($fields='') {
  357. if(empty($fields) || '*'==$fields ) {
  358. // 获取全部聚合字段
  359. $fields = $this->fields;
  360. }
  361. if(!is_array($fields))
  362. $fields = explode(',',$fields);
  363. // 解析成聚合字段
  364. $array = array();
  365. foreach ($fields as $field){
  366. if(array_key_exists($field,$this->mapFields)){
  367. // 需要处理映射字段
  368. $array[] = $this->mapFields[$field].' AS '.$field;
  369. }else{
  370. $array[] = $field;
  371. }
  372. }
  373. $fields = implode(',',$array);
  374. return $fields;
  375. }
  376. /**
  377. * 获取数据表字段信息
  378. * @access public
  379. * @return array
  380. */
  381. public function getDbFields(){
  382. return $this->fields;
  383. }
  384. }