Fetch.php 13 KB

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