Model.class.php 35 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083
  1. <?php
  2. // +----------------------------------------------------------------------
  3. // | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
  4. // +----------------------------------------------------------------------
  5. // | Copyright (c) 2006-2012 http://thinkphp.cn All rights reserved.
  6. // +----------------------------------------------------------------------
  7. // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
  8. // +----------------------------------------------------------------------
  9. // | Author: liu21st <liu21st@gmail.com>
  10. // +----------------------------------------------------------------------
  11. /**
  12. * ThinkPHP 精简模式Model模型类
  13. * 只支持CURD和连贯操作 以及常用查询 去掉回调接口
  14. * @category Think
  15. * @package Think
  16. * @subpackage Core
  17. * @author liu21st <liu21st@gmail.com>
  18. */
  19. class Model {
  20. // 操作状态
  21. const MODEL_INSERT = 1; // 插入模型数据
  22. const MODEL_UPDATE = 2; // 更新模型数据
  23. const MODEL_BOTH = 3; // 包含上面两种方式
  24. const MUST_VALIDATE = 1;// 必须验证
  25. const EXISTS_VALIDATE = 0;// 表单存在字段则验证
  26. const VALUE_VALIDATE = 2;// 表单值不为空则验证
  27. // 当前数据库操作对象
  28. protected $db = null;
  29. // 主键名称
  30. protected $pk = 'id';
  31. // 数据表前缀
  32. protected $tablePrefix = '';
  33. // 模型名称
  34. protected $name = '';
  35. // 数据库名称
  36. protected $dbName = '';
  37. // 数据表名(不包含表前缀)
  38. protected $tableName = '';
  39. // 实际数据表名(包含表前缀)
  40. protected $trueTableName ='';
  41. // 最近错误信息
  42. protected $error = '';
  43. // 字段信息
  44. protected $fields = array();
  45. // 数据信息
  46. protected $data = array();
  47. // 查询表达式参数
  48. protected $options = array();
  49. protected $_validate = array(); // 自动验证定义
  50. protected $_auto = array(); // 自动完成定义
  51. // 是否自动检测数据表字段信息
  52. protected $autoCheckFields = true;
  53. // 是否批处理验证
  54. protected $patchValidate = false;
  55. /**
  56. * 架构函数
  57. * 取得DB类的实例对象 字段检查
  58. * @param string $name 模型名称
  59. * @param string $tablePrefix 表前缀
  60. * @param mixed $connection 数据库连接信息
  61. * @access public
  62. */
  63. public function __construct($name='',$tablePrefix='',$connection='') {
  64. // 模型初始化
  65. $this->_initialize();
  66. // 获取模型名称
  67. if(!empty($name)) {
  68. if(strpos($name,'.')) { // 支持 数据库名.模型名的 定义
  69. list($this->dbName,$this->name) = explode('.',$name);
  70. }else{
  71. $this->name = $name;
  72. }
  73. }elseif(empty($this->name)){
  74. $this->name = $this->getModelName();
  75. }
  76. if(!empty($tablePrefix)) {
  77. $this->tablePrefix = $tablePrefix;
  78. }
  79. // 设置表前缀
  80. $this->tablePrefix = $this->tablePrefix?$this->tablePrefix:C('DB_PREFIX');
  81. // 数据库初始化操作
  82. // 获取数据库操作对象
  83. // 当前模型有独立的数据库连接信息
  84. $this->db(0,empty($this->connection)?$connection:$this->connection);
  85. // 字段检测
  86. if(!empty($this->name) && $this->autoCheckFields) $this->_checkTableInfo();
  87. }
  88. /**
  89. * 自动检测数据表信息
  90. * @access protected
  91. * @return void
  92. */
  93. protected function _checkTableInfo() {
  94. // 如果不是Model类 自动记录数据表信息
  95. // 只在第一次执行记录
  96. if(empty($this->fields)) {
  97. // 如果数据表字段没有定义则自动获取
  98. if(C('DB_FIELDS_CACHE')) {
  99. $db = $this->dbName?$this->dbName:C('DB_NAME');
  100. $this->fields = F('_fields/'.$db.'.'.$this->name);
  101. if(!$this->fields) $this->flush();
  102. }else{
  103. // 每次都会读取数据表信息
  104. $this->flush();
  105. }
  106. }
  107. }
  108. /**
  109. * 获取字段信息并缓存
  110. * @access public
  111. * @return void
  112. */
  113. public function flush() {
  114. // 缓存不存在则查询数据表信息
  115. $fields = $this->db->getFields($this->getTableName());
  116. if(!$fields) { // 无法获取字段信息
  117. return false;
  118. }
  119. $this->fields = array_keys($fields);
  120. $this->fields['_autoinc'] = false;
  121. foreach ($fields as $key=>$val){
  122. // 记录字段类型
  123. $type[$key] = $val['type'];
  124. if($val['primary']) {
  125. $this->fields['_pk'] = $key;
  126. if($val['autoinc']) $this->fields['_autoinc'] = true;
  127. }
  128. }
  129. // 记录字段类型信息
  130. if(C('DB_FIELDTYPE_CHECK')) $this->fields['_type'] = $type;
  131. // 2008-3-7 增加缓存开关控制
  132. if(C('DB_FIELDS_CACHE')){
  133. // 永久缓存数据表信息
  134. $db = $this->dbName?$this->dbName:C('DB_NAME');
  135. F('_fields/'.$db.'.'.$this->name,$this->fields);
  136. }
  137. }
  138. /**
  139. * 设置数据对象的值
  140. * @access public
  141. * @param string $name 名称
  142. * @param mixed $value 值
  143. * @return void
  144. */
  145. public function __set($name,$value) {
  146. // 设置数据对象属性
  147. $this->data[$name] = $value;
  148. }
  149. /**
  150. * 获取数据对象的值
  151. * @access public
  152. * @param string $name 名称
  153. * @return mixed
  154. */
  155. public function __get($name) {
  156. return isset($this->data[$name])?$this->data[$name]:null;
  157. }
  158. /**
  159. * 检测数据对象的值
  160. * @access public
  161. * @param string $name 名称
  162. * @return boolean
  163. */
  164. public function __isset($name) {
  165. return isset($this->data[$name]);
  166. }
  167. /**
  168. * 销毁数据对象的值
  169. * @access public
  170. * @param string $name 名称
  171. * @return void
  172. */
  173. public function __unset($name) {
  174. unset($this->data[$name]);
  175. }
  176. /**
  177. * 利用__call方法实现一些特殊的Model方法
  178. * @access public
  179. * @param string $method 方法名称
  180. * @param array $args 调用参数
  181. * @return mixed
  182. */
  183. public function __call($method,$args) {
  184. if(in_array(strtolower($method),array('table','where','order','limit','page','alias','having','group','lock','distinct'),true)) {
  185. // 连贯操作的实现
  186. $this->options[strtolower($method)] = $args[0];
  187. return $this;
  188. }elseif(in_array(strtolower($method),array('count','sum','min','max','avg'),true)){
  189. // 统计查询的实现
  190. $field = isset($args[0])?$args[0]:'*';
  191. return $this->getField(strtoupper($method).'('.$field.') AS tp_'.$method);
  192. }elseif(strtolower(substr($method,0,5))=='getby') {
  193. // 根据某个字段获取记录
  194. $field = parse_name(substr($method,5));
  195. $where[$field] = $args[0];
  196. return $this->where($where)->find();
  197. }else{
  198. throw_exception(__CLASS__.':'.$method.L('_METHOD_NOT_EXIST_'));
  199. return;
  200. }
  201. }
  202. // 回调方法 初始化模型
  203. protected function _initialize() {}
  204. /**
  205. * 对保存到数据库的数据进行处理
  206. * @access protected
  207. * @param mixed $data 要操作的数据
  208. * @return boolean
  209. */
  210. protected function _facade($data) {
  211. // 检查非数据字段
  212. if(!empty($this->fields)) {
  213. foreach ($data as $key=>$val){
  214. if(!in_array($key,$this->fields,true)){
  215. unset($data[$key]);
  216. }elseif(C('DB_FIELDTYPE_CHECK') && is_scalar($val)) {
  217. // 字段类型检查
  218. $this->_parseType($data,$key);
  219. }
  220. }
  221. }
  222. return $data;
  223. }
  224. /**
  225. * 新增数据
  226. * @access public
  227. * @param mixed $data 数据
  228. * @param array $options 表达式
  229. * @return mixed
  230. */
  231. public function add($data='',$options=array()) {
  232. if(empty($data)) {
  233. // 没有传递数据,获取当前数据对象的值
  234. if(!empty($this->data)) {
  235. $data = $this->data;
  236. }else{
  237. $this->error = L('_DATA_TYPE_INVALID_');
  238. return false;
  239. }
  240. }
  241. // 分析表达式
  242. $options = $this->_parseOptions($options);
  243. // 数据处理
  244. $data = $this->_facade($data);
  245. // 写入数据到数据库
  246. $result = $this->db->insert($data,$options);
  247. if(false !== $result ) {
  248. $insertId = $this->getLastInsID();
  249. if($insertId) {
  250. // 自增主键返回插入ID
  251. return $insertId;
  252. }
  253. }
  254. return $result;
  255. }
  256. /**
  257. * 保存数据
  258. * @access public
  259. * @param mixed $data 数据
  260. * @param array $options 表达式
  261. * @return boolean
  262. */
  263. public function save($data='',$options=array()) {
  264. if(empty($data)) {
  265. // 没有传递数据,获取当前数据对象的值
  266. if(!empty($this->data)) {
  267. $data = $this->data;
  268. }else{
  269. $this->error = L('_DATA_TYPE_INVALID_');
  270. return false;
  271. }
  272. }
  273. // 数据处理
  274. $data = $this->_facade($data);
  275. // 分析表达式
  276. $options = $this->_parseOptions($options);
  277. if(!isset($options['where']) ) {
  278. // 如果存在主键数据 则自动作为更新条件
  279. if(isset($data[$this->getPk()])) {
  280. $pk = $this->getPk();
  281. $where[$pk] = $data[$pk];
  282. $options['where'] = $where;
  283. unset($data[$pk]);
  284. }else{
  285. // 如果没有任何更新条件则不执行
  286. $this->error = L('_OPERATION_WRONG_');
  287. return false;
  288. }
  289. }
  290. $result = $this->db->update($data,$options);
  291. return $result;
  292. }
  293. /**
  294. * 删除数据
  295. * @access public
  296. * @param mixed $options 表达式
  297. * @return mixed
  298. */
  299. public function delete($options=array()) {
  300. if(empty($options) && empty($this->options['where'])) {
  301. // 如果删除条件为空 则删除当前数据对象所对应的记录
  302. if(!empty($this->data) && isset($this->data[$this->getPk()]))
  303. return $this->delete($this->data[$this->getPk()]);
  304. else
  305. return false;
  306. }
  307. if(is_numeric($options) || is_string($options)) {
  308. // 根据主键删除记录
  309. $pk = $this->getPk();
  310. if(strpos($options,',')) {
  311. $where[$pk] = array('IN', $options);
  312. }else{
  313. $where[$pk] = $options;
  314. $pkValue = $options;
  315. }
  316. $options = array();
  317. $options['where'] = $where;
  318. }
  319. // 分析表达式
  320. $options = $this->_parseOptions($options);
  321. $result= $this->db->delete($options);
  322. // 返回删除记录个数
  323. return $result;
  324. }
  325. /**
  326. * 查询数据集
  327. * @access public
  328. * @param array $options 表达式参数
  329. * @return mixed
  330. */
  331. public function select($options=array()) {
  332. if(is_string($options) || is_numeric($options)) {
  333. // 根据主键查询
  334. $pk = $this->getPk();
  335. if(strpos($options,',')) {
  336. $where[$pk] = array('IN',$options);
  337. }else{
  338. $where[$pk] = $options;
  339. }
  340. $options = array();
  341. $options['where'] = $where;
  342. }
  343. // 分析表达式
  344. $options = $this->_parseOptions($options);
  345. $resultSet = $this->db->select($options);
  346. if(false === $resultSet) {
  347. return false;
  348. }
  349. if(empty($resultSet)) { // 查询结果为空
  350. return null;
  351. }
  352. return $resultSet;
  353. }
  354. /**
  355. * 分析表达式
  356. * @access proteced
  357. * @param array $options 表达式参数
  358. * @return array
  359. */
  360. protected function _parseOptions($options=array()) {
  361. if(is_array($options))
  362. $options = array_merge($this->options,$options);
  363. // 查询过后清空sql表达式组装 避免影响下次查询
  364. $this->options = array();
  365. if(!isset($options['table']))
  366. // 自动获取表名
  367. $options['table'] =$this->getTableName();
  368. if(!empty($options['alias'])) {
  369. $options['table'] .= ' '.$options['alias'];
  370. }
  371. // 字段类型验证
  372. if(C('DB_FIELDTYPE_CHECK')) {
  373. if(isset($options['where']) && is_array($options['where'])) {
  374. // 对数组查询条件进行字段类型检查
  375. foreach ($options['where'] as $key=>$val){
  376. if(in_array($key,$this->fields,true) && is_scalar($val)){
  377. $this->_parseType($options['where'],$key);
  378. }
  379. }
  380. }
  381. }
  382. return $options;
  383. }
  384. /**
  385. * 数据类型检测
  386. * @access protected
  387. * @param mixed $data 数据
  388. * @param string $key 字段名
  389. * @return void
  390. */
  391. protected function _parseType(&$data,$key) {
  392. $fieldType = strtolower($this->fields['_type'][$key]);
  393. if(false !== strpos($fieldType,'int')) {
  394. $data[$key] = intval($data[$key]);
  395. }elseif(false !== strpos($fieldType,'float') || false !== strpos($fieldType,'double')){
  396. $data[$key] = floatval($data[$key]);
  397. }elseif(false !== strpos($fieldType,'bool')){
  398. $data[$key] = (bool)$data[$key];
  399. }
  400. }
  401. /**
  402. * 查询数据
  403. * @access public
  404. * @param mixed $options 表达式参数
  405. * @return mixed
  406. */
  407. public function find($options=array()) {
  408. if(is_numeric($options) || is_string($options)) {
  409. $where[$this->getPk()] =$options;
  410. $options = array();
  411. $options['where'] = $where;
  412. }
  413. // 总是查找一条记录
  414. $options['limit'] = 1;
  415. // 分析表达式
  416. $options = $this->_parseOptions($options);
  417. $resultSet = $this->db->select($options);
  418. if(false === $resultSet) {
  419. return false;
  420. }
  421. if(empty($resultSet)) {// 查询结果为空
  422. return null;
  423. }
  424. $this->data = $resultSet[0];
  425. return $this->data;
  426. }
  427. /**
  428. * 设置记录的某个字段值
  429. * 支持使用数据库字段和方法
  430. * @access public
  431. * @param string|array $field 字段名
  432. * @param string|array $value 字段值
  433. * @return boolean
  434. */
  435. public function setField($field,$value) {
  436. if(is_array($field)) {
  437. $data = $field;
  438. }else{
  439. $data[$field] = $value;
  440. }
  441. return $this->save($data);
  442. }
  443. /**
  444. * 字段值增长
  445. * @access public
  446. * @param string $field 字段名
  447. * @param integer $step 增长值
  448. * @return boolean
  449. */
  450. public function setInc($field,$step=1) {
  451. return $this->setField($field,array('exp',$field.'+'.$step));
  452. }
  453. /**
  454. * 字段值减少
  455. * @access public
  456. * @param string $field 字段名
  457. * @param integer $step 减少值
  458. * @return boolean
  459. */
  460. public function setDec($field,$step=1) {
  461. return $this->setField($field,array('exp',$field.'-'.$step));
  462. }
  463. /**
  464. * 获取一条记录的某个字段值
  465. * @access public
  466. * @param string $field 字段名
  467. * @param string $spea 字段数据间隔符号
  468. * @return mixed
  469. */
  470. public function getField($field,$sepa=null) {
  471. $options['field'] = $field;
  472. $options = $this->_parseOptions($options);
  473. if(strpos($field,',')) { // 多字段
  474. $resultSet = $this->db->select($options);
  475. if(!empty($resultSet)) {
  476. $_field = explode(',', $field);
  477. $field = array_keys($resultSet[0]);
  478. $move = $_field[0]==$_field[1]?false:true;
  479. $key = array_shift($field);
  480. $key2 = array_shift($field);
  481. $cols = array();
  482. $count = count($_field);
  483. foreach ($resultSet as $result){
  484. $name = $result[$key];
  485. if($move) { // 删除键值记录
  486. unset($result[$key]);
  487. }
  488. if(2==$count) {
  489. $cols[$name] = $result[$key2];
  490. }else{
  491. $cols[$name] = is_null($sepa)?$result:implode($sepa,$result);
  492. }
  493. }
  494. return $cols;
  495. }
  496. }else{ // 查找一条记录
  497. $options['limit'] = 1;
  498. $result = $this->db->select($options);
  499. if(!empty($result)) {
  500. return reset($result[0]);
  501. }
  502. }
  503. return null;
  504. }
  505. /**
  506. * 创建数据对象 但不保存到数据库
  507. * @access public
  508. * @param mixed $data 创建数据
  509. * @param string $type 状态
  510. * @return mixed
  511. */
  512. public function create($data='',$type='') {
  513. // 如果没有传值默认取POST数据
  514. if(empty($data)) {
  515. $data = $_POST;
  516. }elseif(is_object($data)){
  517. $data = get_object_vars($data);
  518. }
  519. // 验证数据
  520. if(empty($data) || !is_array($data)) {
  521. $this->error = L('_DATA_TYPE_INVALID_');
  522. return false;
  523. }
  524. // 状态
  525. $type = $type?$type:(!empty($data[$this->getPk()])?self::MODEL_UPDATE:self::MODEL_INSERT);
  526. // 数据自动验证
  527. if(!$this->autoValidation($data,$type)) return false;
  528. // 验证完成生成数据对象
  529. if($this->autoCheckFields) { // 开启字段检测 则过滤非法字段数据
  530. $vo = array();
  531. foreach ($this->fields as $key=>$name){
  532. if(substr($key,0,1)=='_') continue;
  533. $val = isset($data[$name])?$data[$name]:null;
  534. //保证赋值有效
  535. if(!is_null($val)){
  536. $vo[$name] = (MAGIC_QUOTES_GPC && is_string($val))? stripslashes($val) : $val;
  537. }
  538. }
  539. }else{
  540. $vo = $data;
  541. }
  542. // 创建完成对数据进行自动处理
  543. $this->autoOperation($vo,$type);
  544. // 赋值当前数据对象
  545. $this->data = $vo;
  546. // 返回创建的数据以供其他调用
  547. return $vo;
  548. }
  549. /**
  550. * 使用正则验证数据
  551. * @access public
  552. * @param string $value 要验证的数据
  553. * @param string $rule 验证规则
  554. * @return boolean
  555. */
  556. public function regex($value,$rule) {
  557. $validate = array(
  558. 'require'=> '/.+/',
  559. 'email' => '/^\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$/',
  560. 'url' => '/^http:\/\/[A-Za-z0-9]+\.[A-Za-z0-9]+[\/=\?%\-&_~`@[\]\':+!]*([^<>\"\"])*$/',
  561. 'currency' => '/^\d+(\.\d+)?$/',
  562. 'number' => '/^\d+$/',
  563. 'zip' => '/^[1-9]\d{5}$/',
  564. 'integer' => '/^[-\+]?\d+$/',
  565. 'double' => '/^[-\+]?\d+(\.\d+)?$/',
  566. 'english' => '/^[A-Za-z]+$/',
  567. );
  568. // 检查是否有内置的正则表达式
  569. if(isset($validate[strtolower($rule)]))
  570. $rule = $validate[strtolower($rule)];
  571. return preg_match($rule,$value)===1;
  572. }
  573. /**
  574. * 自动表单处理
  575. * @access public
  576. * @param array $data 创建数据
  577. * @param string $type 创建类型
  578. * @return mixed
  579. */
  580. private function autoOperation(&$data,$type) {
  581. // 自动填充
  582. if(!empty($this->_auto)) {
  583. foreach ($this->_auto as $auto){
  584. // 填充因子定义格式
  585. // array('field','填充内容','填充条件','附加规则',[额外参数])
  586. if(empty($auto[2])) $auto[2] = self::MODEL_INSERT; // 默认为新增的时候自动填充
  587. if( $type == $auto[2] || $auto[2] == self::MODEL_BOTH) {
  588. switch($auto[3]) {
  589. case 'function': // 使用函数进行填充 字段的值作为参数
  590. case 'callback': // 使用回调方法
  591. $args = isset($auto[4])?$auto[4]:array();
  592. if(isset($data[$auto[0]])) {
  593. array_unshift($args,$data[$auto[0]]);
  594. }
  595. if('function'==$auto[3]) {
  596. $data[$auto[0]] = call_user_func_array($auto[1], $args);
  597. }else{
  598. $data[$auto[0]] = call_user_func_array(array(&$this,$auto[1]), $args);
  599. }
  600. break;
  601. case 'field': // 用其它字段的值进行填充
  602. $data[$auto[0]] = $data[$auto[1]];
  603. break;
  604. case 'string':
  605. default: // 默认作为字符串填充
  606. $data[$auto[0]] = $auto[1];
  607. }
  608. if(false === $data[$auto[0]] ) unset($data[$auto[0]]);
  609. }
  610. }
  611. }
  612. return $data;
  613. }
  614. /**
  615. * 自动表单验证
  616. * @access protected
  617. * @param array $data 创建数据
  618. * @param string $type 创建类型
  619. * @return boolean
  620. */
  621. protected function autoValidation($data,$type) {
  622. // 属性验证
  623. if(!empty($this->_validate)) { // 如果设置了数据自动验证则进行数据验证
  624. if($this->patchValidate) { // 重置验证错误信息
  625. $this->error = array();
  626. }
  627. foreach($this->_validate as $key=>$val) {
  628. // 验证因子定义格式
  629. // array(field,rule,message,condition,type,when,params)
  630. // 判断是否需要执行验证
  631. if(empty($val[5]) || $val[5]== self::MODEL_BOTH || $val[5]== $type ) {
  632. if(0==strpos($val[2],'{%') && strpos($val[2],'}'))
  633. // 支持提示信息的多语言 使用 {%语言定义} 方式
  634. $val[2] = L(substr($val[2],2,-1));
  635. $val[3] = isset($val[3])?$val[3]:self::EXISTS_VALIDATE;
  636. $val[4] = isset($val[4])?$val[4]:'regex';
  637. // 判断验证条件
  638. switch($val[3]) {
  639. case self::MUST_VALIDATE: // 必须验证 不管表单是否有设置该字段
  640. if(false === $this->_validationField($data,$val))
  641. return false;
  642. break;
  643. case self::VALUE_VALIDATE: // 值不为空的时候才验证
  644. if('' != trim($data[$val[0]]))
  645. if(false === $this->_validationField($data,$val))
  646. return false;
  647. break;
  648. default: // 默认表单存在该字段就验证
  649. if(isset($data[$val[0]]))
  650. if(false === $this->_validationField($data,$val))
  651. return false;
  652. }
  653. }
  654. }
  655. // 批量验证的时候最后返回错误
  656. if(!empty($this->error)) return false;
  657. }
  658. return true;
  659. }
  660. /**
  661. * 验证表单字段 支持批量验证
  662. * 如果批量验证返回错误的数组信息
  663. * @access protected
  664. * @param array $data 创建数据
  665. * @param array $val 验证因子
  666. * @return boolean
  667. */
  668. protected function _validationField($data,$val) {
  669. if(false === $this->_validationFieldItem($data,$val)){
  670. if($this->patchValidate) {
  671. $this->error[$val[0]] = $val[2];
  672. }else{
  673. $this->error = $val[2];
  674. return false;
  675. }
  676. }
  677. return ;
  678. }
  679. /**
  680. * 根据验证因子验证字段
  681. * @access protected
  682. * @param array $data 创建数据
  683. * @param array $val 验证因子
  684. * @return boolean
  685. */
  686. protected function _validationFieldItem($data,$val) {
  687. switch($val[4]) {
  688. case 'function':// 使用函数进行验证
  689. case 'callback':// 调用方法进行验证
  690. $args = isset($val[6])?$val[6]:array();
  691. array_unshift($args,$data[$val[0]]);
  692. if('function'==$val[4]) {
  693. return call_user_func_array($val[1], $args);
  694. }else{
  695. return call_user_func_array(array(&$this, $val[1]), $args);
  696. }
  697. case 'confirm': // 验证两个字段是否相同
  698. return $data[$val[0]] == $data[$val[1]];
  699. case 'unique': // 验证某个值是否唯一
  700. if(is_string($val[0]) && strpos($val[0],','))
  701. $val[0] = explode(',',$val[0]);
  702. $map = array();
  703. if(is_array($val[0])) {
  704. // 支持多个字段验证
  705. foreach ($val[0] as $field)
  706. $map[$field] = $data[$field];
  707. }else{
  708. $map[$val[0]] = $data[$val[0]];
  709. }
  710. if(!empty($data[$this->getPk()])) { // 完善编辑的时候验证唯一
  711. $map[$this->getPk()] = array('neq',$data[$this->getPk()]);
  712. }
  713. if($this->where($map)->find()) return false;
  714. return true;
  715. default: // 检查附加规则
  716. return $this->check($data[$val[0]],$val[1],$val[4]);
  717. }
  718. }
  719. /**
  720. * 验证数据 支持 in between equal length regex expire ip_allow ip_deny
  721. * @access public
  722. * @param string $value 验证数据
  723. * @param mixed $rule 验证表达式
  724. * @param string $type 验证方式 默认为正则验证
  725. * @return boolean
  726. */
  727. public function check($value,$rule,$type='regex'){
  728. switch(strtolower($type)) {
  729. case 'in': // 验证是否在某个指定范围之内 逗号分隔字符串或者数组
  730. $range = is_array($rule)?$rule:explode(',',$rule);
  731. return in_array($value ,$range);
  732. case 'between': // 验证是否在某个范围
  733. list($min,$max) = explode(',',$rule);
  734. return $value>=$min && $value<=$max;
  735. case 'equal': // 验证是否等于某个值
  736. return $value == $rule;
  737. case 'length': // 验证长度
  738. $length = mb_strlen($value,'utf-8'); // 当前数据长度
  739. if(strpos($rule,',')) { // 长度区间
  740. list($min,$max) = explode(',',$rule);
  741. return $length >= $min && $length <= $max;
  742. }else{// 指定长度
  743. return $length == $rule;
  744. }
  745. case 'expire':
  746. list($start,$end) = explode(',',$rule);
  747. if(!is_numeric($start)) $start = strtotime($start);
  748. if(!is_numeric($end)) $end = strtotime($end);
  749. return $_SERVER['REQUEST_TIME'] >= $start && $_SERVER['REQUEST_TIME'] <= $end;
  750. case 'ip_allow': // IP 操作许可验证
  751. return in_array(get_client_ip(),explode(',',$rule));
  752. case 'ip_deny': // IP 操作禁止验证
  753. return !in_array(get_client_ip(),explode(',',$rule));
  754. case 'regex':
  755. default: // 默认使用正则验证 可以使用验证类中定义的验证名称
  756. // 检查附加规则
  757. return $this->regex($value,$rule);
  758. }
  759. }
  760. /**
  761. * SQL查询
  762. * @access public
  763. * @param mixed $sql SQL指令
  764. * @return mixed
  765. */
  766. public function query($sql) {
  767. if(!empty($sql)) {
  768. if(strpos($sql,'__TABLE__'))
  769. $sql = str_replace('__TABLE__',$this->getTableName(),$sql);
  770. return $this->db->query($sql);
  771. }else{
  772. return false;
  773. }
  774. }
  775. /**
  776. * 执行SQL语句
  777. * @access public
  778. * @param string $sql SQL指令
  779. * @return false | integer
  780. */
  781. public function execute($sql) {
  782. if(!empty($sql)) {
  783. if(strpos($sql,'__TABLE__'))
  784. $sql = str_replace('__TABLE__',$this->getTableName(),$sql);
  785. return $this->db->execute($sql);
  786. }else {
  787. return false;
  788. }
  789. }
  790. /**
  791. * 切换当前的数据库连接
  792. * @access public
  793. * @param integer $linkNum 连接序号
  794. * @param mixed $config 数据库连接信息
  795. * @param array $params 模型参数
  796. * @return Model
  797. */
  798. public function db($linkNum,$config='',$params=array()){
  799. static $_db = array();
  800. if(!isset($_db[$linkNum])) {
  801. // 创建一个新的实例
  802. if(!empty($config) && false === strpos($config,'/')) { // 支持读取配置参数
  803. $config = C($config);
  804. }
  805. $_db[$linkNum] = Db::getInstance($config);
  806. }elseif(NULL === $config){
  807. $_db[$linkNum]->close(); // 关闭数据库连接
  808. unset($_db[$linkNum]);
  809. return ;
  810. }
  811. if(!empty($params)) {
  812. if(is_string($params)) parse_str($params,$params);
  813. foreach ($params as $name=>$value){
  814. $this->setProperty($name,$value);
  815. }
  816. }
  817. // 切换数据库连接
  818. $this->db = $_db[$linkNum];
  819. return $this;
  820. }
  821. /**
  822. * 得到当前的数据对象名称
  823. * @access public
  824. * @return string
  825. */
  826. public function getModelName() {
  827. if(empty($this->name))
  828. $this->name = substr(get_class($this),0,-5);
  829. return $this->name;
  830. }
  831. /**
  832. * 得到完整的数据表名
  833. * @access public
  834. * @return string
  835. */
  836. public function getTableName() {
  837. if(empty($this->trueTableName)) {
  838. $tableName = !empty($this->tablePrefix) ? $this->tablePrefix : '';
  839. if(!empty($this->tableName)) {
  840. $tableName .= $this->tableName;
  841. }else{
  842. $tableName .= parse_name($this->name);
  843. }
  844. $this->trueTableName = strtolower($tableName);
  845. }
  846. return (!empty($this->dbName)?$this->dbName.'.':'').$this->trueTableName;
  847. }
  848. /**
  849. * 启动事务
  850. * @access public
  851. * @return void
  852. */
  853. public function startTrans() {
  854. $this->commit();
  855. $this->db->startTrans();
  856. return ;
  857. }
  858. /**
  859. * 提交事务
  860. * @access public
  861. * @return boolean
  862. */
  863. public function commit() {
  864. return $this->db->commit();
  865. }
  866. /**
  867. * 事务回滚
  868. * @access public
  869. * @return boolean
  870. */
  871. public function rollback() {
  872. return $this->db->rollback();
  873. }
  874. /**
  875. * 返回模型的错误信息
  876. * @access public
  877. * @return string
  878. */
  879. public function getError() {
  880. return $this->error;
  881. }
  882. /**
  883. * 返回数据库的错误信息
  884. * @access public
  885. * @return string
  886. */
  887. public function getDbError() {
  888. return $this->db->getError();
  889. }
  890. /**
  891. * 返回最后插入的ID
  892. * @access public
  893. * @return string
  894. */
  895. public function getLastInsID() {
  896. return $this->db->getLastInsID();
  897. }
  898. /**
  899. * 返回最后执行的sql语句
  900. * @access public
  901. * @return string
  902. */
  903. public function getLastSql() {
  904. return $this->db->getLastSql();
  905. }
  906. // 鉴于getLastSql比较常用 增加_sql 别名
  907. public function _sql(){
  908. return $this->getLastSql();
  909. }
  910. /**
  911. * 获取主键名称
  912. * @access public
  913. * @return string
  914. */
  915. public function getPk() {
  916. return isset($this->fields['_pk'])?$this->fields['_pk']:$this->pk;
  917. }
  918. /**
  919. * 获取数据表字段信息
  920. * @access public
  921. * @return array
  922. */
  923. public function getDbFields(){
  924. if($this->fields) {
  925. $fields = $this->fields;
  926. unset($fields['_autoinc'],$fields['_pk'],$fields['_type']);
  927. return $fields;
  928. }
  929. return false;
  930. }
  931. /**
  932. * 指定查询字段 支持字段排除
  933. * @access public
  934. * @param mixed $field
  935. * @param boolean $except 是否排除
  936. * @return Model
  937. */
  938. public function field($field,$except=false){
  939. if($except) {// 字段排除
  940. if(is_string($field)) {
  941. $field = explode(',',$field);
  942. }
  943. $fields = $this->getDbFields();
  944. $field = $fields?array_diff($fields,$field):$field;
  945. }
  946. $this->options['field'] = $field;
  947. return $this;
  948. }
  949. /**
  950. * 设置数据对象值
  951. * @access public
  952. * @param mixed $data 数据
  953. * @return Model
  954. */
  955. public function data($data){
  956. if(is_object($data)){
  957. $data = get_object_vars($data);
  958. }elseif(is_string($data)){
  959. parse_str($data,$data);
  960. }elseif(!is_array($data)){
  961. throw_exception(L('_DATA_TYPE_INVALID_'));
  962. }
  963. $this->data = $data;
  964. return $this;
  965. }
  966. /**
  967. * 查询SQL组装 join
  968. * @access public
  969. * @param mixed $join
  970. * @return Model
  971. */
  972. public function join($join) {
  973. if(is_array($join))
  974. $this->options['join'] = $join;
  975. else
  976. $this->options['join'][] = $join;
  977. return $this;
  978. }
  979. /**
  980. * 查询SQL组装 union
  981. * @access public
  982. * @param array $union
  983. * @return Model
  984. */
  985. public function union($union) {
  986. if(empty($union)) return $this;
  987. // 转换union表达式
  988. if($union instanceof Model) {
  989. $options = $union->getProperty('options');
  990. if(!isset($options['table'])){
  991. // 自动获取表名
  992. $options['table'] =$union->getTableName();
  993. }
  994. if(!isset($options['field'])) {
  995. $options['field'] =$this->options['field'];
  996. }
  997. }elseif(is_object($union)) {
  998. $options = get_object_vars($union);
  999. }elseif(!is_array($union)){
  1000. throw_exception(L('_DATA_TYPE_INVALID_'));
  1001. }
  1002. $this->options['union'][] = $options;
  1003. return $this;
  1004. }
  1005. /**
  1006. * 设置模型的属性值
  1007. * @access public
  1008. * @param string $name 名称
  1009. * @param mixed $value 值
  1010. * @return Model
  1011. */
  1012. public function setProperty($name,$value) {
  1013. if(property_exists($this,$name))
  1014. $this->$name = $value;
  1015. return $this;
  1016. }
  1017. /**
  1018. * 获取模型的属性值
  1019. * @access public
  1020. * @param string $name 名称
  1021. * @return mixed
  1022. */
  1023. public function getProperty($name){
  1024. if(property_exists($this,$name))
  1025. return $this->$name;
  1026. else
  1027. return NULL;
  1028. }
  1029. }