AdvModel.class.php 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600
  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. * 高级模型扩展
  20. */
  21. class AdvModel extends Model {
  22. protected $optimLock = 'lock_version';
  23. protected $returnType = 'array';
  24. protected $blobFields = array();
  25. protected $blobValues = null;
  26. protected $serializeField = array();
  27. protected $readonlyField = array();
  28. protected $_filter = array();
  29. protected $partition = array();
  30. public function __construct($name='',$tablePrefix='',$connection='') {
  31. if('' !== $name || is_subclass_of($this,'AdvModel') ){
  32. // 如果是AdvModel子类或者有传入模型名称则获取字段缓存
  33. }else{
  34. // 空的模型 关闭字段缓存
  35. $this->autoCheckFields = false;
  36. }
  37. parent::__construct($name,$tablePrefix,$connection);
  38. }
  39. /**
  40. * 利用__call方法重载 实现一些特殊的Model方法 (魔术方法)
  41. * @access public
  42. * @param string $method 方法名称
  43. * @param mixed $args 调用参数
  44. * @return mixed
  45. */
  46. public function __call($method,$args) {
  47. if(strtolower(substr($method,0,3))=='top'){
  48. // 获取前N条记录
  49. $count = substr($method,3);
  50. array_unshift($args,$count);
  51. return call_user_func_array(array(&$this, 'topN'), $args);
  52. }else{
  53. return parent::__call($method,$args);
  54. }
  55. }
  56. /**
  57. * 对保存到数据库的数据进行处理
  58. * @access protected
  59. * @param mixed $data 要操作的数据
  60. * @return boolean
  61. */
  62. protected function _facade($data) {
  63. // 检查序列化字段
  64. $data = $this->serializeField($data);
  65. return parent::_facade($data);
  66. }
  67. // 查询成功后的回调方法
  68. protected function _after_find(&$result,$options='') {
  69. // 检查序列化字段
  70. $this->checkSerializeField($result);
  71. // 获取文本字段
  72. $this->getBlobFields($result);
  73. // 检查字段过滤
  74. $result = $this->getFilterFields($result);
  75. // 缓存乐观锁
  76. $this->cacheLockVersion($result);
  77. }
  78. // 查询数据集成功后的回调方法
  79. protected function _after_select(&$resultSet,$options='') {
  80. // 检查序列化字段
  81. $resultSet = $this->checkListSerializeField($resultSet);
  82. // 获取文本字段
  83. $resultSet = $this->getListBlobFields($resultSet);
  84. // 检查列表字段过滤
  85. $resultSet = $this->getFilterListFields($resultSet);
  86. }
  87. // 写入前的回调方法
  88. protected function _before_insert(&$data,$options='') {
  89. // 记录乐观锁
  90. $data = $this->recordLockVersion($data);
  91. // 检查文本字段
  92. $data = $this->checkBlobFields($data);
  93. // 检查字段过滤
  94. $data = $this->setFilterFields($data);
  95. }
  96. protected function _after_insert($data,$options) {
  97. // 保存文本字段
  98. $this->saveBlobFields($data);
  99. }
  100. // 更新前的回调方法
  101. protected function _before_update(&$data,$options='') {
  102. // 检查乐观锁
  103. $pk = $this->getPK();
  104. if(isset($options['where'][$pk])){
  105. $id = $options['where'][$pk];
  106. if(!$this->checkLockVersion($id,$data)) {
  107. return false;
  108. }
  109. }
  110. // 检查文本字段
  111. $data = $this->checkBlobFields($data);
  112. // 检查只读字段
  113. $data = $this->checkReadonlyField($data);
  114. // 检查字段过滤
  115. $data = $this->setFilterFields($data);
  116. }
  117. protected function _after_update($data,$options) {
  118. // 保存文本字段
  119. $this->saveBlobFields($data);
  120. }
  121. protected function _after_delete($data,$options) {
  122. // 删除Blob数据
  123. $this->delBlobFields($data);
  124. }
  125. /**
  126. * 记录乐观锁
  127. * @access protected
  128. * @param array $data 数据对象
  129. * @return array
  130. */
  131. protected function recordLockVersion($data) {
  132. // 记录乐观锁
  133. if($this->optimLock && !isset($data[$this->optimLock]) ) {
  134. if(in_array($this->optimLock,$this->fields,true)) {
  135. $data[$this->optimLock] = 0;
  136. }
  137. }
  138. return $data;
  139. }
  140. /**
  141. * 缓存乐观锁
  142. * @access protected
  143. * @param array $data 数据对象
  144. * @return void
  145. */
  146. protected function cacheLockVersion($data) {
  147. if($this->optimLock) {
  148. if(isset($data[$this->optimLock]) && isset($data[$this->getPk()])) {
  149. // 只有当存在乐观锁字段和主键有值的时候才记录乐观锁
  150. $_SESSION[$this->name.'_'.$data[$this->getPk()].'_lock_version'] = $data[$this->optimLock];
  151. }
  152. }
  153. }
  154. /**
  155. * 检查乐观锁
  156. * @access protected
  157. * @param inteter $id 当前主键
  158. * @param array $data 当前数据
  159. * @return mixed
  160. */
  161. protected function checkLockVersion($id,&$data) {
  162. // 检查乐观锁
  163. $identify = $this->name.'_'.$id.'_lock_version';
  164. if($this->optimLock && isset($_SESSION[$identify])) {
  165. $lock_version = $_SESSION[$identify];
  166. $vo = $this->field($this->optimLock)->find($id);
  167. $_SESSION[$identify] = $lock_version;
  168. $curr_version = $vo[$this->optimLock];
  169. if(isset($curr_version)) {
  170. if($curr_version>0 && $lock_version != $curr_version) {
  171. // 记录已经更新
  172. $this->error = L('_RECORD_HAS_UPDATE_');
  173. return false;
  174. }else{
  175. // 更新乐观锁
  176. $save_version = $data[$this->optimLock];
  177. if($save_version != $lock_version+1) {
  178. $data[$this->optimLock] = $lock_version+1;
  179. }
  180. $_SESSION[$identify] = $lock_version+1;
  181. }
  182. }
  183. }
  184. return true;
  185. }
  186. /**
  187. * 查找前N个记录
  188. * @access public
  189. * @param integer $count 记录个数
  190. * @param array $options 查询表达式
  191. * @return array
  192. */
  193. public function topN($count,$options=array()) {
  194. $options['limit'] = $count;
  195. return $this->select($options);
  196. }
  197. /**
  198. * 查询符合条件的第N条记录
  199. * 0 表示第一条记录 -1 表示最后一条记录
  200. * @access public
  201. * @param integer $position 记录位置
  202. * @param array $options 查询表达式
  203. * @return mixed
  204. */
  205. public function getN($position=0,$options=array()) {
  206. if($position>=0) { // 正向查找
  207. $options['limit'] = $position.',1';
  208. $list = $this->select($options);
  209. return $list?$list[0]:false;
  210. }else{ // 逆序查找
  211. $list = $this->select($options);
  212. return $list?$list[count($list)-abs($position)]:false;
  213. }
  214. }
  215. /**
  216. * 获取满足条件的第一条记录
  217. * @access public
  218. * @param array $options 查询表达式
  219. * @return mixed
  220. */
  221. public function first($options=array()) {
  222. return $this->getN(0,$options);
  223. }
  224. /**
  225. * 获取满足条件的最后一条记录
  226. * @access public
  227. * @param array $options 查询表达式
  228. * @return mixed
  229. */
  230. public function last($options=array()) {
  231. return $this->getN(-1,$options);
  232. }
  233. /**
  234. * 返回数据
  235. * @access public
  236. * @param array $data 数据
  237. * @param string $type 返回类型 默认为数组
  238. * @return mixed
  239. */
  240. public function returnResult($data,$type='') {
  241. if('' === $type)
  242. $type = $this->returnType;
  243. switch($type) {
  244. case 'array' : return $data;
  245. case 'object': return (object)$data;
  246. default:// 允许用户自定义返回类型
  247. if(class_exists($type))
  248. return new $type($data);
  249. else
  250. E(L('_CLASS_NOT_EXIST_').':'.$type);
  251. }
  252. }
  253. /**
  254. * 获取数据的时候过滤数据字段
  255. * @access protected
  256. * @param mixed $result 查询的数据
  257. * @return array
  258. */
  259. protected function getFilterFields(&$result) {
  260. if(!empty($this->_filter)) {
  261. foreach ($this->_filter as $field=>$filter){
  262. if(isset($result[$field])) {
  263. $fun = $filter[1];
  264. if(!empty($fun)) {
  265. if(isset($filter[2]) && $filter[2]){
  266. // 传递整个数据对象作为参数
  267. $result[$field] = call_user_func($fun,$result);
  268. }else{
  269. // 传递字段的值作为参数
  270. $result[$field] = call_user_func($fun,$result[$field]);
  271. }
  272. }
  273. }
  274. }
  275. }
  276. return $result;
  277. }
  278. protected function getFilterListFields(&$resultSet) {
  279. if(!empty($this->_filter)) {
  280. foreach ($resultSet as $key=>$result)
  281. $resultSet[$key] = $this->getFilterFields($result);
  282. }
  283. return $resultSet;
  284. }
  285. /**
  286. * 写入数据的时候过滤数据字段
  287. * @access protected
  288. * @param mixed $result 查询的数据
  289. * @return array
  290. */
  291. protected function setFilterFields($data) {
  292. if(!empty($this->_filter)) {
  293. foreach ($this->_filter as $field=>$filter){
  294. if(isset($data[$field])) {
  295. $fun = $filter[0];
  296. if(!empty($fun)) {
  297. if(isset($filter[2]) && $filter[2]) {
  298. // 传递整个数据对象作为参数
  299. $data[$field] = call_user_func($fun,$data);
  300. }else{
  301. // 传递字段的值作为参数
  302. $data[$field] = call_user_func($fun,$data[$field]);
  303. }
  304. }
  305. }
  306. }
  307. }
  308. return $data;
  309. }
  310. /**
  311. * 返回数据列表
  312. * @access protected
  313. * @param array $resultSet 数据
  314. * @param string $type 返回类型 默认为数组
  315. * @return void
  316. */
  317. protected function returnResultSet(&$resultSet,$type='') {
  318. foreach ($resultSet as $key=>$data)
  319. $resultSet[$key] = $this->returnResult($data,$type);
  320. return $resultSet;
  321. }
  322. protected function checkBlobFields(&$data) {
  323. // 检查Blob文件保存字段
  324. if(!empty($this->blobFields)) {
  325. foreach ($this->blobFields as $field){
  326. if(isset($data[$field])) {
  327. if(isset($data[$this->getPk()]))
  328. $this->blobValues[$this->name.'/'.$data[$this->getPk()].'_'.$field] = $data[$field];
  329. else
  330. $this->blobValues[$this->name.'/@?id@_'.$field] = $data[$field];
  331. unset($data[$field]);
  332. }
  333. }
  334. }
  335. return $data;
  336. }
  337. /**
  338. * 获取数据集的文本字段
  339. * @access protected
  340. * @param mixed $resultSet 查询的数据
  341. * @param string $field 查询的字段
  342. * @return void
  343. */
  344. protected function getListBlobFields(&$resultSet,$field='') {
  345. if(!empty($this->blobFields)) {
  346. foreach ($resultSet as $key=>$result){
  347. $result = $this->getBlobFields($result,$field);
  348. $resultSet[$key] = $result;
  349. }
  350. }
  351. return $resultSet;
  352. }
  353. /**
  354. * 获取数据的文本字段
  355. * @access protected
  356. * @param mixed $data 查询的数据
  357. * @param string $field 查询的字段
  358. * @return void
  359. */
  360. protected function getBlobFields(&$data,$field='') {
  361. if(!empty($this->blobFields)) {
  362. $pk = $this->getPk();
  363. $id = $data[$pk];
  364. if(empty($field)) {
  365. foreach ($this->blobFields as $field){
  366. $identify = $this->name.'/'.$id.'_'.$field;
  367. $data[$field] = F($identify);
  368. }
  369. return $data;
  370. }else{
  371. $identify = $this->name.'/'.$id.'_'.$field;
  372. return F($identify);
  373. }
  374. }
  375. }
  376. /**
  377. * 保存File方式的字段
  378. * @access protected
  379. * @param mixed $data 保存的数据
  380. * @return void
  381. */
  382. protected function saveBlobFields(&$data) {
  383. if(!empty($this->blobFields)) {
  384. foreach ($this->blobValues as $key=>$val){
  385. if(strpos($key,'@?id@'))
  386. $key = str_replace('@?id@',$data[$this->getPk()],$key);
  387. F($key,$val);
  388. }
  389. }
  390. }
  391. /**
  392. * 删除File方式的字段
  393. * @access protected
  394. * @param mixed $data 保存的数据
  395. * @param string $field 查询的字段
  396. * @return void
  397. */
  398. protected function delBlobFields(&$data,$field='') {
  399. if(!empty($this->blobFields)) {
  400. $pk = $this->getPk();
  401. $id = $data[$pk];
  402. if(empty($field)) {
  403. foreach ($this->blobFields as $field){
  404. $identify = $this->name.'/'.$id.'_'.$field;
  405. F($identify,null);
  406. }
  407. }else{
  408. $identify = $this->name.'/'.$id.'_'.$field;
  409. F($identify,null);
  410. }
  411. }
  412. }
  413. /**
  414. * 检查序列化数据字段
  415. * @access protected
  416. * @param array $data 数据
  417. * @return array
  418. */
  419. protected function serializeField(&$data) {
  420. // 检查序列化字段
  421. if(!empty($this->serializeField)) {
  422. // 定义方式 $this->serializeField = array('ser'=>array('name','email'));
  423. foreach ($this->serializeField as $key=>$val){
  424. if(empty($data[$key])) {
  425. $serialize = array();
  426. foreach ($val as $name){
  427. if(isset($data[$name])) {
  428. $serialize[$name] = $data[$name];
  429. unset($data[$name]);
  430. }
  431. }
  432. if(!empty($serialize)) {
  433. $data[$key] = serialize($serialize);
  434. }
  435. }
  436. }
  437. }
  438. return $data;
  439. }
  440. // 检查返回数据的序列化字段
  441. protected function checkSerializeField(&$result) {
  442. // 检查序列化字段
  443. if(!empty($this->serializeField)) {
  444. foreach ($this->serializeField as $key=>$val){
  445. if(isset($result[$key])) {
  446. $serialize = unserialize($result[$key]);
  447. foreach ($serialize as $name=>$value)
  448. $result[$name] = $value;
  449. unset($serialize,$result[$key]);
  450. }
  451. }
  452. }
  453. return $result;
  454. }
  455. // 检查数据集的序列化字段
  456. protected function checkListSerializeField(&$resultSet) {
  457. // 检查序列化字段
  458. if(!empty($this->serializeField)) {
  459. foreach ($this->serializeField as $key=>$val){
  460. foreach ($resultSet as $k=>$result){
  461. if(isset($result[$key])) {
  462. $serialize = unserialize($result[$key]);
  463. foreach ($serialize as $name=>$value)
  464. $result[$name] = $value;
  465. unset($serialize,$result[$key]);
  466. $resultSet[$k] = $result;
  467. }
  468. }
  469. }
  470. }
  471. return $resultSet;
  472. }
  473. /**
  474. * 检查只读字段
  475. * @access protected
  476. * @param array $data 数据
  477. * @return array
  478. */
  479. protected function checkReadonlyField(&$data) {
  480. if(!empty($this->readonlyField)) {
  481. foreach ($this->readonlyField as $key=>$field){
  482. if(isset($data[$field]))
  483. unset($data[$field]);
  484. }
  485. }
  486. return $data;
  487. }
  488. /**
  489. * 批处理执行SQL语句
  490. * 批处理的指令都认为是execute操作
  491. * @access public
  492. * @param array $sql SQL批处理指令
  493. * @return boolean
  494. */
  495. public function patchQuery($sql=array()) {
  496. if(!is_array($sql)) return false;
  497. // 自动启动事务支持
  498. $this->startTrans();
  499. try{
  500. foreach ($sql as $_sql){
  501. $result = $this->execute($_sql);
  502. if(false === $result) {
  503. // 发生错误自动回滚事务
  504. $this->rollback();
  505. return false;
  506. }
  507. }
  508. // 提交事务
  509. $this->commit();
  510. } catch (ThinkException $e) {
  511. $this->rollback();
  512. }
  513. return true;
  514. }
  515. /**
  516. * 得到分表的的数据表名
  517. * @access public
  518. * @param array $data 操作的数据
  519. * @return string
  520. */
  521. public function getPartitionTableName($data=array()) {
  522. // 对数据表进行分区
  523. if(isset($data[$this->partition['field']])) {
  524. $field = $data[$this->partition['field']];
  525. switch($this->partition['type']) {
  526. case 'id':
  527. // 按照id范围分表
  528. $step = $this->partition['expr'];
  529. $seq = floor($field / $step)+1;
  530. break;
  531. case 'year':
  532. // 按照年份分表
  533. if(!is_numeric($field)) {
  534. $field = strtotime($field);
  535. }
  536. $seq = date('Y',$field)-$this->partition['expr']+1;
  537. break;
  538. case 'mod':
  539. // 按照id的模数分表
  540. $seq = ($field % $this->partition['num'])+1;
  541. break;
  542. case 'md5':
  543. // 按照md5的序列分表
  544. $seq = (ord(substr(md5($field),0,1)) % $this->partition['num'])+1;
  545. break;
  546. default :
  547. if(function_exists($this->partition['type'])) {
  548. // 支持指定函数哈希
  549. $fun = $this->partition['type'];
  550. $seq = (ord(substr($fun($field),0,1)) % $this->partition['num'])+1;
  551. }else{
  552. // 按照字段的首字母的值分表
  553. $seq = (ord($field{0}) % $this->partition['num'])+1;
  554. }
  555. }
  556. return $this->getTableName().'_'.$seq;
  557. }else{
  558. // 当设置的分表字段不在查询条件或者数据中
  559. // 进行联合查询,必须设定 partition['num']
  560. $tableName = array();
  561. for($i=0;$i<$this->partition['num'];$i++)
  562. $tableName[] = 'SELECT * FROM '.$this->getTableName().'_'.($i+1);
  563. $tableName = '( '.implode(" UNION ",$tableName).') AS '.$this->name;
  564. return $tableName;
  565. }
  566. }
  567. }