Fetch.php 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494
  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\db\exception\DbException as Exception;
  14. use think\helper\Str;
  15. /**
  16. * SQL获取类
  17. */
  18. class Fetch
  19. {
  20. /**
  21. * 查询对象
  22. * @var Query
  23. */
  24. protected $query;
  25. /**
  26. * Connection对象
  27. * @var Connection
  28. */
  29. protected $connection;
  30. /**
  31. * Builder对象
  32. * @var Builder
  33. */
  34. protected $builder;
  35. /**
  36. * 创建一个查询SQL获取对象
  37. *
  38. * @param Query $query 查询对象
  39. */
  40. public function __construct(Query $query)
  41. {
  42. $this->query = $query;
  43. $this->connection = $query->getConnection();
  44. $this->builder = $this->connection->getBuilder();
  45. }
  46. /**
  47. * 聚合查询
  48. * @access protected
  49. * @param string $aggregate 聚合方法
  50. * @param string $field 字段名
  51. * @return string
  52. */
  53. protected function aggregate(string $aggregate, string $field): string
  54. {
  55. $this->query->parseOptions();
  56. $field = $aggregate . '(' . $this->builder->parseKey($this->query, $field) . ') AS think_' . strtolower($aggregate);
  57. return $this->value($field, 0, false);
  58. }
  59. /**
  60. * 得到某个字段的值
  61. * @access public
  62. * @param string $field 字段名
  63. * @param mixed $default 默认值
  64. * @param bool $one
  65. * @return string
  66. */
  67. public function value(string $field, $default = null, bool $one = true): string
  68. {
  69. $options = $this->query->parseOptions();
  70. if (isset($options['field'])) {
  71. $this->query->removeOption('field');
  72. }
  73. $this->query->setOption('field', (array) $field);
  74. // 生成查询SQL
  75. $sql = $this->builder->select($this->query, $one);
  76. if (isset($options['field'])) {
  77. $this->query->setOption('field', $options['field']);
  78. } else {
  79. $this->query->removeOption('field');
  80. }
  81. return $this->fetch($sql);
  82. }
  83. /**
  84. * 得到某个列的数组
  85. * @access public
  86. * @param string $field 字段名 多个字段用逗号分隔
  87. * @param string $key 索引
  88. * @return string
  89. */
  90. public function column(string $field, string $key = ''): string
  91. {
  92. $options = $this->query->parseOptions();
  93. if (isset($options['field'])) {
  94. $this->query->removeOption('field');
  95. }
  96. if ($key && '*' != $field) {
  97. $field = $key . ',' . $field;
  98. }
  99. $field = array_map('trim', explode(',', $field));
  100. $this->query->setOption('field', $field);
  101. // 生成查询SQL
  102. $sql = $this->builder->select($this->query);
  103. if (isset($options['field'])) {
  104. $this->query->setOption('field', $options['field']);
  105. } else {
  106. $this->query->removeOption('field');
  107. }
  108. return $this->fetch($sql);
  109. }
  110. /**
  111. * 插入记录
  112. * @access public
  113. * @param array $data 数据
  114. * @return string
  115. */
  116. public function insert(array $data = []): string
  117. {
  118. $options = $this->query->parseOptions();
  119. if (!empty($data)) {
  120. $this->query->setOption('data', $data);
  121. }
  122. $sql = $this->builder->insert($this->query);
  123. return $this->fetch($sql);
  124. }
  125. /**
  126. * 插入记录并获取自增ID
  127. * @access public
  128. * @param array $data 数据
  129. * @return string
  130. */
  131. public function insertGetId(array $data = []): string
  132. {
  133. return $this->insert($data);
  134. }
  135. /**
  136. * 保存数据 自动判断insert或者update
  137. * @access public
  138. * @param array $data 数据
  139. * @param bool $forceInsert 是否强制insert
  140. * @return string
  141. */
  142. public function save(array $data = [], bool $forceInsert = false): string
  143. {
  144. if ($forceInsert) {
  145. return $this->insert($data);
  146. }
  147. $data = array_merge($this->query->getOptions('data') ?: [], $data);
  148. $this->query->setOption('data', $data);
  149. if ($this->query->getOptions('where')) {
  150. $isUpdate = true;
  151. } else {
  152. $isUpdate = $this->query->parseUpdateData($data);
  153. }
  154. return $isUpdate ? $this->update() : $this->insert();
  155. }
  156. /**
  157. * 批量插入记录
  158. * @access public
  159. * @param array $dataSet 数据集
  160. * @param integer $limit 每次写入数据限制
  161. * @return string
  162. */
  163. public function insertAll(array $dataSet = [], int $limit = null): string
  164. {
  165. $options = $this->query->parseOptions();
  166. if (empty($dataSet)) {
  167. $dataSet = $options['data'];
  168. }
  169. if (empty($limit) && !empty($options['limit'])) {
  170. $limit = $options['limit'];
  171. }
  172. if ($limit) {
  173. $array = array_chunk($dataSet, $limit, true);
  174. $fetchSql = [];
  175. foreach ($array as $item) {
  176. $sql = $this->builder->insertAll($this->query, $item);
  177. $bind = $this->query->getBind();
  178. $fetchSql[] = $this->connection->getRealSql($sql, $bind);
  179. }
  180. return implode(';', $fetchSql);
  181. }
  182. $sql = $this->builder->insertAll($this->query, $dataSet);
  183. return $this->fetch($sql);
  184. }
  185. /**
  186. * 通过Select方式插入记录
  187. * @access public
  188. * @param array $fields 要插入的数据表字段名
  189. * @param string $table 要插入的数据表名
  190. * @return string
  191. */
  192. public function selectInsert(array $fields, string $table): string
  193. {
  194. $this->query->parseOptions();
  195. $sql = $this->builder->selectInsert($this->query, $fields, $table);
  196. return $this->fetch($sql);
  197. }
  198. /**
  199. * 更新记录
  200. * @access public
  201. * @param mixed $data 数据
  202. * @return string
  203. */
  204. public function update(array $data = []): string
  205. {
  206. $options = $this->query->parseOptions();
  207. $data = !empty($data) ? $data : $options['data'];
  208. $pk = $this->query->getPk();
  209. if (empty($options['where'])) {
  210. // 如果存在主键数据 则自动作为更新条件
  211. if (is_string($pk) && isset($data[$pk])) {
  212. $this->query->where($pk, '=', $data[$pk]);
  213. unset($data[$pk]);
  214. } elseif (is_array($pk)) {
  215. // 增加复合主键支持
  216. foreach ($pk as $field) {
  217. if (isset($data[$field])) {
  218. $this->query->where($field, '=', $data[$field]);
  219. } else {
  220. // 如果缺少复合主键数据则不执行
  221. throw new Exception('miss complex primary data');
  222. }
  223. unset($data[$field]);
  224. }
  225. }
  226. if (empty($this->query->getOptions('where'))) {
  227. // 如果没有任何更新条件则不执行
  228. throw new Exception('miss update condition');
  229. }
  230. }
  231. // 更新数据
  232. $this->query->setOption('data', $data);
  233. // 生成UPDATE SQL语句
  234. $sql = $this->builder->update($this->query);
  235. return $this->fetch($sql);
  236. }
  237. /**
  238. * 删除记录
  239. * @access public
  240. * @param mixed $data 表达式 true 表示强制删除
  241. * @return string
  242. */
  243. public function delete($data = null): string
  244. {
  245. $options = $this->query->parseOptions();
  246. if (!is_null($data) && true !== $data) {
  247. // AR模式分析主键条件
  248. $this->query->parsePkWhere($data);
  249. }
  250. if (!empty($options['soft_delete'])) {
  251. // 软删除
  252. [$field, $condition] = $options['soft_delete'];
  253. if ($condition) {
  254. $this->query->setOption('soft_delete', null);
  255. $this->query->setOption('data', [$field => $condition]);
  256. // 生成删除SQL语句
  257. $sql = $this->builder->delete($this->query);
  258. return $this->fetch($sql);
  259. }
  260. }
  261. // 生成删除SQL语句
  262. $sql = $this->builder->delete($this->query);
  263. return $this->fetch($sql);
  264. }
  265. /**
  266. * 查找记录 返回SQL
  267. * @access public
  268. * @param mixed $data
  269. * @return string
  270. */
  271. public function select($data = null): string
  272. {
  273. $this->query->parseOptions();
  274. if (!is_null($data)) {
  275. // 主键条件分析
  276. $this->query->parsePkWhere($data);
  277. }
  278. // 生成查询SQL
  279. $sql = $this->builder->select($this->query);
  280. return $this->fetch($sql);
  281. }
  282. /**
  283. * 查找单条记录 返回SQL语句
  284. * @access public
  285. * @param mixed $data
  286. * @return string
  287. */
  288. public function find($data = null): string
  289. {
  290. $this->query->parseOptions();
  291. if (!is_null($data)) {
  292. // AR模式分析主键条件
  293. $this->query->parsePkWhere($data);
  294. }
  295. // 生成查询SQL
  296. $sql = $this->builder->select($this->query, true);
  297. // 获取实际执行的SQL语句
  298. return $this->fetch($sql);
  299. }
  300. /**
  301. * 查找多条记录 如果不存在则抛出异常
  302. * @access public
  303. * @param mixed $data
  304. * @return string
  305. */
  306. public function selectOrFail($data = null): string
  307. {
  308. return $this->select($data);
  309. }
  310. /**
  311. * 查找单条记录 如果不存在则抛出异常
  312. * @access public
  313. * @param mixed $data
  314. * @return string
  315. */
  316. public function findOrFail($data = null): string
  317. {
  318. return $this->find($data);
  319. }
  320. /**
  321. * 查找单条记录 不存在返回空数据(或者空模型)
  322. * @access public
  323. * @param mixed $data 数据
  324. * @return string
  325. */
  326. public function findOrEmpty($data = null)
  327. {
  328. return $this->find($data);
  329. }
  330. /**
  331. * 获取实际的SQL语句
  332. * @access public
  333. * @param string $sql
  334. * @return string
  335. */
  336. public function fetch(string $sql): string
  337. {
  338. $bind = $this->query->getBind();
  339. return $this->connection->getRealSql($sql, $bind);
  340. }
  341. /**
  342. * COUNT查询
  343. * @access public
  344. * @param string $field 字段名
  345. * @return string
  346. */
  347. public function count(string $field = '*'): string
  348. {
  349. $options = $this->query->parseOptions();
  350. if (!empty($options['group'])) {
  351. // 支持GROUP
  352. $bind = $this->query->getBind();
  353. $subSql = $this->query->options($options)->field('count(' . $field . ') AS think_count')->bind($bind)->buildSql();
  354. $query = $this->query->newQuery()->table([$subSql => '_group_count_']);
  355. return $query->fetchsql()->aggregate('COUNT', '*');
  356. } else {
  357. return $this->aggregate('COUNT', $field);
  358. }
  359. }
  360. /**
  361. * SUM查询
  362. * @access public
  363. * @param string $field 字段名
  364. * @return string
  365. */
  366. public function sum(string $field): string
  367. {
  368. return $this->aggregate('SUM', $field);
  369. }
  370. /**
  371. * MIN查询
  372. * @access public
  373. * @param string $field 字段名
  374. * @return string
  375. */
  376. public function min(string $field): string
  377. {
  378. return $this->aggregate('MIN', $field);
  379. }
  380. /**
  381. * MAX查询
  382. * @access public
  383. * @param string $field 字段名
  384. * @return string
  385. */
  386. public function max(string $field): string
  387. {
  388. return $this->aggregate('MAX', $field);
  389. }
  390. /**
  391. * AVG查询
  392. * @access public
  393. * @param string $field 字段名
  394. * @return string
  395. */
  396. public function avg(string $field): string
  397. {
  398. return $this->aggregate('AVG', $field);
  399. }
  400. public function __call($method, $args)
  401. {
  402. if (strtolower(substr($method, 0, 5)) == 'getby') {
  403. // 根据某个字段获取记录
  404. $field = Str::snake(substr($method, 5));
  405. return $this->where($field, '=', $args[0])->find();
  406. } elseif (strtolower(substr($method, 0, 10)) == 'getfieldby') {
  407. // 根据某个字段获取记录的某个值
  408. $name = Str::snake(substr($method, 10));
  409. return $this->where($name, '=', $args[0])->value($args[1]);
  410. }
  411. $result = call_user_func_array([$this->query, $method], $args);
  412. return $result === $this->query ? $this : $result;
  413. }
  414. }