BaseQuery.php 37 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314
  1. <?php
  2. // +----------------------------------------------------------------------
  3. // | ThinkPHP [ WE CAN DO IT JUST THINK ]
  4. // +----------------------------------------------------------------------
  5. // | Copyright (c) 2006~2019 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. declare (strict_types = 1);
  12. namespace think\db;
  13. use think\Collection;
  14. use think\db\exception\DataNotFoundException;
  15. use think\db\exception\DbException as Exception;
  16. use think\db\exception\ModelNotFoundException;
  17. use think\helper\Str;
  18. use think\Model;
  19. use think\Paginator;
  20. /**
  21. * 数据查询基础类
  22. */
  23. abstract class BaseQuery
  24. {
  25. use concern\TimeFieldQuery;
  26. use concern\AggregateQuery;
  27. use concern\ModelRelationQuery;
  28. use concern\ResultOperation;
  29. use concern\Transaction;
  30. use concern\WhereQuery;
  31. /**
  32. * 当前数据库连接对象
  33. * @var Connection
  34. */
  35. protected $connection;
  36. /**
  37. * 当前数据表名称(不含前缀)
  38. * @var string
  39. */
  40. protected $name = '';
  41. /**
  42. * 当前数据表主键
  43. * @var string|array
  44. */
  45. protected $pk;
  46. /**
  47. * 当前数据表自增主键
  48. * @var string
  49. */
  50. protected $autoinc;
  51. /**
  52. * 当前数据表前缀
  53. * @var string
  54. */
  55. protected $prefix = '';
  56. /**
  57. * 当前查询参数
  58. * @var array
  59. */
  60. protected $options = [];
  61. /**
  62. * 架构函数
  63. * @access public
  64. * @param ConnectionInterface $connection 数据库连接对象
  65. */
  66. public function __construct(ConnectionInterface $connection)
  67. {
  68. $this->connection = $connection;
  69. $this->prefix = $this->connection->getConfig('prefix');
  70. }
  71. /**
  72. * 利用__call方法实现一些特殊的Model方法
  73. * @access public
  74. * @param string $method 方法名称
  75. * @param array $args 调用参数
  76. * @return mixed
  77. * @throws Exception
  78. */
  79. public function __call(string $method, array $args)
  80. {
  81. if (strtolower(substr($method, 0, 5)) == 'getby') {
  82. // 根据某个字段获取记录
  83. $field = Str::snake(substr($method, 5));
  84. return $this->where($field, '=', $args[0])->find();
  85. } elseif (strtolower(substr($method, 0, 10)) == 'getfieldby') {
  86. // 根据某个字段获取记录的某个值
  87. $name = Str::snake(substr($method, 10));
  88. return $this->where($name, '=', $args[0])->value($args[1]);
  89. } elseif (strtolower(substr($method, 0, 7)) == 'whereor') {
  90. $name = Str::snake(substr($method, 7));
  91. array_unshift($args, $name);
  92. return call_user_func_array([$this, 'whereOr'], $args);
  93. } elseif (strtolower(substr($method, 0, 5)) == 'where') {
  94. $name = Str::snake(substr($method, 5));
  95. array_unshift($args, $name);
  96. return call_user_func_array([$this, 'where'], $args);
  97. } elseif ($this->model && method_exists($this->model, 'scope' . $method)) {
  98. // 动态调用命名范围
  99. $method = 'scope' . $method;
  100. array_unshift($args, $this);
  101. call_user_func_array([$this->model, $method], $args);
  102. return $this;
  103. } else {
  104. throw new Exception('method not exist:' . static::class . '->' . $method);
  105. }
  106. }
  107. /**
  108. * 创建一个新的查询对象
  109. * @access public
  110. * @return BaseQuery
  111. */
  112. public function newQuery(): BaseQuery
  113. {
  114. $query = new static($this->connection);
  115. if ($this->model) {
  116. $query->model($this->model);
  117. }
  118. if (isset($this->options['table'])) {
  119. $query->table($this->options['table']);
  120. } else {
  121. $query->name($this->name);
  122. }
  123. if (!empty($this->options['json'])) {
  124. $query->json($this->options['json'], $this->options['json_assoc']);
  125. }
  126. if (isset($this->options['field_type'])) {
  127. $query->setFieldType($this->options['field_type']);
  128. }
  129. return $query;
  130. }
  131. /**
  132. * 获取当前的数据库Connection对象
  133. * @access public
  134. * @return ConnectionInterface
  135. */
  136. public function getConnection()
  137. {
  138. return $this->connection;
  139. }
  140. /**
  141. * 指定当前数据表名(不含前缀)
  142. * @access public
  143. * @param string $name 不含前缀的数据表名字
  144. * @return $this
  145. */
  146. public function name(string $name)
  147. {
  148. $this->name = $name;
  149. return $this;
  150. }
  151. /**
  152. * 获取当前的数据表名称
  153. * @access public
  154. * @return string
  155. */
  156. public function getName(): string
  157. {
  158. return $this->name ?: $this->model->getName();
  159. }
  160. /**
  161. * 获取数据库的配置参数
  162. * @access public
  163. * @param string $name 参数名称
  164. * @return mixed
  165. */
  166. public function getConfig(string $name = '')
  167. {
  168. return $this->connection->getConfig($name);
  169. }
  170. /**
  171. * 得到当前或者指定名称的数据表
  172. * @access public
  173. * @param string $name 不含前缀的数据表名字
  174. * @return mixed
  175. */
  176. public function getTable(string $name = '')
  177. {
  178. if (empty($name) && isset($this->options['table'])) {
  179. return $this->options['table'];
  180. }
  181. $name = $name ?: $this->name;
  182. return $this->prefix . Str::snake($name);
  183. }
  184. /**
  185. * 设置字段类型信息
  186. * @access public
  187. * @param array $type 字段类型信息
  188. * @return $this
  189. */
  190. public function setFieldType(array $type)
  191. {
  192. $this->options['field_type'] = $type;
  193. return $this;
  194. }
  195. /**
  196. * 获取最近一次查询的sql语句
  197. * @access public
  198. * @return string
  199. */
  200. public function getLastSql(): string
  201. {
  202. return $this->connection->getLastSql();
  203. }
  204. /**
  205. * 获取返回或者影响的记录数
  206. * @access public
  207. * @return integer
  208. */
  209. public function getNumRows(): int
  210. {
  211. return $this->connection->getNumRows();
  212. }
  213. /**
  214. * 获取最近插入的ID
  215. * @access public
  216. * @param string $sequence 自增序列名
  217. * @return mixed
  218. */
  219. public function getLastInsID(string $sequence = null)
  220. {
  221. return $this->connection->getLastInsID($this, $sequence);
  222. }
  223. /**
  224. * 得到某个字段的值
  225. * @access public
  226. * @param string $field 字段名
  227. * @param mixed $default 默认值
  228. * @return mixed
  229. */
  230. public function value(string $field, $default = null)
  231. {
  232. $result = $this->connection->value($this, $field, $default);
  233. $array[$field] = $result;
  234. $this->result($array);
  235. return $array[$field];
  236. }
  237. /**
  238. * 得到某个列的数组
  239. * @access public
  240. * @param string|array $field 字段名 多个字段用逗号分隔
  241. * @param string $key 索引
  242. * @return array
  243. */
  244. public function column($field, string $key = ''): array
  245. {
  246. $result = $this->connection->column($this, $field, $key);
  247. if (count($result) != count($result, 1)) {
  248. $this->resultSet($result, false);
  249. }
  250. return $result;
  251. }
  252. /**
  253. * 查询SQL组装 union
  254. * @access public
  255. * @param mixed $union UNION
  256. * @param boolean $all 是否适用UNION ALL
  257. * @return $this
  258. */
  259. public function union($union, bool $all = false)
  260. {
  261. $this->options['union']['type'] = $all ? 'UNION ALL' : 'UNION';
  262. if (is_array($union)) {
  263. $this->options['union'] = array_merge($this->options['union'], $union);
  264. } else {
  265. $this->options['union'][] = $union;
  266. }
  267. return $this;
  268. }
  269. /**
  270. * 查询SQL组装 union all
  271. * @access public
  272. * @param mixed $union UNION数据
  273. * @return $this
  274. */
  275. public function unionAll($union)
  276. {
  277. return $this->union($union, true);
  278. }
  279. /**
  280. * 指定查询字段
  281. * @access public
  282. * @param mixed $field 字段信息
  283. * @return $this
  284. */
  285. public function field($field)
  286. {
  287. if (empty($field)) {
  288. return $this;
  289. } elseif ($field instanceof Raw) {
  290. $this->options['field'][] = $field;
  291. return $this;
  292. }
  293. if (is_string($field)) {
  294. if (preg_match('/[\<\'\"\(]/', $field)) {
  295. return $this->fieldRaw($field);
  296. }
  297. $field = array_map('trim', explode(',', $field));
  298. }
  299. if (true === $field) {
  300. // 获取全部字段
  301. $fields = $this->getTableFields();
  302. $field = $fields ?: ['*'];
  303. }
  304. if (isset($this->options['field'])) {
  305. $field = array_merge((array) $this->options['field'], $field);
  306. }
  307. $this->options['field'] = array_unique($field, SORT_REGULAR);
  308. return $this;
  309. }
  310. /**
  311. * 指定要排除的查询字段
  312. * @access public
  313. * @param array|string $field 要排除的字段
  314. * @return $this
  315. */
  316. public function withoutField($field)
  317. {
  318. if (empty($field)) {
  319. return $this;
  320. }
  321. if (is_string($field)) {
  322. $field = array_map('trim', explode(',', $field));
  323. }
  324. // 字段排除
  325. $fields = $this->getTableFields();
  326. $field = $fields ? array_diff($fields, $field) : $field;
  327. if (isset($this->options['field'])) {
  328. $field = array_merge((array) $this->options['field'], $field);
  329. }
  330. $this->options['field'] = array_unique($field, SORT_REGULAR);
  331. return $this;
  332. }
  333. /**
  334. * 指定其它数据表的查询字段
  335. * @access public
  336. * @param mixed $field 字段信息
  337. * @param string $tableName 数据表名
  338. * @param string $prefix 字段前缀
  339. * @param string $alias 别名前缀
  340. * @return $this
  341. */
  342. public function tableField($field, string $tableName, string $prefix = '', string $alias = '')
  343. {
  344. if (empty($field)) {
  345. return $this;
  346. }
  347. if (is_string($field)) {
  348. $field = array_map('trim', explode(',', $field));
  349. }
  350. if (true === $field) {
  351. // 获取全部字段
  352. $fields = $this->getTableFields($tableName);
  353. $field = $fields ?: ['*'];
  354. }
  355. // 添加统一的前缀
  356. $prefix = $prefix ?: $tableName;
  357. foreach ($field as $key => &$val) {
  358. if (is_numeric($key) && $alias) {
  359. $field[$prefix . '.' . $val] = $alias . $val;
  360. unset($field[$key]);
  361. } elseif (is_numeric($key)) {
  362. $val = $prefix . '.' . $val;
  363. }
  364. }
  365. if (isset($this->options['field'])) {
  366. $field = array_merge((array) $this->options['field'], $field);
  367. }
  368. $this->options['field'] = array_unique($field, SORT_REGULAR);
  369. return $this;
  370. }
  371. /**
  372. * 设置数据
  373. * @access public
  374. * @param array $data 数据
  375. * @return $this
  376. */
  377. public function data(array $data)
  378. {
  379. $this->options['data'] = $data;
  380. return $this;
  381. }
  382. /**
  383. * 去除查询参数
  384. * @access public
  385. * @param string $option 参数名 留空去除所有参数
  386. * @return $this
  387. */
  388. public function removeOption(string $option = '')
  389. {
  390. if ('' === $option) {
  391. $this->options = [];
  392. $this->bind = [];
  393. } elseif (isset($this->options[$option])) {
  394. unset($this->options[$option]);
  395. }
  396. return $this;
  397. }
  398. /**
  399. * 指定查询数量
  400. * @access public
  401. * @param int $offset 起始位置
  402. * @param int $length 查询数量
  403. * @return $this
  404. */
  405. public function limit(int $offset, int $length = null)
  406. {
  407. $this->options['limit'] = $offset . ($length ? ',' . $length : '');
  408. return $this;
  409. }
  410. /**
  411. * 指定分页
  412. * @access public
  413. * @param int $page 页数
  414. * @param int $listRows 每页数量
  415. * @return $this
  416. */
  417. public function page(int $page, int $listRows = null)
  418. {
  419. $this->options['page'] = [$page, $listRows];
  420. return $this;
  421. }
  422. /**
  423. * 指定当前操作的数据表
  424. * @access public
  425. * @param mixed $table 表名
  426. * @return $this
  427. */
  428. public function table($table)
  429. {
  430. if (is_string($table)) {
  431. if (strpos($table, ')')) {
  432. // 子查询
  433. } elseif (false === strpos($table, ',')) {
  434. if (strpos($table, ' ')) {
  435. [$item, $alias] = explode(' ', $table);
  436. $table = [];
  437. $this->alias([$item => $alias]);
  438. $table[$item] = $alias;
  439. }
  440. } else {
  441. $tables = explode(',', $table);
  442. $table = [];
  443. foreach ($tables as $item) {
  444. $item = trim($item);
  445. if (strpos($item, ' ')) {
  446. [$item, $alias] = explode(' ', $item);
  447. $this->alias([$item => $alias]);
  448. $table[$item] = $alias;
  449. } else {
  450. $table[] = $item;
  451. }
  452. }
  453. }
  454. } elseif (is_array($table)) {
  455. $tables = $table;
  456. $table = [];
  457. foreach ($tables as $key => $val) {
  458. if (is_numeric($key)) {
  459. $table[] = $val;
  460. } else {
  461. $this->alias([$key => $val]);
  462. $table[$key] = $val;
  463. }
  464. }
  465. }
  466. $this->options['table'] = $table;
  467. return $this;
  468. }
  469. /**
  470. * 指定排序 order('id','desc') 或者 order(['id'=>'desc','create_time'=>'desc'])
  471. * @access public
  472. * @param string|array|Raw $field 排序字段
  473. * @param string $order 排序
  474. * @return $this
  475. */
  476. public function order($field, string $order = '')
  477. {
  478. if (empty($field)) {
  479. return $this;
  480. } elseif ($field instanceof Raw) {
  481. $this->options['order'][] = $field;
  482. return $this;
  483. }
  484. if (is_string($field)) {
  485. if (!empty($this->options['via'])) {
  486. $field = $this->options['via'] . '.' . $field;
  487. }
  488. if (strpos($field, ',')) {
  489. $field = array_map('trim', explode(',', $field));
  490. } else {
  491. $field = empty($order) ? $field : [$field => $order];
  492. }
  493. } elseif (!empty($this->options['via'])) {
  494. foreach ($field as $key => $val) {
  495. if (is_numeric($key)) {
  496. $field[$key] = $this->options['via'] . '.' . $val;
  497. } else {
  498. $field[$this->options['via'] . '.' . $key] = $val;
  499. unset($field[$key]);
  500. }
  501. }
  502. }
  503. if (!isset($this->options['order'])) {
  504. $this->options['order'] = [];
  505. }
  506. if (is_array($field)) {
  507. $this->options['order'] = array_merge($this->options['order'], $field);
  508. } else {
  509. $this->options['order'][] = $field;
  510. }
  511. return $this;
  512. }
  513. /**
  514. * 分页查询
  515. * @access public
  516. * @param int|array $listRows 每页数量 数组表示配置参数
  517. * @param int|bool $simple 是否简洁模式或者总记录数
  518. * @return Paginator
  519. * @throws Exception
  520. */
  521. public function paginate($listRows = null, $simple = false): Paginator
  522. {
  523. if (is_int($simple)) {
  524. $total = $simple;
  525. $simple = false;
  526. }
  527. $defaultConfig = [
  528. 'query' => [], //url额外参数
  529. 'fragment' => '', //url锚点
  530. 'var_page' => 'page', //分页变量
  531. 'list_rows' => 15, //每页数量
  532. ];
  533. if (is_array($listRows)) {
  534. $config = array_merge($defaultConfig, $listRows);
  535. $listRows = intval($config['list_rows']);
  536. } else {
  537. $config = $defaultConfig;
  538. $listRows = intval($listRows ?: $config['list_rows']);
  539. }
  540. $page = isset($config['page']) ? (int) $config['page'] : Paginator::getCurrentPage($config['var_page']);
  541. $page = $page < 1 ? 1 : $page;
  542. $config['path'] = $config['path'] ?? Paginator::getCurrentPath();
  543. if (!isset($total) && !$simple) {
  544. $options = $this->getOptions();
  545. unset($this->options['order'], $this->options['cache'], $this->options['limit'], $this->options['page'], $this->options['field']);
  546. $bind = $this->bind;
  547. $total = $this->count();
  548. if ($total > 0) {
  549. $results = $this->options($options)->bind($bind)->page($page, $listRows)->select();
  550. } else {
  551. if (!empty($this->model)) {
  552. $results = new \think\model\Collection([]);
  553. } else {
  554. $results = new \think\Collection([]);
  555. }
  556. }
  557. } elseif ($simple) {
  558. $results = $this->limit(($page - 1) * $listRows, $listRows + 1)->select();
  559. $total = null;
  560. } else {
  561. $results = $this->page($page, $listRows)->select();
  562. }
  563. $this->removeOption('limit');
  564. $this->removeOption('page');
  565. return Paginator::make($results, $listRows, $page, $total, $simple, $config);
  566. }
  567. /**
  568. * 根据数字类型字段进行分页查询(大数据)
  569. * @access public
  570. * @param int|array $listRows 每页数量或者分页配置
  571. * @param string $key 分页索引键
  572. * @param string $sort 索引键排序 asc|desc
  573. * @return Paginator
  574. * @throws Exception
  575. */
  576. public function paginateX($listRows = null, string $key = null, string $sort = null): Paginator
  577. {
  578. $defaultConfig = [
  579. 'query' => [], //url额外参数
  580. 'fragment' => '', //url锚点
  581. 'var_page' => 'page', //分页变量
  582. 'list_rows' => 15, //每页数量
  583. ];
  584. $config = is_array($listRows) ? array_merge($defaultConfig, $listRows) : $defaultConfig;
  585. $listRows = is_int($listRows) ? $listRows : (int) $config['list_rows'];
  586. $page = isset($config['page']) ? (int) $config['page'] : Paginator::getCurrentPage($config['var_page']);
  587. $page = $page < 1 ? 1 : $page;
  588. $config['path'] = $config['path'] ?? Paginator::getCurrentPath();
  589. $key = $key ?: $this->getPk();
  590. $options = $this->getOptions();
  591. if (is_null($sort)) {
  592. $order = $options['order'] ?? '';
  593. if (!empty($order)) {
  594. $sort = $order[$key] ?? 'desc';
  595. } else {
  596. $this->order($key, 'desc');
  597. $sort = 'desc';
  598. }
  599. } else {
  600. $this->order($key, $sort);
  601. }
  602. $newOption = $options;
  603. unset($newOption['field'], $newOption['page']);
  604. $data = $this->newQuery()
  605. ->options($newOption)
  606. ->field($key)
  607. ->where(true)
  608. ->order($key, $sort)
  609. ->limit(1)
  610. ->find();
  611. $result = $data[$key] ?? 0;
  612. if (is_numeric($result)) {
  613. $lastId = 'asc' == $sort ? ($result - 1) + ($page - 1) * $listRows : ($result + 1) - ($page - 1) * $listRows;
  614. } else {
  615. throw new Exception('not support type');
  616. }
  617. $results = $this->when($lastId, function ($query) use ($key, $sort, $lastId) {
  618. $query->where($key, 'asc' == $sort ? '>' : '<', $lastId);
  619. })
  620. ->limit($listRows)
  621. ->select();
  622. $this->options($options);
  623. return Paginator::make($results, $listRows, $page, null, true, $config);
  624. }
  625. /**
  626. * 根据最后ID查询更多N个数据
  627. * @access public
  628. * @param int $limit LIMIT
  629. * @param int|string $lastId LastId
  630. * @param string $key 分页索引键 默认为主键
  631. * @param string $sort 索引键排序 asc|desc
  632. * @return array
  633. * @throws Exception
  634. */
  635. public function more(int $limit, $lastId = null, string $key = null, string $sort = null): array
  636. {
  637. $key = $key ?: $this->getPk();
  638. if (is_null($sort)) {
  639. $order = $this->getOptions('order');
  640. if (!empty($order)) {
  641. $sort = $order[$key] ?? 'desc';
  642. } else {
  643. $this->order($key, 'desc');
  644. $sort = 'desc';
  645. }
  646. } else {
  647. $this->order($key, $sort);
  648. }
  649. $result = $this->when($lastId, function ($query) use ($key, $sort, $lastId) {
  650. $query->where($key, 'asc' == $sort ? '>' : '<', $lastId);
  651. })->limit($limit)->select();
  652. $last = $result->last();
  653. $result->first();
  654. return [
  655. 'data' => $result,
  656. 'lastId' => $last ? $last[$key] : null,
  657. ];
  658. }
  659. /**
  660. * 查询缓存 数据为空不缓存
  661. * @access public
  662. * @param mixed $key 缓存key
  663. * @param integer|\DateTime $expire 缓存有效期
  664. * @param string|array $tag 缓存标签
  665. * @param bool $always 始终缓存
  666. * @return $this
  667. */
  668. public function cache($key = true, $expire = null, $tag = null, bool $always = false)
  669. {
  670. if (false === $key || !$this->getConnection()->getCache()) {
  671. return $this;
  672. }
  673. if ($key instanceof \DateTimeInterface || $key instanceof \DateInterval || (is_int($key) && is_null($expire))) {
  674. $expire = $key;
  675. $key = true;
  676. }
  677. $this->options['cache'] = [$key, $expire, $tag];
  678. $this->options['cache_always'] = $always;
  679. return $this;
  680. }
  681. /**
  682. * 查询缓存 允许缓存空数据
  683. * @access public
  684. * @param mixed $key 缓存key
  685. * @param integer|\DateTime $expire 缓存有效期
  686. * @param string|array $tag 缓存标签
  687. * @return $this
  688. */
  689. public function cacheAlways($key = true, $expire = null, $tag = null)
  690. {
  691. return $this->cache($key, $expire, $tag, true);
  692. }
  693. /**
  694. * 指定查询lock
  695. * @access public
  696. * @param bool|string $lock 是否lock
  697. * @return $this
  698. */
  699. public function lock($lock = false)
  700. {
  701. $this->options['lock'] = $lock;
  702. if ($lock) {
  703. $this->options['master'] = true;
  704. }
  705. return $this;
  706. }
  707. /**
  708. * 指定数据表别名
  709. * @access public
  710. * @param array|string $alias 数据表别名
  711. * @return $this
  712. */
  713. public function alias($alias)
  714. {
  715. if (is_array($alias)) {
  716. $this->options['alias'] = $alias;
  717. } else {
  718. $table = $this->getTable();
  719. $this->options['alias'][$table] = $alias;
  720. }
  721. return $this;
  722. }
  723. /**
  724. * 设置从主服务器读取数据
  725. * @access public
  726. * @param bool $readMaster 是否从主服务器读取
  727. * @return $this
  728. */
  729. public function master(bool $readMaster = true)
  730. {
  731. $this->options['master'] = $readMaster;
  732. return $this;
  733. }
  734. /**
  735. * 设置是否严格检查字段名
  736. * @access public
  737. * @param bool $strict 是否严格检查字段
  738. * @return $this
  739. */
  740. public function strict(bool $strict = true)
  741. {
  742. $this->options['strict'] = $strict;
  743. return $this;
  744. }
  745. /**
  746. * 设置自增序列名
  747. * @access public
  748. * @param string $sequence 自增序列名
  749. * @return $this
  750. */
  751. public function sequence(string $sequence = null)
  752. {
  753. $this->options['sequence'] = $sequence;
  754. return $this;
  755. }
  756. /**
  757. * 设置JSON字段信息
  758. * @access public
  759. * @param array $json JSON字段
  760. * @param bool $assoc 是否取出数组
  761. * @return $this
  762. */
  763. public function json(array $json = [], bool $assoc = false)
  764. {
  765. $this->options['json'] = $json;
  766. $this->options['json_assoc'] = $assoc;
  767. return $this;
  768. }
  769. /**
  770. * 指定数据表主键
  771. * @access public
  772. * @param string|array $pk 主键
  773. * @return $this
  774. */
  775. public function pk($pk)
  776. {
  777. $this->pk = $pk;
  778. return $this;
  779. }
  780. /**
  781. * 查询参数批量赋值
  782. * @access protected
  783. * @param array $options 表达式参数
  784. * @return $this
  785. */
  786. protected function options(array $options)
  787. {
  788. $this->options = $options;
  789. return $this;
  790. }
  791. /**
  792. * 获取当前的查询参数
  793. * @access public
  794. * @param string $name 参数名
  795. * @return mixed
  796. */
  797. public function getOptions(string $name = '')
  798. {
  799. if ('' === $name) {
  800. return $this->options;
  801. }
  802. return $this->options[$name] ?? null;
  803. }
  804. /**
  805. * 设置当前的查询参数
  806. * @access public
  807. * @param string $option 参数名
  808. * @param mixed $value 参数值
  809. * @return $this
  810. */
  811. public function setOption(string $option, $value)
  812. {
  813. $this->options[$option] = $value;
  814. return $this;
  815. }
  816. /**
  817. * 设置当前字段添加的表别名
  818. * @access public
  819. * @param string $via 临时表别名
  820. * @return $this
  821. */
  822. public function via(string $via = '')
  823. {
  824. $this->options['via'] = $via;
  825. return $this;
  826. }
  827. /**
  828. * 保存记录 自动判断insert或者update
  829. * @access public
  830. * @param array $data 数据
  831. * @param bool $forceInsert 是否强制insert
  832. * @return integer
  833. */
  834. public function save(array $data = [], bool $forceInsert = false)
  835. {
  836. if ($forceInsert) {
  837. return $this->insert($data);
  838. }
  839. $this->options['data'] = array_merge($this->options['data'] ?? [], $data);
  840. if (!empty($this->options['where'])) {
  841. $isUpdate = true;
  842. } else {
  843. $isUpdate = $this->parseUpdateData($this->options['data']);
  844. }
  845. return $isUpdate ? $this->update() : $this->insert();
  846. }
  847. /**
  848. * 插入记录
  849. * @access public
  850. * @param array $data 数据
  851. * @param boolean $getLastInsID 返回自增主键
  852. * @return integer|string
  853. */
  854. public function insert(array $data = [], bool $getLastInsID = false)
  855. {
  856. if (!empty($data)) {
  857. $this->options['data'] = $data;
  858. }
  859. return $this->connection->insert($this, $getLastInsID);
  860. }
  861. /**
  862. * 插入记录并获取自增ID
  863. * @access public
  864. * @param array $data 数据
  865. * @return integer|string
  866. */
  867. public function insertGetId(array $data)
  868. {
  869. return $this->insert($data, true);
  870. }
  871. /**
  872. * 批量插入记录
  873. * @access public
  874. * @param array $dataSet 数据集
  875. * @param integer $limit 每次写入数据限制
  876. * @return integer
  877. */
  878. public function insertAll(array $dataSet = [], int $limit = 0): int
  879. {
  880. if (empty($dataSet)) {
  881. $dataSet = $this->options['data'] ?? [];
  882. }
  883. if (empty($limit) && !empty($this->options['limit']) && is_numeric($this->options['limit'])) {
  884. $limit = (int) $this->options['limit'];
  885. }
  886. return $this->connection->insertAll($this, $dataSet, $limit);
  887. }
  888. /**
  889. * 通过Select方式插入记录
  890. * @access public
  891. * @param array $fields 要插入的数据表字段名
  892. * @param string $table 要插入的数据表名
  893. * @return integer
  894. */
  895. public function selectInsert(array $fields, string $table): int
  896. {
  897. return $this->connection->selectInsert($this, $fields, $table);
  898. }
  899. /**
  900. * 更新记录
  901. * @access public
  902. * @param mixed $data 数据
  903. * @return integer
  904. * @throws Exception
  905. */
  906. public function update(array $data = []): int
  907. {
  908. if (!empty($data)) {
  909. $this->options['data'] = array_merge($this->options['data'] ?? [], $data);
  910. }
  911. if (empty($this->options['where'])) {
  912. $this->parseUpdateData($this->options['data']);
  913. }
  914. if (empty($this->options['where']) && $this->model) {
  915. $this->where($this->model->getWhere());
  916. }
  917. if (empty($this->options['where'])) {
  918. // 如果没有任何更新条件则不执行
  919. throw new Exception('miss update condition');
  920. }
  921. return $this->connection->update($this);
  922. }
  923. /**
  924. * 删除记录
  925. * @access public
  926. * @param mixed $data 表达式 true 表示强制删除
  927. * @return int
  928. * @throws Exception
  929. */
  930. public function delete($data = null): int
  931. {
  932. if (!is_null($data) && true !== $data) {
  933. // AR模式分析主键条件
  934. $this->parsePkWhere($data);
  935. }
  936. if (empty($this->options['where']) && $this->model) {
  937. $this->where($this->model->getWhere());
  938. }
  939. if (true !== $data && empty($this->options['where'])) {
  940. // 如果条件为空 不进行删除操作 除非设置 1=1
  941. throw new Exception('delete without condition');
  942. }
  943. if (!empty($this->options['soft_delete'])) {
  944. // 软删除
  945. list($field, $condition) = $this->options['soft_delete'];
  946. if ($condition) {
  947. unset($this->options['soft_delete']);
  948. $this->options['data'] = [$field => $condition];
  949. return $this->connection->update($this);
  950. }
  951. }
  952. $this->options['data'] = $data;
  953. return $this->connection->delete($this);
  954. }
  955. /**
  956. * 查找记录
  957. * @access public
  958. * @param mixed $data 数据
  959. * @return Collection|array|static[]
  960. * @throws Exception
  961. * @throws ModelNotFoundException
  962. * @throws DataNotFoundException
  963. */
  964. public function select($data = null): Collection
  965. {
  966. if (!is_null($data)) {
  967. // 主键条件分析
  968. $this->parsePkWhere($data);
  969. }
  970. $resultSet = $this->connection->select($this);
  971. // 返回结果处理
  972. if (!empty($this->options['fail']) && count($resultSet) == 0) {
  973. $this->throwNotFound();
  974. }
  975. // 数据列表读取后的处理
  976. if (!empty($this->model)) {
  977. // 生成模型对象
  978. $resultSet = $this->resultSetToModelCollection($resultSet);
  979. } else {
  980. $this->resultSet($resultSet);
  981. }
  982. return $resultSet;
  983. }
  984. /**
  985. * 查找单条记录
  986. * @access public
  987. * @param mixed $data 查询数据
  988. * @return array|Model|null|static|mixed
  989. * @throws Exception
  990. * @throws ModelNotFoundException
  991. * @throws DataNotFoundException
  992. */
  993. public function find($data = null)
  994. {
  995. if (!is_null($data)) {
  996. // AR模式分析主键条件
  997. $this->parsePkWhere($data);
  998. }
  999. if (empty($this->options['where']) && empty($this->options['order'])) {
  1000. $result = [];
  1001. } else {
  1002. $result = $this->connection->find($this);
  1003. }
  1004. // 数据处理
  1005. if (empty($result)) {
  1006. return $this->resultToEmpty();
  1007. }
  1008. if (!empty($this->model)) {
  1009. // 返回模型对象
  1010. $this->resultToModel($result);
  1011. } else {
  1012. $this->result($result);
  1013. }
  1014. return $result;
  1015. }
  1016. /**
  1017. * 分析表达式(可用于查询或者写入操作)
  1018. * @access public
  1019. * @return array
  1020. */
  1021. public function parseOptions(): array
  1022. {
  1023. $options = $this->getOptions();
  1024. // 获取数据表
  1025. if (empty($options['table'])) {
  1026. $options['table'] = $this->getTable();
  1027. }
  1028. if (!isset($options['where'])) {
  1029. $options['where'] = [];
  1030. } elseif (isset($options['view'])) {
  1031. // 视图查询条件处理
  1032. $this->parseView($options);
  1033. }
  1034. foreach (['data', 'order', 'join', 'union', 'filter', 'json', 'with_attr', 'with_relation_attr'] as $name) {
  1035. if (!isset($options[$name])) {
  1036. $options[$name] = [];
  1037. }
  1038. }
  1039. if (!isset($options['strict'])) {
  1040. $options['strict'] = $this->connection->getConfig('fields_strict');
  1041. }
  1042. foreach (['master', 'lock', 'fetch_sql', 'array', 'distinct', 'procedure', 'with_cache'] as $name) {
  1043. if (!isset($options[$name])) {
  1044. $options[$name] = false;
  1045. }
  1046. }
  1047. foreach (['group', 'having', 'limit', 'force', 'comment', 'partition', 'duplicate', 'extra'] as $name) {
  1048. if (!isset($options[$name])) {
  1049. $options[$name] = '';
  1050. }
  1051. }
  1052. if (isset($options['page'])) {
  1053. // 根据页数计算limit
  1054. [$page, $listRows] = $options['page'];
  1055. $page = $page > 0 ? $page : 1;
  1056. $listRows = $listRows ?: (is_numeric($options['limit']) ? $options['limit'] : 20);
  1057. $offset = $listRows * ($page - 1);
  1058. $options['limit'] = $offset . ',' . $listRows;
  1059. }
  1060. $this->options = $options;
  1061. return $options;
  1062. }
  1063. /**
  1064. * 分析数据是否存在更新条件
  1065. * @access public
  1066. * @param array $data 数据
  1067. * @return bool
  1068. * @throws Exception
  1069. */
  1070. public function parseUpdateData(&$data): bool
  1071. {
  1072. $pk = $this->getPk();
  1073. $isUpdate = false;
  1074. // 如果存在主键数据 则自动作为更新条件
  1075. if (is_string($pk) && isset($data[$pk])) {
  1076. $this->where($pk, '=', $data[$pk]);
  1077. $this->options['key'] = $data[$pk];
  1078. unset($data[$pk]);
  1079. $isUpdate = true;
  1080. } elseif (is_array($pk)) {
  1081. foreach ($pk as $field) {
  1082. if (isset($data[$field])) {
  1083. $this->where($field, '=', $data[$field]);
  1084. $isUpdate = true;
  1085. } else {
  1086. // 如果缺少复合主键数据则不执行
  1087. throw new Exception('miss complex primary data');
  1088. }
  1089. unset($data[$field]);
  1090. }
  1091. }
  1092. return $isUpdate;
  1093. }
  1094. /**
  1095. * 把主键值转换为查询条件 支持复合主键
  1096. * @access public
  1097. * @param array|string $data 主键数据
  1098. * @return void
  1099. * @throws Exception
  1100. */
  1101. public function parsePkWhere($data): void
  1102. {
  1103. $pk = $this->getPk();
  1104. if (is_string($pk)) {
  1105. // 获取数据表
  1106. if (empty($this->options['table'])) {
  1107. $this->options['table'] = $this->getTable();
  1108. }
  1109. $table = is_array($this->options['table']) ? key($this->options['table']) : $this->options['table'];
  1110. if (!empty($this->options['alias'][$table])) {
  1111. $alias = $this->options['alias'][$table];
  1112. }
  1113. $key = isset($alias) ? $alias . '.' . $pk : $pk;
  1114. // 根据主键查询
  1115. if (is_array($data)) {
  1116. $this->where($key, 'in', $data);
  1117. } else {
  1118. $this->where($key, '=', $data);
  1119. $this->options['key'] = $data;
  1120. }
  1121. }
  1122. }
  1123. /**
  1124. * 获取模型的更新条件
  1125. * @access protected
  1126. * @param array $options 查询参数
  1127. */
  1128. protected function getModelUpdateCondition(array $options)
  1129. {
  1130. return $options['where']['AND'] ?? null;
  1131. }
  1132. }