BaseQuery.php 34 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278
  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 (isset($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. return $this->connection->value($this, $field, $default);
  233. }
  234. /**
  235. * 得到某个列的数组
  236. * @access public
  237. * @param string|array $field 字段名 多个字段用逗号分隔
  238. * @param string $key 索引
  239. * @return array
  240. */
  241. public function column($field, string $key = ''): array
  242. {
  243. return $this->connection->column($this, $field, $key);
  244. }
  245. /**
  246. * 查询SQL组装 union
  247. * @access public
  248. * @param mixed $union UNION
  249. * @param boolean $all 是否适用UNION ALL
  250. * @return $this
  251. */
  252. public function union($union, bool $all = false)
  253. {
  254. $this->options['union']['type'] = $all ? 'UNION ALL' : 'UNION';
  255. if (is_array($union)) {
  256. $this->options['union'] = array_merge($this->options['union'], $union);
  257. } else {
  258. $this->options['union'][] = $union;
  259. }
  260. return $this;
  261. }
  262. /**
  263. * 查询SQL组装 union all
  264. * @access public
  265. * @param mixed $union UNION数据
  266. * @return $this
  267. */
  268. public function unionAll($union)
  269. {
  270. return $this->union($union, true);
  271. }
  272. /**
  273. * 指定查询字段
  274. * @access public
  275. * @param mixed $field 字段信息
  276. * @return $this
  277. */
  278. public function field($field)
  279. {
  280. if (empty($field)) {
  281. return $this;
  282. } elseif ($field instanceof Raw) {
  283. $this->options['field'][] = $field;
  284. return $this;
  285. }
  286. if (is_string($field)) {
  287. if (preg_match('/[\<\'\"\(]/', $field)) {
  288. return $this->fieldRaw($field);
  289. }
  290. $field = array_map('trim', explode(',', $field));
  291. }
  292. if (true === $field) {
  293. // 获取全部字段
  294. $fields = $this->getTableFields();
  295. $field = $fields ?: ['*'];
  296. }
  297. if (isset($this->options['field'])) {
  298. $field = array_merge((array) $this->options['field'], $field);
  299. }
  300. $this->options['field'] = array_unique($field);
  301. return $this;
  302. }
  303. /**
  304. * 指定要排除的查询字段
  305. * @access public
  306. * @param array|string $field 要排除的字段
  307. * @return $this
  308. */
  309. public function withoutField($field)
  310. {
  311. if (empty($field)) {
  312. return $this;
  313. }
  314. if (is_string($field)) {
  315. $field = array_map('trim', explode(',', $field));
  316. }
  317. // 字段排除
  318. $fields = $this->getTableFields();
  319. $field = $fields ? array_diff($fields, $field) : $field;
  320. if (isset($this->options['field'])) {
  321. $field = array_merge((array) $this->options['field'], $field);
  322. }
  323. $this->options['field'] = array_unique($field);
  324. return $this;
  325. }
  326. /**
  327. * 指定其它数据表的查询字段
  328. * @access public
  329. * @param mixed $field 字段信息
  330. * @param string $tableName 数据表名
  331. * @param string $prefix 字段前缀
  332. * @param string $alias 别名前缀
  333. * @return $this
  334. */
  335. public function tableField($field, string $tableName, string $prefix = '', string $alias = '')
  336. {
  337. if (empty($field)) {
  338. return $this;
  339. }
  340. if (is_string($field)) {
  341. $field = array_map('trim', explode(',', $field));
  342. }
  343. if (true === $field) {
  344. // 获取全部字段
  345. $fields = $this->getTableFields($tableName);
  346. $field = $fields ?: ['*'];
  347. }
  348. // 添加统一的前缀
  349. $prefix = $prefix ?: $tableName;
  350. foreach ($field as $key => &$val) {
  351. if (is_numeric($key) && $alias) {
  352. $field[$prefix . '.' . $val] = $alias . $val;
  353. unset($field[$key]);
  354. } elseif (is_numeric($key)) {
  355. $val = $prefix . '.' . $val;
  356. }
  357. }
  358. if (isset($this->options['field'])) {
  359. $field = array_merge((array) $this->options['field'], $field);
  360. }
  361. $this->options['field'] = array_unique($field);
  362. return $this;
  363. }
  364. /**
  365. * 设置数据
  366. * @access public
  367. * @param array $data 数据
  368. * @return $this
  369. */
  370. public function data(array $data)
  371. {
  372. $this->options['data'] = $data;
  373. return $this;
  374. }
  375. /**
  376. * 去除查询参数
  377. * @access public
  378. * @param string $option 参数名 留空去除所有参数
  379. * @return $this
  380. */
  381. public function removeOption(string $option = '')
  382. {
  383. if ('' === $option) {
  384. $this->options = [];
  385. $this->bind = [];
  386. } elseif (isset($this->options[$option])) {
  387. unset($this->options[$option]);
  388. }
  389. return $this;
  390. }
  391. /**
  392. * 指定查询数量
  393. * @access public
  394. * @param int $offset 起始位置
  395. * @param int $length 查询数量
  396. * @return $this
  397. */
  398. public function limit(int $offset, int $length = null)
  399. {
  400. $this->options['limit'] = $offset . ($length ? ',' . $length : '');
  401. return $this;
  402. }
  403. /**
  404. * 指定分页
  405. * @access public
  406. * @param int $page 页数
  407. * @param int $listRows 每页数量
  408. * @return $this
  409. */
  410. public function page(int $page, int $listRows = null)
  411. {
  412. $this->options['page'] = [$page, $listRows];
  413. return $this;
  414. }
  415. /**
  416. * 指定当前操作的数据表
  417. * @access public
  418. * @param mixed $table 表名
  419. * @return $this
  420. */
  421. public function table($table)
  422. {
  423. if (is_string($table)) {
  424. if (strpos($table, ')')) {
  425. // 子查询
  426. } elseif (false === strpos($table, ',')) {
  427. if (strpos($table, ' ')) {
  428. [$item, $alias] = explode(' ', $table);
  429. $table = [];
  430. $this->alias([$item => $alias]);
  431. $table[$item] = $alias;
  432. }
  433. } else {
  434. $tables = explode(',', $table);
  435. $table = [];
  436. foreach ($tables as $item) {
  437. $item = trim($item);
  438. if (strpos($item, ' ')) {
  439. [$item, $alias] = explode(' ', $item);
  440. $this->alias([$item => $alias]);
  441. $table[$item] = $alias;
  442. } else {
  443. $table[] = $item;
  444. }
  445. }
  446. }
  447. } elseif (is_array($table)) {
  448. $tables = $table;
  449. $table = [];
  450. foreach ($tables as $key => $val) {
  451. if (is_numeric($key)) {
  452. $table[] = $val;
  453. } else {
  454. $this->alias([$key => $val]);
  455. $table[$key] = $val;
  456. }
  457. }
  458. }
  459. $this->options['table'] = $table;
  460. return $this;
  461. }
  462. /**
  463. * 指定排序 order('id','desc') 或者 order(['id'=>'desc','create_time'=>'desc'])
  464. * @access public
  465. * @param string|array|Raw $field 排序字段
  466. * @param string $order 排序
  467. * @return $this
  468. */
  469. public function order($field, string $order = '')
  470. {
  471. if (empty($field)) {
  472. return $this;
  473. } elseif ($field instanceof Raw) {
  474. $this->options['order'][] = $field;
  475. return $this;
  476. }
  477. if (is_string($field)) {
  478. if (!empty($this->options['via'])) {
  479. $field = $this->options['via'] . '.' . $field;
  480. }
  481. if (strpos($field, ',')) {
  482. $field = array_map('trim', explode(',', $field));
  483. } else {
  484. $field = empty($order) ? $field : [$field => $order];
  485. }
  486. } elseif (!empty($this->options['via'])) {
  487. foreach ($field as $key => $val) {
  488. if (is_numeric($key)) {
  489. $field[$key] = $this->options['via'] . '.' . $val;
  490. } else {
  491. $field[$this->options['via'] . '.' . $key] = $val;
  492. unset($field[$key]);
  493. }
  494. }
  495. }
  496. if (!isset($this->options['order'])) {
  497. $this->options['order'] = [];
  498. }
  499. if (is_array($field)) {
  500. $this->options['order'] = array_merge($this->options['order'], $field);
  501. } else {
  502. $this->options['order'][] = $field;
  503. }
  504. return $this;
  505. }
  506. /**
  507. * 分页查询
  508. * @access public
  509. * @param int|array $listRows 每页数量 数组表示配置参数
  510. * @param int|bool $simple 是否简洁模式或者总记录数
  511. * @return Paginator
  512. * @throws Exception
  513. */
  514. public function paginate($listRows = null, $simple = false): Paginator
  515. {
  516. if (is_int($simple)) {
  517. $total = $simple;
  518. $simple = false;
  519. }
  520. $defaultConfig = [
  521. 'query' => [], //url额外参数
  522. 'fragment' => '', //url锚点
  523. 'var_page' => 'page', //分页变量
  524. 'list_rows' => 15, //每页数量
  525. ];
  526. if (is_array($listRows)) {
  527. $config = array_merge($defaultConfig, $listRows);
  528. $listRows = intval($config['list_rows']);
  529. } else {
  530. $config = $defaultConfig;
  531. $listRows = intval($listRows ?: $config['list_rows']);
  532. }
  533. $page = isset($config['page']) ? (int) $config['page'] : Paginator::getCurrentPage($config['var_page']);
  534. $page = $page < 1 ? 1 : $page;
  535. $config['path'] = $config['path'] ?? Paginator::getCurrentPath();
  536. if (!isset($total) && !$simple) {
  537. $options = $this->getOptions();
  538. unset($this->options['order'], $this->options['limit'], $this->options['page'], $this->options['field']);
  539. $bind = $this->bind;
  540. $total = $this->count();
  541. $results = $total > 0 ? $this->options($options)->bind($bind)->page($page, $listRows)->select() : [];
  542. } elseif ($simple) {
  543. $results = $this->limit(($page - 1) * $listRows, $listRows + 1)->select();
  544. $total = null;
  545. } else {
  546. $results = $this->page($page, $listRows)->select();
  547. }
  548. $this->removeOption('limit');
  549. $this->removeOption('page');
  550. return Paginator::make($results, $listRows, $page, $total, $simple, $config);
  551. }
  552. /**
  553. * 根据数字类型字段进行分页查询(大数据)
  554. * @access public
  555. * @param int|array $listRows 每页数量或者分页配置
  556. * @param string $key 分页索引键
  557. * @param string $sort 索引键排序 asc|desc
  558. * @return Paginator
  559. * @throws Exception
  560. */
  561. public function paginateX($listRows = null, string $key = null, string $sort = null): Paginator
  562. {
  563. $defaultConfig = [
  564. 'query' => [], //url额外参数
  565. 'fragment' => '', //url锚点
  566. 'var_page' => 'page', //分页变量
  567. 'list_rows' => 15, //每页数量
  568. ];
  569. $config = is_array($listRows) ? array_merge($defaultConfig, $listRows) : $defaultConfig;
  570. $listRows = is_int($listRows) ? $listRows : (int) $config['list_rows'];
  571. $page = isset($config['page']) ? (int) $config['page'] : Paginator::getCurrentPage($config['var_page']);
  572. $page = $page < 1 ? 1 : $page;
  573. $config['path'] = $config['path'] ?? Paginator::getCurrentPath();
  574. $key = $key ?: $this->getPk();
  575. $options = $this->getOptions();
  576. if (is_null($sort)) {
  577. $order = $options['order'] ?? '';
  578. if (!empty($order)) {
  579. $sort = $order[$key] ?? 'desc';
  580. } else {
  581. $this->order($key, 'desc');
  582. $sort = 'desc';
  583. }
  584. } else {
  585. $this->order($key, $sort);
  586. }
  587. $newOption = $options;
  588. unset($newOption['field'], $newOption['page']);
  589. $data = $this->newQuery()
  590. ->options($newOption)
  591. ->field($key)
  592. ->where(true)
  593. ->order($key, $sort)
  594. ->limit(1)
  595. ->find();
  596. $result = $data[$key];
  597. if (is_numeric($result)) {
  598. $lastId = 'asc' == $sort ? ($result - 1) + ($page - 1) * $listRows : ($result + 1) - ($page - 1) * $listRows;
  599. } else {
  600. throw new Exception('not support type');
  601. }
  602. $results = $this->when($lastId, function ($query) use ($key, $sort, $lastId) {
  603. $query->where($key, 'asc' == $sort ? '>' : '<', $lastId);
  604. })
  605. ->limit($listRows)
  606. ->select();
  607. $this->options($options);
  608. return Paginator::make($results, $listRows, $page, null, true, $config);
  609. }
  610. /**
  611. * 根据最后ID查询更多N个数据
  612. * @access public
  613. * @param int $limit LIMIT
  614. * @param int|string $lastId LastId
  615. * @param string $key 分页索引键 默认为主键
  616. * @param string $sort 索引键排序 asc|desc
  617. * @return array
  618. * @throws Exception
  619. */
  620. public function more(int $limit, $lastId = null, string $key = null, string $sort = null): array
  621. {
  622. $key = $key ?: $this->getPk();
  623. if (is_null($sort)) {
  624. $order = $this->getOptions('order');
  625. if (!empty($order)) {
  626. $sort = $order[$key] ?? 'desc';
  627. } else {
  628. $this->order($key, 'desc');
  629. $sort = 'desc';
  630. }
  631. } else {
  632. $this->order($key, $sort);
  633. }
  634. $result = $this->when($lastId, function ($query) use ($key, $sort, $lastId) {
  635. $query->where($key, 'asc' == $sort ? '>' : '<', $lastId);
  636. })->limit($limit)->select();
  637. $last = $result->last();
  638. $result->first();
  639. return [
  640. 'data' => $result,
  641. 'lastId' => $last[$key],
  642. ];
  643. }
  644. /**
  645. * 查询缓存
  646. * @access public
  647. * @param mixed $key 缓存key
  648. * @param integer|\DateTime $expire 缓存有效期
  649. * @param string|array $tag 缓存标签
  650. * @return $this
  651. */
  652. public function cache($key = true, $expire = null, $tag = null)
  653. {
  654. if (false === $key || !$this->getConnection()->getCache()) {
  655. return $this;
  656. }
  657. if ($key instanceof \DateTimeInterface || $key instanceof \DateInterval || (is_int($key) && is_null($expire))) {
  658. $expire = $key;
  659. $key = true;
  660. }
  661. $this->options['cache'] = [$key, $expire, $tag];
  662. return $this;
  663. }
  664. /**
  665. * 指定查询lock
  666. * @access public
  667. * @param bool|string $lock 是否lock
  668. * @return $this
  669. */
  670. public function lock($lock = false)
  671. {
  672. $this->options['lock'] = $lock;
  673. if ($lock) {
  674. $this->options['master'] = true;
  675. }
  676. return $this;
  677. }
  678. /**
  679. * 指定数据表别名
  680. * @access public
  681. * @param array|string $alias 数据表别名
  682. * @return $this
  683. */
  684. public function alias($alias)
  685. {
  686. if (is_array($alias)) {
  687. $this->options['alias'] = $alias;
  688. } else {
  689. $table = $this->getTable();
  690. $this->options['alias'][$table] = $alias;
  691. }
  692. return $this;
  693. }
  694. /**
  695. * 设置从主服务器读取数据
  696. * @access public
  697. * @param bool $readMaster 是否从主服务器读取
  698. * @return $this
  699. */
  700. public function master(bool $readMaster = true)
  701. {
  702. $this->options['master'] = $readMaster;
  703. return $this;
  704. }
  705. /**
  706. * 设置是否严格检查字段名
  707. * @access public
  708. * @param bool $strict 是否严格检查字段
  709. * @return $this
  710. */
  711. public function strict(bool $strict = true)
  712. {
  713. $this->options['strict'] = $strict;
  714. return $this;
  715. }
  716. /**
  717. * 设置自增序列名
  718. * @access public
  719. * @param string $sequence 自增序列名
  720. * @return $this
  721. */
  722. public function sequence(string $sequence = null)
  723. {
  724. $this->options['sequence'] = $sequence;
  725. return $this;
  726. }
  727. /**
  728. * 设置JSON字段信息
  729. * @access public
  730. * @param array $json JSON字段
  731. * @param bool $assoc 是否取出数组
  732. * @return $this
  733. */
  734. public function json(array $json = [], bool $assoc = false)
  735. {
  736. $this->options['json'] = $json;
  737. $this->options['json_assoc'] = $assoc;
  738. return $this;
  739. }
  740. /**
  741. * 指定数据表主键
  742. * @access public
  743. * @param string|array $pk 主键
  744. * @return $this
  745. */
  746. public function pk($pk)
  747. {
  748. $this->pk = $pk;
  749. return $this;
  750. }
  751. /**
  752. * 查询参数批量赋值
  753. * @access protected
  754. * @param array $options 表达式参数
  755. * @return $this
  756. */
  757. protected function options(array $options)
  758. {
  759. $this->options = $options;
  760. return $this;
  761. }
  762. /**
  763. * 获取当前的查询参数
  764. * @access public
  765. * @param string $name 参数名
  766. * @return mixed
  767. */
  768. public function getOptions(string $name = '')
  769. {
  770. if ('' === $name) {
  771. return $this->options;
  772. }
  773. return $this->options[$name] ?? null;
  774. }
  775. /**
  776. * 设置当前的查询参数
  777. * @access public
  778. * @param string $option 参数名
  779. * @param mixed $value 参数值
  780. * @return $this
  781. */
  782. public function setOption(string $option, $value)
  783. {
  784. $this->options[$option] = $value;
  785. return $this;
  786. }
  787. /**
  788. * 设置当前字段添加的表别名
  789. * @access public
  790. * @param string $via 临时表别名
  791. * @return $this
  792. */
  793. public function via(string $via = '')
  794. {
  795. $this->options['via'] = $via;
  796. return $this;
  797. }
  798. /**
  799. * 保存记录 自动判断insert或者update
  800. * @access public
  801. * @param array $data 数据
  802. * @param bool $forceInsert 是否强制insert
  803. * @return integer
  804. */
  805. public function save(array $data = [], bool $forceInsert = false)
  806. {
  807. if ($forceInsert) {
  808. return $this->insert($data);
  809. }
  810. $this->options['data'] = array_merge($this->options['data'] ?? [], $data);
  811. if (!empty($this->options['where'])) {
  812. $isUpdate = true;
  813. } else {
  814. $isUpdate = $this->parseUpdateData($this->options['data']);
  815. }
  816. return $isUpdate ? $this->update() : $this->insert();
  817. }
  818. /**
  819. * 插入记录
  820. * @access public
  821. * @param array $data 数据
  822. * @param boolean $getLastInsID 返回自增主键
  823. * @return integer|string
  824. */
  825. public function insert(array $data = [], bool $getLastInsID = false)
  826. {
  827. if (!empty($data)) {
  828. $this->options['data'] = $data;
  829. }
  830. return $this->connection->insert($this, $getLastInsID);
  831. }
  832. /**
  833. * 插入记录并获取自增ID
  834. * @access public
  835. * @param array $data 数据
  836. * @return integer|string
  837. */
  838. public function insertGetId(array $data)
  839. {
  840. return $this->insert($data, true);
  841. }
  842. /**
  843. * 批量插入记录
  844. * @access public
  845. * @param array $dataSet 数据集
  846. * @param integer $limit 每次写入数据限制
  847. * @return integer
  848. */
  849. public function insertAll(array $dataSet = [], int $limit = 0): int
  850. {
  851. if (empty($dataSet)) {
  852. $dataSet = $this->options['data'] ?? [];
  853. }
  854. if (empty($limit) && !empty($this->options['limit']) && is_numeric($this->options['limit'])) {
  855. $limit = (int) $this->options['limit'];
  856. }
  857. return $this->connection->insertAll($this, $dataSet, $limit);
  858. }
  859. /**
  860. * 通过Select方式插入记录
  861. * @access public
  862. * @param array $fields 要插入的数据表字段名
  863. * @param string $table 要插入的数据表名
  864. * @return integer
  865. */
  866. public function selectInsert(array $fields, string $table): int
  867. {
  868. return $this->connection->selectInsert($this, $fields, $table);
  869. }
  870. /**
  871. * 更新记录
  872. * @access public
  873. * @param mixed $data 数据
  874. * @return integer
  875. * @throws Exception
  876. */
  877. public function update(array $data = []): int
  878. {
  879. if (!empty($data)) {
  880. $this->options['data'] = array_merge($this->options['data'] ?? [], $data);
  881. }
  882. if (empty($this->options['where'])) {
  883. $this->parseUpdateData($this->options['data']);
  884. }
  885. if (empty($this->options['where']) && $this->model) {
  886. $this->where($this->model->getWhere());
  887. }
  888. if (empty($this->options['where'])) {
  889. // 如果没有任何更新条件则不执行
  890. throw new Exception('miss update condition');
  891. }
  892. return $this->connection->update($this);
  893. }
  894. /**
  895. * 删除记录
  896. * @access public
  897. * @param mixed $data 表达式 true 表示强制删除
  898. * @return int
  899. * @throws Exception
  900. */
  901. public function delete($data = null): int
  902. {
  903. if (!is_null($data) && true !== $data) {
  904. // AR模式分析主键条件
  905. $this->parsePkWhere($data);
  906. }
  907. if (empty($this->options['where']) && $this->model) {
  908. $this->where($this->model->getWhere());
  909. }
  910. if (true !== $data && empty($this->options['where'])) {
  911. // 如果条件为空 不进行删除操作 除非设置 1=1
  912. throw new Exception('delete without condition');
  913. }
  914. if (!empty($this->options['soft_delete'])) {
  915. // 软删除
  916. list($field, $condition) = $this->options['soft_delete'];
  917. if ($condition) {
  918. unset($this->options['soft_delete']);
  919. $this->options['data'] = [$field => $condition];
  920. return $this->connection->update($this);
  921. }
  922. }
  923. $this->options['data'] = $data;
  924. return $this->connection->delete($this);
  925. }
  926. /**
  927. * 查找记录
  928. * @access public
  929. * @param mixed $data 数据
  930. * @return Collection|array|static[]
  931. * @throws Exception
  932. * @throws ModelNotFoundException
  933. * @throws DataNotFoundException
  934. */
  935. public function select($data = null): Collection
  936. {
  937. if (!is_null($data)) {
  938. // 主键条件分析
  939. $this->parsePkWhere($data);
  940. }
  941. $resultSet = $this->connection->select($this);
  942. // 返回结果处理
  943. if (!empty($this->options['fail']) && count($resultSet) == 0) {
  944. $this->throwNotFound();
  945. }
  946. // 数据列表读取后的处理
  947. if (!empty($this->model)) {
  948. // 生成模型对象
  949. $resultSet = $this->resultSetToModelCollection($resultSet);
  950. } else {
  951. $this->resultSet($resultSet);
  952. }
  953. return $resultSet;
  954. }
  955. /**
  956. * 查找单条记录
  957. * @access public
  958. * @param mixed $data 查询数据
  959. * @return array|Model|null|static
  960. * @throws Exception
  961. * @throws ModelNotFoundException
  962. * @throws DataNotFoundException
  963. */
  964. public function find($data = null)
  965. {
  966. if (!is_null($data)) {
  967. // AR模式分析主键条件
  968. $this->parsePkWhere($data);
  969. }
  970. if (empty($this->options['where']) && empty($this->options['order'])) {
  971. $result = [];
  972. } else {
  973. $result = $this->connection->find($this);
  974. }
  975. // 数据处理
  976. if (empty($result)) {
  977. return $this->resultToEmpty();
  978. }
  979. if (!empty($this->model)) {
  980. // 返回模型对象
  981. $this->resultToModel($result, $this->options);
  982. } else {
  983. $this->result($result);
  984. }
  985. return $result;
  986. }
  987. /**
  988. * 分析表达式(可用于查询或者写入操作)
  989. * @access public
  990. * @return array
  991. */
  992. public function parseOptions(): array
  993. {
  994. $options = $this->getOptions();
  995. // 获取数据表
  996. if (empty($options['table'])) {
  997. $options['table'] = $this->getTable();
  998. }
  999. if (!isset($options['where'])) {
  1000. $options['where'] = [];
  1001. } elseif (isset($options['view'])) {
  1002. // 视图查询条件处理
  1003. $this->parseView($options);
  1004. }
  1005. foreach (['data', 'order', 'join', 'union'] as $name) {
  1006. if (!isset($options[$name])) {
  1007. $options[$name] = [];
  1008. }
  1009. }
  1010. if (!isset($options['strict'])) {
  1011. $options['strict'] = $this->connection->getConfig('fields_strict');
  1012. }
  1013. foreach (['master', 'lock', 'fetch_sql', 'array', 'distinct', 'procedure'] as $name) {
  1014. if (!isset($options[$name])) {
  1015. $options[$name] = false;
  1016. }
  1017. }
  1018. foreach (['group', 'having', 'limit', 'force', 'comment', 'partition', 'duplicate', 'extra'] as $name) {
  1019. if (!isset($options[$name])) {
  1020. $options[$name] = '';
  1021. }
  1022. }
  1023. if (isset($options['page'])) {
  1024. // 根据页数计算limit
  1025. [$page, $listRows] = $options['page'];
  1026. $page = $page > 0 ? $page : 1;
  1027. $listRows = $listRows ?: (is_numeric($options['limit']) ? $options['limit'] : 20);
  1028. $offset = $listRows * ($page - 1);
  1029. $options['limit'] = $offset . ',' . $listRows;
  1030. }
  1031. $this->options = $options;
  1032. return $options;
  1033. }
  1034. /**
  1035. * 分析数据是否存在更新条件
  1036. * @access public
  1037. * @param array $data 数据
  1038. * @return bool
  1039. * @throws Exception
  1040. */
  1041. public function parseUpdateData(&$data): bool
  1042. {
  1043. $pk = $this->getPk();
  1044. $isUpdate = false;
  1045. // 如果存在主键数据 则自动作为更新条件
  1046. if (is_string($pk) && isset($data[$pk])) {
  1047. $this->where($pk, '=', $data[$pk]);
  1048. $this->options['key'] = $data[$pk];
  1049. unset($data[$pk]);
  1050. $isUpdate = true;
  1051. } elseif (is_array($pk)) {
  1052. foreach ($pk as $field) {
  1053. if (isset($data[$field])) {
  1054. $this->where($field, '=', $data[$field]);
  1055. $isUpdate = true;
  1056. } else {
  1057. // 如果缺少复合主键数据则不执行
  1058. throw new Exception('miss complex primary data');
  1059. }
  1060. unset($data[$field]);
  1061. }
  1062. }
  1063. return $isUpdate;
  1064. }
  1065. /**
  1066. * 把主键值转换为查询条件 支持复合主键
  1067. * @access public
  1068. * @param array|string $data 主键数据
  1069. * @return void
  1070. * @throws Exception
  1071. */
  1072. public function parsePkWhere($data): void
  1073. {
  1074. $pk = $this->getPk();
  1075. if (is_string($pk)) {
  1076. // 获取数据表
  1077. if (empty($this->options['table'])) {
  1078. $this->options['table'] = $this->getTable();
  1079. }
  1080. $table = is_array($this->options['table']) ? key($this->options['table']) : $this->options['table'];
  1081. if (!empty($this->options['alias'][$table])) {
  1082. $alias = $this->options['alias'][$table];
  1083. }
  1084. $key = isset($alias) ? $alias . '.' . $pk : $pk;
  1085. // 根据主键查询
  1086. if (is_array($data)) {
  1087. $this->where($key, 'in', $data);
  1088. } else {
  1089. $this->where($key, '=', $data);
  1090. $this->options['key'] = $data;
  1091. }
  1092. }
  1093. }
  1094. /**
  1095. * 获取模型的更新条件
  1096. * @access protected
  1097. * @param array $options 查询参数
  1098. */
  1099. protected function getModelUpdateCondition(array $options)
  1100. {
  1101. return $options['where']['AND'] ?? null;
  1102. }
  1103. }