Base.php 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519
  1. <?php
  2. namespace common\base\models;
  3. use common\components\util;
  4. use Yii;
  5. use yii\db\ActiveRecord;
  6. class Base extends ActiveRecord
  7. {
  8. /**
  9. * 获取当前模型实例
  10. * @return static
  11. */
  12. public function getModel()
  13. {
  14. $self = get_called_class();
  15. return new $self;
  16. }
  17. /**
  18. * 添加数据
  19. * @param array $data 数据数组
  20. * @param bool $returnObject 是否返回对象,true返回模型对象,false返回数组
  21. * @return $this|array
  22. * @throws \Exception
  23. */
  24. public function add($data, $returnObject = false)
  25. {
  26. $model = $this->getModel();
  27. $attributes = $model->attributes;
  28. $fields = array_keys($attributes);
  29. foreach ($fields as $key) {
  30. if (isset($data[$key])) {
  31. $model->$key = $data[$key];
  32. }
  33. }
  34. if (!$model->validate()) {
  35. // 验证失败: $errors 是一个包含错误信息的数组
  36. \Yii::error(static::tableName() . ' -- model validate errors: ' . json_encode($model->errors));
  37. util::fail('输入数据验证失败');
  38. }
  39. // 保存数据并检查结果
  40. if (!$model->save()) {
  41. $errors = $model->errors;
  42. \Yii::error(static::tableName() . ' -- save failed: ' . json_encode($errors));
  43. throw new \Exception('数据保存失败: ' . json_encode($errors));
  44. }
  45. return $returnObject ? $model : $model->attributes;
  46. }
  47. /**
  48. * 批量添加
  49. * @param array $data 二维数组 [['name'=>'a'], ['name'=>'b']]
  50. * @return void
  51. */
  52. public function batchAdd($data)
  53. {
  54. $model = $this->getModel();
  55. foreach ($data as $attributes) {
  56. $newModel = clone $model;
  57. // false 不检测字段安全 true 需要在model设置rules
  58. $newModel->setAttributes($attributes, false);
  59. $newModel->save();
  60. }
  61. }
  62. /**
  63. * 获取表的所有字段名
  64. * @return array
  65. */
  66. public function getFields()
  67. {
  68. $model = $this->getModel();
  69. $attributes = $model->attributes;
  70. return array_keys($attributes);
  71. }
  72. /**
  73. * 根据主键ID删除一条记录
  74. * @param int|string $id 主键ID
  75. * @return int|false 删除的行数,失败返回false
  76. * @throws \Throwable
  77. * @throws \yii\db\StaleObjectException
  78. */
  79. public function deleteById($id)
  80. {
  81. $model = $this->getModel();
  82. return $model::findOne($id)->delete();
  83. }
  84. /**
  85. * 根据多个主键ID批量删除
  86. * @param array $ids 主键ID数组
  87. * @return int 删除的行数
  88. */
  89. public function deleteByIds($ids)
  90. {
  91. $model = $this->getModel();
  92. return $model::deleteAll(['id' => $ids]);
  93. }
  94. /**
  95. * 根据条件删除一条或多条记录
  96. * @param array $condition 查询条件
  97. * @return int 删除的行数
  98. */
  99. public function deleteByCondition($condition)
  100. {
  101. if (empty($condition)) {
  102. return 0;
  103. }
  104. $where = array();
  105. $variable = array();
  106. foreach ($condition as $key => $val) {
  107. $where[] = $key . '=:' . $key;
  108. $variable[':' . $key] = $val;
  109. }
  110. $whereString = implode(' and ', $where);
  111. $count = self::deleteAll($whereString, $variable);
  112. return $count;
  113. }
  114. /**
  115. * 根据主键ID更新一条记录
  116. * @param int|string $id 主键ID
  117. * @param array $data 更新的数据
  118. * @return array ['status' => bool, 'data' => array] status=true更新成功,false更新失败
  119. */
  120. public function updateById($id, $data)
  121. {
  122. $model = $this->getModel();
  123. $attributes = $model->attributes;
  124. $fields = array_keys($attributes);
  125. foreach ($data as $key => $val) {
  126. if (in_array($key, $fields) == false) {
  127. //删除表里不存在字段
  128. unset($data[$key]);
  129. }
  130. }
  131. $re = self::updateAll($data, 'id=:id', ['id' => $id]);
  132. if ($re == 1) {
  133. return ['status' => true, 'data' => $data];
  134. }
  135. return ['status' => false, 'data' => $data];
  136. }
  137. /**
  138. * 根据多个主键ID批量更新
  139. * @param array $ids 主键ID数组
  140. * @param array $data 更新的数据
  141. * @return int 更新的行数
  142. */
  143. public function updateByIds($ids, $data)
  144. {
  145. return self::updateAll($data, ['id' => $ids]);
  146. }
  147. /**
  148. * 根据条件更新一条或多条记录
  149. * @param array $condition 查询条件
  150. * @param array $data 更新的数据
  151. * @return int 更新的行数
  152. */
  153. public function updateByCondition($condition, $data)
  154. {
  155. $where = array();
  156. $variable = array();
  157. foreach ($condition as $key => $val) {
  158. $where[] = $key . '=:' . $key;
  159. $variable[':' . $key] = $val;
  160. }
  161. $whereString = implode(' and ', $where);
  162. $count = self::updateAll($data, $whereString, $variable);
  163. return $count;
  164. }
  165. /**
  166. * 根据主键ID查出一条记录
  167. * @param int|string $id 主键ID
  168. * @param bool $returnObject 是否返回对象
  169. * @param string|array $field 查询字段
  170. * @param bool $reverseExclude 是否反向排除字段(传入的字段不查询,其他字段都查询出来),传 true 则反向排除,false 则正向排除,默认 false
  171. * @return array|ActiveRecord|null
  172. * @throws \Exception
  173. */
  174. public function getById($id, $returnObject = false, $field = '*', $reverseExclude = false)
  175. {
  176. $selectField = '*';
  177. if (!empty($field) && $field !== '*' && $reverseExclude == true) {
  178. $excludeFields = is_array($field) ? $field : array_map('trim', explode(',', $field));
  179. $allFields = $this->getFields();
  180. $selectField = array_values(array_diff($allFields, $excludeFields));
  181. if (empty($selectField)) {
  182. Yii::error('getById: selectField is empty');
  183. return null;
  184. }
  185. } else if (!empty($field) && $field !== '*' && $reverseExclude == false) {
  186. $selectField = $field;
  187. }
  188. $query = $this->conditionQuery(['id' => $id])->select($selectField);
  189. if ($returnObject == false) {
  190. return $query->asArray()->one();
  191. } else {
  192. return $query->one();
  193. }
  194. }
  195. /**
  196. * 根据条件查出一条记录
  197. * @param array $condition 查询条件
  198. * @param bool $returnObject 是否返回对象
  199. * @param string|array|bool $order 排序
  200. * @param string|array $field 查询字段
  201. * @return array|ActiveRecord|null
  202. * @throws \Exception
  203. */
  204. public function getByCondition($condition, $returnObject = false, $order = false, $field = '*')
  205. {
  206. $query = $this->conditionQuery($condition)->select($field);
  207. if (isset($order)) {
  208. $query->orderBy($order);
  209. }
  210. return $returnObject == true ? $query->one() : $query->asArray()->one();
  211. }
  212. /**
  213. * 获取第一条记录 (通常用于获取任意一条或结合排序获取特定一条)
  214. * @param bool $returnObject 是否返回对象
  215. * @param string|array|bool $order 排序
  216. * @param string|array $field 查询字段
  217. * @return array|ActiveRecord|null
  218. * @throws \Exception
  219. */
  220. public function getOne($returnObject = false, $order = false, $field = '*')
  221. {
  222. $query = $this->conditionQuery([])->select($field);
  223. if (isset($order)) {
  224. $query->orderBy($order);
  225. }
  226. return $returnObject == true ? $query->one() : $query->asArray()->one();
  227. }
  228. /**
  229. * 构建查询条件对象
  230. * 支持查询条件包括:
  231. * $condition = [
  232. * 'name' => 'john', // 等于
  233. * 'id>' => 3, // 大于
  234. * 'id<' => 10, // 小于
  235. * 'id>=' => 3, // 大于等于
  236. * 'id<=' => 10, // 小于等于
  237. * 'id!=' => -1, // 不等于
  238. * 'id' => ['in', [10,11,12]], // IN
  239. * 'id' => ['not in', [10,11,12]], // NOT IN
  240. * 'age' => ['between', [20,30]], // BETWEEN
  241. * 'name' => ['like', 'Jack'] // LIKE
  242. * ];
  243. * @param array $condition 查询条件数组
  244. * @return \yii\db\ActiveQuery
  245. * @throws \Exception
  246. */
  247. public function conditionQuery($condition = [])
  248. {
  249. $whereNum = 0;
  250. $query = self::find();
  251. if (!empty($condition)) {
  252. foreach ($condition as $key => $val) {
  253. $param = [$key => $val];
  254. if (strstr($key, '!=')) {
  255. //不等于
  256. $pos = strpos($key, '!=');
  257. $currentKey = substr($key, 0, $pos);
  258. $param = ['!=', $currentKey, $val];
  259. } elseif (strstr($key, '>=')) {
  260. //大于等于
  261. $pos = strpos($key, '>=');
  262. $currentKey = substr($key, 0, $pos);
  263. $param = ['>=', $currentKey, $val];
  264. } elseif (strstr($key, '<=')) {
  265. //小于等于
  266. $pos = strpos($key, '<=');
  267. $currentKey = substr($key, 0, $pos);
  268. $param = ['<=', $currentKey, $val];
  269. } elseif (strstr($key, '>')) {
  270. //大于
  271. $pos = strpos($key, '>');
  272. $currentKey = substr($key, 0, $pos);
  273. $param = ['>', $currentKey, $val];
  274. } elseif (strstr($key, '<')) {
  275. //小于
  276. $pos = strpos($key, '<');
  277. $currentKey = substr($key, 0, $pos);
  278. $param = ['<', $currentKey, $val];
  279. } elseif (is_array($val)) {
  280. if (count($val) != 2) {
  281. util::fail('参数不至2个,非法查询方式:' . json_encode($condition));
  282. }
  283. if ($val[0] == 'in') {
  284. $param = ['in', $key, $val[1]];
  285. } elseif ($val[0] == 'not in') {
  286. $param = ['not in', $key, $val[1]];
  287. } elseif ($val[0] == 'between') {
  288. $param = ['between', $key, $val[1][0], $val[1][1]];
  289. } elseif ($val[0] == 'like') {
  290. $param = ['like', $key, $val[1]];
  291. } else {
  292. util::fail('非法查询方式:' . json_encode($condition));
  293. }
  294. } else {
  295. }
  296. if ($whereNum == 0) {
  297. $query->where($param);
  298. } else {
  299. $query->andWhere($param);
  300. }
  301. $whereNum++;
  302. }
  303. }
  304. return $query;
  305. }
  306. /**
  307. * 判断数据是否存在
  308. * @param array $condition 查询条件
  309. * @return bool
  310. * @throws \Exception
  311. */
  312. public function exists($condition)
  313. {
  314. return $this->conditionQuery($condition)->exists();
  315. }
  316. /**
  317. * 根据条件查出多条记录
  318. * @param array $condition 查询条件
  319. * @param string|array|null $order 排序
  320. * @param string|array $field 查询字段
  321. * @param string|callable|null $indexBy 索引字段
  322. * @param bool $returnObject 是否返回对象
  323. * @return array|ActiveRecord[]
  324. * @throws \Exception
  325. */
  326. public function getAllByCondition($condition, $order = null, $field, $indexBy = null, $returnObject = false)
  327. {
  328. $query = $this->conditionQuery($condition)->select($field);
  329. if (isset($indexBy)) {
  330. $query->indexBy($indexBy);
  331. }
  332. if (isset($order)) {
  333. $query->orderBy($order);
  334. }
  335. if ($returnObject == false) {
  336. return $query->asArray()->all();
  337. } else {
  338. return $query->all();
  339. }
  340. }
  341. /**
  342. * 根据多个主键ID查询多条记录
  343. * @param array $ids 主键ID数组
  344. * @param string|array|null $order 排序
  345. * @param string|callable|null $indexBy 索引字段
  346. * @param string|array $field 查询字段
  347. * @return array
  348. * @throws \Exception
  349. */
  350. public function getByIds($ids, $order = null, $indexBy = null, $field = '*')
  351. {
  352. $data = [];
  353. if (empty($ids)) {
  354. return $data;
  355. }
  356. $query = $this->conditionQuery(['id' => ['in', $ids]])->select($field);
  357. if (isset($indexBy)) {
  358. $query->indexBy($indexBy);
  359. }
  360. if (!empty($order)) {
  361. $query->order($order);
  362. }
  363. $data = $query->asArray()->all();
  364. return $data;
  365. }
  366. /**
  367. * 根据条件获取记录数量
  368. * @param array $condition 查询条件
  369. * @return int|string
  370. * @throws \Exception
  371. */
  372. public function getCount($condition)
  373. {
  374. return $this->conditionQuery($condition)->count();
  375. }
  376. /**
  377. * 分页查询列表
  378. * @param string|array $field 查询字段
  379. * @param array $where 查询条件
  380. * @param int $page 当前页码
  381. * @param int $pageSize 每页数量
  382. * @param string|array $order 排序
  383. * @param string|array $with 关联查询
  384. * @return array ['totalNum' => int, 'totalPage' => int, 'moreData' => int, 'list' => array]
  385. * @throws \Exception
  386. */
  387. public function getList($field, $where, $page, $pageSize, $order = '', $with = '')
  388. {
  389. $offset = ($page - 1) * $pageSize;
  390. $query = $this->conditionQuery($where)->select($field);
  391. $clone = clone $query;
  392. $count = $clone->count();
  393. $totalPage = ceil($count / $pageSize);
  394. $data['totalNum'] = $count;
  395. $data['totalPage'] = $totalPage;//总共页数
  396. $data['moreData'] = $totalPage > $page ? 1 : 0;//是否还有更多数据
  397. if (!empty($order)) {
  398. $query->orderBy($order);
  399. }
  400. if (!empty($with)) {
  401. $query->with($with);
  402. }
  403. $list = $query->offset($offset)->limit($pageSize)->asArray()->all();
  404. $data['list'] = $list;//数据
  405. return $data;
  406. }
  407. /**
  408. * 查询全部列表 (不分页)
  409. * @param string|array $field 查询字段
  410. * @param array $where 查询条件
  411. * @param string|array $order 排序
  412. * @param string|array $with 关联查询
  413. * @return array
  414. * @throws \Exception
  415. */
  416. public function getAllList($field, $where, $order = '', $with = '')
  417. {
  418. $query = $this->conditionQuery($where)->select($field);
  419. if (!empty($order)) {
  420. $query->orderBy($order);
  421. }
  422. if (!empty($with)) {
  423. $query->with($with);
  424. }
  425. $list = $query->asArray()->all();
  426. return $list;
  427. }
  428. /**
  429. * 查询指定条数的列表
  430. * @param string|array $field 查询字段
  431. * @param array $where 查询条件
  432. * @param int $limit 限制条数
  433. * @param string|array $order 排序
  434. * @param string|array $with 关联查询
  435. * @return array
  436. * @throws \Exception
  437. */
  438. public function getLimitList($field, $where, $limit, $order = '', $with = '')
  439. {
  440. $offset = 0;
  441. $query = $this->conditionQuery($where)->select($field);
  442. if (!empty($order)) {
  443. $query->orderBy($order);
  444. }
  445. if (!empty($with)) {
  446. $query->with($with);
  447. }
  448. $list = $query->offset($offset)->limit($limit)->asArray()->all();
  449. return $list;
  450. }
  451. /**
  452. * 更新计数器 (原子操作)
  453. * @param array $counters 更新的计数器数组,例如 ['view_count' => 1] 表示加1,['view_count' => -1] 表示减1
  454. * @param array $condition 更新条件
  455. * @param array $params 绑定参数
  456. * @return int 更新行数
  457. */
  458. public function counters($counters, $condition, $params = [])
  459. {
  460. return self::updateAllCounters($counters, $condition, $params);
  461. }
  462. /**
  463. * 根据主键获取记录并锁定 (SELECT FOR UPDATE)
  464. * @param int|string $id 主键ID
  465. * @param string|array $field 查询字段
  466. * @return array|ActiveRecord|null
  467. */
  468. public function getLockById($id, $field = '*')
  469. {
  470. return self::findBySql('select ' . $field . ' from ' . static::tableName() . ' where id = :id for update', ['id' => $id])->one();
  471. }
  472. /**
  473. * 求和
  474. * @param array $condition 查询条件
  475. * @param string $field 求和字段
  476. * @return mixed
  477. * @throws \Exception
  478. */
  479. public function sum($condition, $field)
  480. {
  481. return $this->conditionQuery($condition)->sum($field);
  482. }
  483. }