Model.php 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125
  1. <?php
  2. // +----------------------------------------------------------------------
  3. // | ThinkPHP [ WE CAN DO IT JUST THINK ]
  4. // +----------------------------------------------------------------------
  5. // | Copyright (c) 2006~2018 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. namespace think;
  12. use InvalidArgumentException;
  13. use think\db\Query;
  14. /**
  15. * Class Model
  16. * @package think
  17. * @mixin Query
  18. * @method $this scope(string|array $scope) static 查询范围
  19. * @method $this where(mixed $field, string $op = null, mixed $condition = null) static 查询条件
  20. * @method $this whereRaw(string $where, array $bind = [], string $logic = 'AND') static 表达式查询
  21. * @method $this whereExp(string $field, string $condition, array $bind = [], string $logic = 'AND') static 字段表达式查询
  22. * @method $this when(mixed $condition, mixed $query, mixed $otherwise = null) static 条件查询
  23. * @method $this join(mixed $join, mixed $condition = null, string $type = 'INNER', array $bind = []) static JOIN查询
  24. * @method $this view(mixed $join, mixed $field = null, mixed $on = null, string $type = 'INNER') static 视图查询
  25. * @method $this with(mixed $with, callable $callback = null) static 关联预载入
  26. * @method $this count(string $field = '*') static Count统计查询
  27. * @method $this min(string $field, bool $force = true) static Min统计查询
  28. * @method $this max(string $field, bool $force = true) static Max统计查询
  29. * @method $this sum(string $field) static SUM统计查询
  30. * @method $this avg(string $field) static Avg统计查询
  31. * @method $this field(mixed $field, boolean $except = false, string $tableName = '', string $prefix = '', string $alias = '') static 指定查询字段
  32. * @method $this fieldRaw(string $field) static 指定查询字段
  33. * @method $this union(mixed $union, boolean $all = false) static UNION查询
  34. * @method $this limit(mixed $offset, integer $length = null) static 查询LIMIT
  35. * @method $this order(mixed $field, string $order = null) static 查询ORDER
  36. * @method $this orderRaw(string $field, array $bind = []) static 查询ORDER
  37. * @method $this cache(mixed $key = null , integer|\DateTime $expire = null, string $tag = null) static 设置查询缓存
  38. * @method mixed value(string $field, mixed $default = null) static 获取某个字段的值
  39. * @method array column(string $field, string $key = '') static 获取某个列的值
  40. * @method $this find(mixed $data = null) static 查询单个记录
  41. * @method $this findOrFail(mixed $data = null) 查询单个记录
  42. * @method Collection|$this[] select(mixed $data = null) static 查询多个记录
  43. * @method $this get(mixed $data = null,mixed $with = [],bool $cache = false, bool $failException = false) static 查询单个记录 支持关联预载入
  44. * @method $this getOrFail(mixed $data = null,mixed $with = [],bool $cache = false) static 查询单个记录 不存在则抛出异常
  45. * @method $this findOrEmpty(mixed $data = null) static 查询单个记录 不存在则返回空模型
  46. * @method Collection|$this[] all(mixed $data = null,mixed $with = [],bool $cache = false) static 查询多个记录 支持关联预载入
  47. * @method $this withAttr(array $name,\Closure $closure = null) static 动态定义获取器
  48. * @method $this withJoin(string|array $with, string $joinType = '') static
  49. * @method $this withCount(string|array $relation, bool $subQuery = true) static 关联统计
  50. * @method $this withSum(string|array $relation, string $field, bool $subQuery = true) static 关联SUM统计
  51. * @method $this withMax(string|array $relation, string $field, bool $subQuery = true) static 关联MAX统计
  52. * @method $this withMin(string|array $relation, string $field, bool $subQuery = true) static 关联Min统计
  53. * @method $this withAvg(string|array $relation, string $field, bool $subQuery = true) static 关联Avg统计
  54. * @method Paginator|$this paginate() static 分页
  55. */
  56. abstract class Model implements \JsonSerializable, \ArrayAccess
  57. {
  58. use model\concern\Attribute;
  59. use model\concern\RelationShip;
  60. use model\concern\ModelEvent;
  61. use model\concern\TimeStamp;
  62. use model\concern\Conversion;
  63. /**
  64. * 是否存在数据
  65. * @var bool
  66. */
  67. private $exists = false;
  68. /**
  69. * 是否Replace
  70. * @var bool
  71. */
  72. private $replace = false;
  73. /**
  74. * 是否强制更新所有数据
  75. * @var bool
  76. */
  77. private $force = false;
  78. /**
  79. * 更新条件
  80. * @var array
  81. */
  82. private $updateWhere;
  83. /**
  84. * 数据库配置信息
  85. * @var array|string
  86. */
  87. protected $connection = [];
  88. /**
  89. * 数据库查询对象类名
  90. * @var string
  91. */
  92. protected $query;
  93. /**
  94. * 模型名称
  95. * @var string
  96. */
  97. protected $name;
  98. /**
  99. * 数据表名称
  100. * @var string
  101. */
  102. protected $table;
  103. /**
  104. * 写入自动完成定义
  105. * @var array
  106. */
  107. protected $auto = [];
  108. /**
  109. * 新增自动完成定义
  110. * @var array
  111. */
  112. protected $insert = [];
  113. /**
  114. * 更新自动完成定义
  115. * @var array
  116. */
  117. protected $update = [];
  118. /**
  119. * 初始化过的模型.
  120. * @var array
  121. */
  122. protected static $initialized = [];
  123. /**
  124. * 是否从主库读取(主从分布式有效)
  125. * @var array
  126. */
  127. protected static $readMaster;
  128. /**
  129. * 查询对象实例
  130. * @var Query
  131. */
  132. protected $queryInstance;
  133. /**
  134. * 错误信息
  135. * @var mixed
  136. */
  137. protected $error;
  138. /**
  139. * 软删除字段默认值
  140. * @var mixed
  141. */
  142. protected $defaultSoftDelete;
  143. /**
  144. * 全局查询范围
  145. * @var array
  146. */
  147. protected $globalScope = [];
  148. /**
  149. * 架构函数
  150. * @access public
  151. * @param array|object $data 数据
  152. */
  153. public function __construct($data = [])
  154. {
  155. if (is_object($data)) {
  156. $this->data = get_object_vars($data);
  157. } else {
  158. $this->data = $data;
  159. }
  160. if ($this->disuse) {
  161. // 废弃字段
  162. foreach ((array) $this->disuse as $key) {
  163. if (array_key_exists($key, $this->data)) {
  164. unset($this->data[$key]);
  165. }
  166. }
  167. }
  168. // 记录原始数据
  169. $this->origin = $this->data;
  170. $config = Db::getConfig();
  171. if (empty($this->name)) {
  172. // 当前模型名
  173. $name = str_replace('\\', '/', static::class);
  174. $this->name = basename($name);
  175. if (Container::get('config')->get('class_suffix')) {
  176. $suffix = basename(dirname($name));
  177. $this->name = substr($this->name, 0, -strlen($suffix));
  178. }
  179. }
  180. if (is_null($this->autoWriteTimestamp)) {
  181. // 自动写入时间戳
  182. $this->autoWriteTimestamp = $config['auto_timestamp'];
  183. }
  184. if (is_null($this->dateFormat)) {
  185. // 设置时间戳格式
  186. $this->dateFormat = $config['datetime_format'];
  187. }
  188. if (is_null($this->resultSetType)) {
  189. $this->resultSetType = $config['resultset_type'];
  190. }
  191. if (!empty($this->connection) && is_array($this->connection)) {
  192. // 设置模型的数据库连接
  193. $this->connection = array_merge($config, $this->connection);
  194. }
  195. if ($this->observerClass) {
  196. // 注册模型观察者
  197. static::observe($this->observerClass);
  198. }
  199. // 执行初始化操作
  200. $this->initialize();
  201. }
  202. /**
  203. * 获取当前模型名称
  204. * @access public
  205. * @return string
  206. */
  207. public function getName()
  208. {
  209. return $this->name;
  210. }
  211. /**
  212. * 是否从主库读取数据(主从分布有效)
  213. * @access public
  214. * @param bool $all 是否所有模型有效
  215. * @return $this
  216. */
  217. public function readMaster($all = false)
  218. {
  219. $model = $all ? '*' : static::class;
  220. static::$readMaster[$model] = true;
  221. return $this;
  222. }
  223. /**
  224. * 创建新的模型实例
  225. * @access public
  226. * @param array|object $data 数据
  227. * @param bool $isUpdate 是否为更新
  228. * @param mixed $where 更新条件
  229. * @return Model
  230. */
  231. public function newInstance($data = [], $isUpdate = false, $where = null)
  232. {
  233. return (new static($data))->isUpdate($isUpdate, $where);
  234. }
  235. /**
  236. * 创建模型的查询对象
  237. * @access protected
  238. * @return Query
  239. */
  240. protected function buildQuery()
  241. {
  242. // 设置当前模型 确保查询返回模型对象
  243. $query = Db::connect($this->connection, false, $this->query);
  244. $query->model($this)
  245. ->name($this->name)
  246. ->json($this->json, $this->jsonAssoc)
  247. ->setJsonFieldType($this->jsonType);
  248. if (isset(static::$readMaster['*']) || isset(static::$readMaster[static::class])) {
  249. $query->master(true);
  250. }
  251. // 设置当前数据表和模型名
  252. if (!empty($this->table)) {
  253. $query->table($this->table);
  254. }
  255. if (!empty($this->pk)) {
  256. $query->pk($this->pk);
  257. }
  258. return $query;
  259. }
  260. /**
  261. * 获取当前模型的数据库查询对象
  262. * @access public
  263. * @param Query $query 查询对象实例
  264. * @return $this
  265. */
  266. public function setQuery($query)
  267. {
  268. $this->queryInstance = $query;
  269. return $this;
  270. }
  271. /**
  272. * 获取当前模型的数据库查询对象
  273. * @access public
  274. * @param bool|array $useBaseQuery 是否调用全局查询范围(或者指定查询范围名称)
  275. * @return Query
  276. */
  277. public function db($useBaseQuery = true)
  278. {
  279. if ($this->queryInstance) {
  280. return $this->queryInstance;
  281. }
  282. $query = $this->buildQuery();
  283. // 软删除
  284. if (property_exists($this, 'withTrashed') && !$this->withTrashed) {
  285. $this->withNoTrashed($query);
  286. }
  287. // 全局作用域
  288. if (true === $useBaseQuery && method_exists($this, 'base')) {
  289. call_user_func_array([$this, 'base'], [ & $query]);
  290. }
  291. $globalScope = is_array($useBaseQuery) && $useBaseQuery ? $useBaseQuery : $this->globalScope;
  292. if ($globalScope && false !== $useBaseQuery) {
  293. $query->scope($globalScope);
  294. }
  295. // 返回当前模型的数据库查询对象
  296. return $query;
  297. }
  298. /**
  299. * 初始化模型
  300. * @access protected
  301. * @return void
  302. */
  303. protected function initialize()
  304. {
  305. if (!isset(static::$initialized[static::class])) {
  306. static::$initialized[static::class] = true;
  307. static::init();
  308. }
  309. }
  310. /**
  311. * 初始化处理
  312. * @access protected
  313. * @return void
  314. */
  315. protected static function init()
  316. {}
  317. /**
  318. * 数据自动完成
  319. * @access protected
  320. * @param array $auto 要自动更新的字段列表
  321. * @return void
  322. */
  323. protected function autoCompleteData($auto = [])
  324. {
  325. foreach ($auto as $field => $value) {
  326. if (is_integer($field)) {
  327. $field = $value;
  328. $value = null;
  329. }
  330. if (!isset($this->data[$field])) {
  331. $default = null;
  332. } else {
  333. $default = $this->data[$field];
  334. }
  335. $this->setAttr($field, !is_null($value) ? $value : $default);
  336. }
  337. }
  338. /**
  339. * 更新是否强制写入数据 而不做比较
  340. * @access public
  341. * @param bool $force
  342. * @return $this
  343. */
  344. public function force($force = true)
  345. {
  346. $this->force = $force;
  347. return $this;
  348. }
  349. /**
  350. * 判断force
  351. * @access public
  352. * @return bool
  353. */
  354. public function isForce()
  355. {
  356. return $this->force;
  357. }
  358. /**
  359. * 新增数据是否使用Replace
  360. * @access public
  361. * @param bool $replace
  362. * @return $this
  363. */
  364. public function replace($replace = true)
  365. {
  366. $this->replace = $replace;
  367. return $this;
  368. }
  369. /**
  370. * 设置数据是否存在
  371. * @access public
  372. * @param bool $exists
  373. * @return $this
  374. */
  375. public function exists($exists)
  376. {
  377. $this->exists = $exists;
  378. return $this;
  379. }
  380. /**
  381. * 判断数据是否存在数据库
  382. * @access public
  383. * @return bool
  384. */
  385. public function isExists()
  386. {
  387. return $this->exists;
  388. }
  389. /**
  390. * 判断模型是否为空
  391. * @access public
  392. * @return bool
  393. */
  394. public function isEmpty()
  395. {
  396. return empty($this->data);
  397. }
  398. /**
  399. * 保存当前数据对象
  400. * @access public
  401. * @param array $data 数据
  402. * @param array $where 更新条件
  403. * @param string $sequence 自增序列名
  404. * @return bool
  405. */
  406. public function save($data = [], $where = [], $sequence = null)
  407. {
  408. if (is_string($data)) {
  409. $sequence = $data;
  410. $data = [];
  411. }
  412. if (!$this->checkBeforeSave($data, $where)) {
  413. return false;
  414. }
  415. $result = $this->exists ? $this->updateData($where) : $this->insertData($sequence);
  416. if (false === $result) {
  417. return false;
  418. }
  419. // 写入回调
  420. $this->trigger('after_write');
  421. // 重新记录原始数据
  422. $this->origin = $this->data;
  423. $this->set = [];
  424. return true;
  425. }
  426. /**
  427. * 写入之前检查数据
  428. * @access protected
  429. * @param array $data 数据
  430. * @param array $where 保存条件
  431. * @return bool
  432. */
  433. protected function checkBeforeSave($data, $where)
  434. {
  435. if (!empty($data)) {
  436. // 数据对象赋值
  437. foreach ($data as $key => $value) {
  438. $this->setAttr($key, $value, $data);
  439. }
  440. if (!empty($where)) {
  441. $this->exists = true;
  442. $this->updateWhere = $where;
  443. }
  444. }
  445. // 数据自动完成
  446. $this->autoCompleteData($this->auto);
  447. // 事件回调
  448. if (false === $this->trigger('before_write')) {
  449. return false;
  450. }
  451. return true;
  452. }
  453. /**
  454. * 检查数据是否允许写入
  455. * @access protected
  456. * @param array $append 自动完成的字段列表
  457. * @return array
  458. */
  459. protected function checkAllowFields(array $append = [])
  460. {
  461. // 检测字段
  462. if (empty($this->field) || true === $this->field) {
  463. $query = $this->db(false);
  464. $table = $this->table ?: $query->getTable();
  465. $this->field = $query->getConnection()->getTableFields($table);
  466. $field = $this->field;
  467. } else {
  468. $field = array_merge($this->field, $append);
  469. if ($this->autoWriteTimestamp) {
  470. array_push($field, $this->createTime, $this->updateTime);
  471. }
  472. }
  473. if ($this->disuse) {
  474. // 废弃字段
  475. $field = array_diff($field, (array) $this->disuse);
  476. }
  477. return $field;
  478. }
  479. /**
  480. * 更新写入数据
  481. * @access protected
  482. * @param mixed $where 更新条件
  483. * @return bool
  484. */
  485. protected function updateData($where)
  486. {
  487. // 自动更新
  488. $this->autoCompleteData($this->update);
  489. // 事件回调
  490. if (false === $this->trigger('before_update')) {
  491. return false;
  492. }
  493. // 获取有更新的数据
  494. $data = $this->getChangedData();
  495. if (empty($data)) {
  496. // 关联更新
  497. if (!empty($this->relationWrite)) {
  498. $this->autoRelationUpdate();
  499. }
  500. return true;
  501. } elseif ($this->autoWriteTimestamp && $this->updateTime && !isset($data[$this->updateTime])) {
  502. // 自动写入更新时间
  503. $data[$this->updateTime] = $this->autoWriteTimestamp($this->updateTime);
  504. $this->data[$this->updateTime] = $data[$this->updateTime];
  505. }
  506. if (empty($where) && !empty($this->updateWhere)) {
  507. $where = $this->updateWhere;
  508. }
  509. // 检查允许字段
  510. $allowFields = $this->checkAllowFields(array_merge($this->auto, $this->update));
  511. // 保留主键数据
  512. foreach ($this->data as $key => $val) {
  513. if ($this->isPk($key)) {
  514. $data[$key] = $val;
  515. }
  516. }
  517. $pk = $this->getPk();
  518. $array = [];
  519. foreach ((array) $pk as $key) {
  520. if (isset($data[$key])) {
  521. $array[] = [$key, '=', $data[$key]];
  522. unset($data[$key]);
  523. }
  524. }
  525. if (!empty($array)) {
  526. $where = $array;
  527. }
  528. foreach ((array) $this->relationWrite as $name => $val) {
  529. if (is_array($val)) {
  530. foreach ($val as $key) {
  531. if (isset($data[$key])) {
  532. unset($data[$key]);
  533. }
  534. }
  535. }
  536. }
  537. // 模型更新
  538. $db = $this->db(false);
  539. $db->startTrans();
  540. try {
  541. $db->where($where)
  542. ->strict(false)
  543. ->field($allowFields)
  544. ->update($data);
  545. // 关联更新
  546. if (!empty($this->relationWrite)) {
  547. $this->autoRelationUpdate();
  548. }
  549. $db->commit();
  550. // 更新回调
  551. $this->trigger('after_update');
  552. return true;
  553. } catch (\Exception $e) {
  554. $db->rollback();
  555. throw $e;
  556. }
  557. }
  558. /**
  559. * 新增写入数据
  560. * @access protected
  561. * @param string $sequence 自增序列名
  562. * @return bool
  563. */
  564. protected function insertData($sequence)
  565. {
  566. // 自动写入
  567. $this->autoCompleteData($this->insert);
  568. // 时间戳自动写入
  569. $this->checkTimeStampWrite();
  570. if (false === $this->trigger('before_insert')) {
  571. return false;
  572. }
  573. // 检查允许字段
  574. $allowFields = $this->checkAllowFields(array_merge($this->auto, $this->insert));
  575. $db = $this->db(false);
  576. $db->startTrans();
  577. try {
  578. $result = $db->strict(false)
  579. ->field($allowFields)
  580. ->insert($this->data, $this->replace, false, $sequence);
  581. // 获取自动增长主键
  582. if ($result && $insertId = $db->getLastInsID($sequence)) {
  583. $pk = $this->getPk();
  584. foreach ((array) $pk as $key) {
  585. if (!isset($this->data[$key]) || '' == $this->data[$key]) {
  586. $this->data[$key] = $insertId;
  587. }
  588. }
  589. }
  590. // 关联写入
  591. if (!empty($this->relationWrite)) {
  592. $this->autoRelationInsert();
  593. }
  594. $db->commit();
  595. // 标记为更新
  596. $this->exists = true;
  597. // 新增回调
  598. $this->trigger('after_insert');
  599. return true;
  600. } catch (\Exception $e) {
  601. $db->rollback();
  602. throw $e;
  603. }
  604. }
  605. /**
  606. * 字段值(延迟)增长
  607. * @access public
  608. * @param string $field 字段名
  609. * @param integer $step 增长值
  610. * @param integer $lazyTime 延时时间(s)
  611. * @return bool
  612. * @throws Exception
  613. */
  614. public function setInc($field, $step = 1, $lazyTime = 0)
  615. {
  616. // 读取更新条件
  617. $where = $this->getWhere();
  618. // 事件回调
  619. if (false === $this->trigger('before_update')) {
  620. return false;
  621. }
  622. $result = $this->db(false)
  623. ->where($where)
  624. ->setInc($field, $step, $lazyTime);
  625. if (true !== $result) {
  626. $this->data[$field] += $step;
  627. }
  628. // 更新回调
  629. $this->trigger('after_update');
  630. return true;
  631. }
  632. /**
  633. * 字段值(延迟)减少
  634. * @access public
  635. * @param string $field 字段名
  636. * @param integer $step 减少值
  637. * @param integer $lazyTime 延时时间(s)
  638. * @return bool
  639. * @throws Exception
  640. */
  641. public function setDec($field, $step = 1, $lazyTime = 0)
  642. {
  643. // 读取更新条件
  644. $where = $this->getWhere();
  645. // 事件回调
  646. if (false === $this->trigger('before_update')) {
  647. return false;
  648. }
  649. $result = $this->db(false)
  650. ->where($where)
  651. ->setDec($field, $step, $lazyTime);
  652. if (true !== $result) {
  653. $this->data[$field] -= $step;
  654. }
  655. // 更新回调
  656. $this->trigger('after_update');
  657. return true;
  658. }
  659. /**
  660. * 获取当前的更新条件
  661. * @access protected
  662. * @return mixed
  663. */
  664. protected function getWhere()
  665. {
  666. // 删除条件
  667. $pk = $this->getPk();
  668. $where = [];
  669. if (is_string($pk) && isset($this->data[$pk])) {
  670. $where[] = [$pk, '=', $this->data[$pk]];
  671. } elseif (is_array($pk)) {
  672. foreach ($pk as $field) {
  673. if (isset($this->data[$field])) {
  674. $where[] = [$field, '=', $this->data[$field]];
  675. }
  676. }
  677. }
  678. if (empty($where)) {
  679. $where = empty($this->updateWhere) ? null : $this->updateWhere;
  680. }
  681. return $where;
  682. }
  683. /**
  684. * 保存多个数据到当前数据对象
  685. * @access public
  686. * @param array $dataSet 数据
  687. * @param boolean $replace 是否自动识别更新和写入
  688. * @return Collection
  689. * @throws \Exception
  690. */
  691. public function saveAll($dataSet, $replace = true)
  692. {
  693. $db = $this->db(false);
  694. $db->startTrans();
  695. try {
  696. $pk = $this->getPk();
  697. if (is_string($pk) && $replace) {
  698. $auto = true;
  699. }
  700. $result = [];
  701. foreach ($dataSet as $key => $data) {
  702. if ($this->exists || (!empty($auto) && isset($data[$pk]))) {
  703. $result[$key] = self::update($data, [], $this->field);
  704. } else {
  705. $result[$key] = self::create($data, $this->field, $this->replace);
  706. }
  707. }
  708. $db->commit();
  709. return $this->toCollection($result);
  710. } catch (\Exception $e) {
  711. $db->rollback();
  712. throw $e;
  713. }
  714. }
  715. /**
  716. * 是否为更新数据
  717. * @access public
  718. * @param mixed $update
  719. * @param mixed $where
  720. * @return $this
  721. */
  722. public function isUpdate($update = true, $where = null)
  723. {
  724. if (is_bool($update)) {
  725. $this->exists = $update;
  726. if (!empty($where)) {
  727. $this->updateWhere = $where;
  728. }
  729. } else {
  730. $this->exists = true;
  731. $this->updateWhere = $update;
  732. }
  733. return $this;
  734. }
  735. /**
  736. * 删除当前的记录
  737. * @access public
  738. * @return bool
  739. */
  740. public function delete()
  741. {
  742. if (!$this->exists || false === $this->trigger('before_delete')) {
  743. return false;
  744. }
  745. // 读取更新条件
  746. $where = $this->getWhere();
  747. $db = $this->db(false);
  748. $db->startTrans();
  749. try {
  750. // 删除当前模型数据
  751. $db->where($where)->delete();
  752. // 关联删除
  753. if (!empty($this->relationWrite)) {
  754. $this->autoRelationDelete();
  755. }
  756. $db->commit();
  757. $this->trigger('after_delete');
  758. $this->exists = false;
  759. return true;
  760. } catch (\Exception $e) {
  761. $db->rollback();
  762. throw $e;
  763. }
  764. }
  765. /**
  766. * 设置自动完成的字段( 规则通过修改器定义)
  767. * @access public
  768. * @param array $fields 需要自动完成的字段
  769. * @return $this
  770. */
  771. public function auto($fields)
  772. {
  773. $this->auto = $fields;
  774. return $this;
  775. }
  776. /**
  777. * 写入数据
  778. * @access public
  779. * @param array $data 数据数组
  780. * @param array|true $field 允许字段
  781. * @param bool $replace 使用Replace
  782. * @return static
  783. */
  784. public static function create($data = [], $field = null, $replace = false)
  785. {
  786. $model = new static();
  787. if (!empty($field)) {
  788. $model->allowField($field);
  789. }
  790. $model->isUpdate(false)->replace($replace)->save($data, []);
  791. return $model;
  792. }
  793. /**
  794. * 更新数据
  795. * @access public
  796. * @param array $data 数据数组
  797. * @param array $where 更新条件
  798. * @param array|true $field 允许字段
  799. * @return static
  800. */
  801. public static function update($data = [], $where = [], $field = null)
  802. {
  803. $model = new static();
  804. if (!empty($field)) {
  805. $model->allowField($field);
  806. }
  807. $model->isUpdate(true)->save($data, $where);
  808. return $model;
  809. }
  810. /**
  811. * 删除记录
  812. * @access public
  813. * @param mixed $data 主键列表 支持闭包查询条件
  814. * @return bool
  815. */
  816. public static function destroy($data)
  817. {
  818. if (empty($data) && 0 !== $data) {
  819. return false;
  820. }
  821. $model = new static();
  822. $query = $model->db();
  823. if (is_array($data) && key($data) !== 0) {
  824. $query->where($data);
  825. $data = null;
  826. } elseif ($data instanceof \Closure) {
  827. $data($query);
  828. $data = null;
  829. }
  830. $resultSet = $query->select($data);
  831. if ($resultSet) {
  832. foreach ($resultSet as $data) {
  833. $data->delete();
  834. }
  835. }
  836. return true;
  837. }
  838. /**
  839. * 获取错误信息
  840. * @access public
  841. * @return mixed
  842. */
  843. public function getError()
  844. {
  845. return $this->error;
  846. }
  847. /**
  848. * 解序列化后处理
  849. */
  850. public function __wakeup()
  851. {
  852. $this->initialize();
  853. }
  854. public function __debugInfo()
  855. {
  856. return [
  857. 'data' => $this->data,
  858. 'relation' => $this->relation,
  859. ];
  860. }
  861. /**
  862. * 修改器 设置数据对象的值
  863. * @access public
  864. * @param string $name 名称
  865. * @param mixed $value 值
  866. * @return void
  867. */
  868. public function __set($name, $value)
  869. {
  870. $this->setAttr($name, $value);
  871. }
  872. /**
  873. * 获取器 获取数据对象的值
  874. * @access public
  875. * @param string $name 名称
  876. * @return mixed
  877. */
  878. public function __get($name)
  879. {
  880. return $this->getAttr($name);
  881. }
  882. /**
  883. * 检测数据对象的值
  884. * @access public
  885. * @param string $name 名称
  886. * @return boolean
  887. */
  888. public function __isset($name)
  889. {
  890. try {
  891. return !is_null($this->getAttr($name));
  892. } catch (InvalidArgumentException $e) {
  893. return false;
  894. }
  895. }
  896. /**
  897. * 销毁数据对象的值
  898. * @access public
  899. * @param string $name 名称
  900. * @return void
  901. */
  902. public function __unset($name)
  903. {
  904. unset($this->data[$name], $this->relation[$name]);
  905. }
  906. // ArrayAccess
  907. public function offsetSet($name, $value)
  908. {
  909. $this->setAttr($name, $value);
  910. }
  911. public function offsetExists($name)
  912. {
  913. return $this->__isset($name);
  914. }
  915. public function offsetUnset($name)
  916. {
  917. $this->__unset($name);
  918. }
  919. public function offsetGet($name)
  920. {
  921. return $this->getAttr($name);
  922. }
  923. /**
  924. * 设置是否使用全局查询范围
  925. * @access public
  926. * @param bool|array $use 是否启用全局查询范围(或者用数组指定查询范围名称)
  927. * @return Query
  928. */
  929. public static function useGlobalScope($use)
  930. {
  931. $model = new static();
  932. return $model->db($use);
  933. }
  934. public function __call($method, $args)
  935. {
  936. if ('withattr' == strtolower($method)) {
  937. return call_user_func_array([$this, 'withAttribute'], $args);
  938. }
  939. return call_user_func_array([$this->db(), $method], $args);
  940. }
  941. public static function __callStatic($method, $args)
  942. {
  943. $model = new static();
  944. return call_user_func_array([$model->db(), $method], $args);
  945. }
  946. }