赛亿官网

Model.php 65KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203
  1. <?php
  2. // +----------------------------------------------------------------------
  3. // | ThinkPHP [ WE CAN DO IT JUST THINK ]
  4. // +----------------------------------------------------------------------
  5. // | Copyright (c) 2006~2017 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. use think\exception\ValidateException;
  15. use think\model\Collection as ModelCollection;
  16. use think\model\Relation;
  17. use think\model\relation\BelongsTo;
  18. use think\model\relation\BelongsToMany;
  19. use think\model\relation\HasMany;
  20. use think\model\relation\HasManyThrough;
  21. use think\model\relation\HasOne;
  22. use think\model\relation\MorphMany;
  23. use think\model\relation\MorphOne;
  24. use think\model\relation\MorphTo;
  25. /**
  26. * Class Model
  27. * @package think
  28. * @mixin Query
  29. */
  30. abstract class Model implements \JsonSerializable, \ArrayAccess
  31. {
  32. // 数据库查询对象池
  33. protected static $links = [];
  34. // 数据库配置
  35. protected $connection = [];
  36. // 父关联模型对象
  37. protected $parent;
  38. // 数据库查询对象
  39. protected $query;
  40. // 当前模型名称
  41. protected $name;
  42. // 数据表名称
  43. protected $table;
  44. // 当前类名称
  45. protected $class;
  46. // 回调事件
  47. private static $event = [];
  48. // 错误信息
  49. protected $error;
  50. // 字段验证规则
  51. protected $validate;
  52. // 数据表主键 复合主键使用数组定义 不设置则自动获取
  53. protected $pk;
  54. // 数据表字段信息 留空则自动获取
  55. protected $field = [];
  56. // 只读字段
  57. protected $readonly = [];
  58. // 显示属性
  59. protected $visible = [];
  60. // 隐藏属性
  61. protected $hidden = [];
  62. // 追加属性
  63. protected $append = [];
  64. // 数据信息
  65. protected $data = [];
  66. // 原始数据
  67. protected $origin = [];
  68. // 关联模型
  69. protected $relation = [];
  70. // 保存自动完成列表
  71. protected $auto = [];
  72. // 新增自动完成列表
  73. protected $insert = [];
  74. // 更新自动完成列表
  75. protected $update = [];
  76. // 是否需要自动写入时间戳 如果设置为字符串 则表示时间字段的类型
  77. protected $autoWriteTimestamp;
  78. // 创建时间字段
  79. protected $createTime = 'create_time';
  80. // 更新时间字段
  81. protected $updateTime = 'update_time';
  82. // 时间字段取出后的默认时间格式
  83. protected $dateFormat;
  84. // 字段类型或者格式转换
  85. protected $type = [];
  86. // 是否为更新数据
  87. protected $isUpdate = false;
  88. // 更新条件
  89. protected $updateWhere;
  90. // 验证失败是否抛出异常
  91. protected $failException = false;
  92. // 全局查询范围
  93. protected $useGlobalScope = true;
  94. // 是否采用批量验证
  95. protected $batchValidate = false;
  96. // 查询数据集对象
  97. protected $resultSetType;
  98. // 关联自动写入
  99. protected $relationWrite;
  100. /**
  101. * 初始化过的模型.
  102. *
  103. * @var array
  104. */
  105. protected static $initialized = [];
  106. /**
  107. * 构造方法
  108. * @access public
  109. * @param array|object $data 数据
  110. */
  111. public function __construct($data = [])
  112. {
  113. if (is_object($data)) {
  114. $this->data = get_object_vars($data);
  115. } else {
  116. $this->data = $data;
  117. }
  118. // 记录原始数据
  119. $this->origin = $this->data;
  120. // 当前类名
  121. $this->class = get_called_class();
  122. if (empty($this->name)) {
  123. // 当前模型名
  124. $name = str_replace('\\', '/', $this->class);
  125. $this->name = basename($name);
  126. if (Config::get('class_suffix')) {
  127. $suffix = basename(dirname($name));
  128. $this->name = substr($this->name, 0, -strlen($suffix));
  129. }
  130. }
  131. if (is_null($this->autoWriteTimestamp)) {
  132. // 自动写入时间戳
  133. $this->autoWriteTimestamp = $this->getQuery()->getConfig('auto_timestamp');
  134. }
  135. if (is_null($this->dateFormat)) {
  136. // 设置时间戳格式
  137. $this->dateFormat = $this->getQuery()->getConfig('datetime_format');
  138. }
  139. if (is_null($this->resultSetType)) {
  140. $this->resultSetType = $this->getQuery()->getConfig('resultset_type');
  141. }
  142. // 执行初始化操作
  143. $this->initialize();
  144. }
  145. /**
  146. * 创建模型的查询对象
  147. * @access protected
  148. * @return Query
  149. */
  150. protected function buildQuery()
  151. {
  152. // 合并数据库配置
  153. if (!empty($this->connection)) {
  154. if (is_array($this->connection)) {
  155. $connection = array_merge(Config::get('database'), $this->connection);
  156. } else {
  157. $connection = $this->connection;
  158. }
  159. } else {
  160. $connection = [];
  161. }
  162. $con = Db::connect($connection);
  163. // 设置当前模型 确保查询返回模型对象
  164. $queryClass = $this->query ?: $con->getConfig('query');
  165. $query = new $queryClass($con, $this->class);
  166. // 设置当前数据表和模型名
  167. if (!empty($this->table)) {
  168. $query->setTable($this->table);
  169. } else {
  170. $query->name($this->name);
  171. }
  172. if (!empty($this->pk)) {
  173. $query->pk($this->pk);
  174. }
  175. return $query;
  176. }
  177. /**
  178. * 获取当前模型的查询对象
  179. * @access public
  180. * @param bool $buildNewQuery 创建新的查询对象
  181. * @return Query
  182. */
  183. public function getQuery($buildNewQuery = false)
  184. {
  185. if ($buildNewQuery) {
  186. return $this->buildQuery();
  187. } elseif (!isset(self::$links[$this->class])) {
  188. // 创建模型查询对象
  189. self::$links[$this->class] = $this->buildQuery();
  190. }
  191. return self::$links[$this->class];
  192. }
  193. /**
  194. * 获取当前模型的数据库查询对象
  195. * @access public
  196. * @param bool $useBaseQuery 是否调用全局查询范围
  197. * @param bool $buildNewQuery 创建新的查询对象
  198. * @return Query
  199. */
  200. public function db($useBaseQuery = true, $buildNewQuery = true)
  201. {
  202. $query = $this->getQuery($buildNewQuery);
  203. // 全局作用域
  204. if ($useBaseQuery && method_exists($this, 'base')) {
  205. call_user_func_array([$this, 'base'], [ & $query]);
  206. }
  207. // 返回当前模型的数据库查询对象
  208. return $query;
  209. }
  210. /**
  211. * 初始化模型
  212. * @access protected
  213. * @return void
  214. */
  215. protected function initialize()
  216. {
  217. $class = get_class($this);
  218. if (!isset(static::$initialized[$class])) {
  219. static::$initialized[$class] = true;
  220. static::init();
  221. }
  222. }
  223. /**
  224. * 初始化处理
  225. * @access protected
  226. * @return void
  227. */
  228. protected static function init()
  229. {
  230. }
  231. /**
  232. * 设置父关联对象
  233. * @access public
  234. * @param Model $model 模型对象
  235. * @return $this
  236. */
  237. public function setParent($model)
  238. {
  239. $this->parent = $model;
  240. return $this;
  241. }
  242. /**
  243. * 获取父关联对象
  244. * @access public
  245. * @return Model
  246. */
  247. public function getParent()
  248. {
  249. return $this->parent;
  250. }
  251. /**
  252. * 设置数据对象值
  253. * @access public
  254. * @param mixed $data 数据或者属性名
  255. * @param mixed $value 值
  256. * @return $this
  257. */
  258. public function data($data, $value = null)
  259. {
  260. if (is_string($data)) {
  261. $this->data[$data] = $value;
  262. } else {
  263. // 清空数据
  264. $this->data = [];
  265. if (is_object($data)) {
  266. $data = get_object_vars($data);
  267. }
  268. if (true === $value) {
  269. // 数据对象赋值
  270. foreach ($data as $key => $value) {
  271. $this->setAttr($key, $value, $data);
  272. }
  273. } else {
  274. $this->data = $data;
  275. }
  276. }
  277. return $this;
  278. }
  279. /**
  280. * 获取对象原始数据 如果不存在指定字段返回false
  281. * @access public
  282. * @param string $name 字段名 留空获取全部
  283. * @return mixed
  284. * @throws InvalidArgumentException
  285. */
  286. public function getData($name = null)
  287. {
  288. if (is_null($name)) {
  289. return $this->data;
  290. } elseif (array_key_exists($name, $this->data)) {
  291. return $this->data[$name];
  292. } elseif (array_key_exists($name, $this->relation)) {
  293. return $this->relation[$name];
  294. } else {
  295. throw new InvalidArgumentException('property not exists:' . $this->class . '->' . $name);
  296. }
  297. }
  298. /**
  299. * 是否需要自动写入时间字段
  300. * @access public
  301. * @param bool $auto
  302. * @return $this
  303. */
  304. public function isAutoWriteTimestamp($auto)
  305. {
  306. $this->autoWriteTimestamp = $auto;
  307. return $this;
  308. }
  309. /**
  310. * 修改器 设置数据对象值
  311. * @access public
  312. * @param string $name 属性名
  313. * @param mixed $value 属性值
  314. * @param array $data 数据
  315. * @return $this
  316. */
  317. public function setAttr($name, $value, $data = [])
  318. {
  319. if (is_null($value) && $this->autoWriteTimestamp && in_array($name, [$this->createTime, $this->updateTime])) {
  320. // 自动写入的时间戳字段
  321. $value = $this->autoWriteTimestamp($name);
  322. } else {
  323. // 检测修改器
  324. $method = 'set' . Loader::parseName($name, 1) . 'Attr';
  325. if (method_exists($this, $method)) {
  326. $value = $this->$method($value, array_merge($this->data, $data), $this->relation);
  327. } elseif (isset($this->type[$name])) {
  328. // 类型转换
  329. $value = $this->writeTransform($value, $this->type[$name]);
  330. }
  331. }
  332. // 设置数据对象属性
  333. $this->data[$name] = $value;
  334. return $this;
  335. }
  336. /**
  337. * 获取当前模型的关联模型数据
  338. * @access public
  339. * @param string $name 关联方法名
  340. * @return mixed
  341. */
  342. public function getRelation($name = null)
  343. {
  344. if (is_null($name)) {
  345. return $this->relation;
  346. } elseif (array_key_exists($name, $this->relation)) {
  347. return $this->relation[$name];
  348. } else {
  349. return;
  350. }
  351. }
  352. /**
  353. * 设置关联数据对象值
  354. * @access public
  355. * @param string $name 属性名
  356. * @param mixed $value 属性值
  357. * @return $this
  358. */
  359. public function setRelation($name, $value)
  360. {
  361. $this->relation[$name] = $value;
  362. return $this;
  363. }
  364. /**
  365. * 自动写入时间戳
  366. * @access public
  367. * @param string $name 时间戳字段
  368. * @return mixed
  369. */
  370. protected function autoWriteTimestamp($name)
  371. {
  372. if (isset($this->type[$name])) {
  373. $type = $this->type[$name];
  374. if (strpos($type, ':')) {
  375. list($type, $param) = explode(':', $type, 2);
  376. }
  377. switch ($type) {
  378. case 'datetime':
  379. case 'date':
  380. $format = !empty($param) ? $param : $this->dateFormat;
  381. $value = $this->formatDateTime(time(), $format);
  382. break;
  383. case 'timestamp':
  384. case 'integer':
  385. default:
  386. $value = time();
  387. break;
  388. }
  389. } elseif (is_string($this->autoWriteTimestamp) && in_array(strtolower($this->autoWriteTimestamp), [
  390. 'datetime',
  391. 'date',
  392. 'timestamp',
  393. ])
  394. ) {
  395. $value = $this->formatDateTime(time(), $this->dateFormat);
  396. } else {
  397. $value = $this->formatDateTime(time(), $this->dateFormat, true);
  398. }
  399. return $value;
  400. }
  401. /**
  402. * 时间日期字段格式化处理
  403. * @access public
  404. * @param mixed $time 时间日期表达式
  405. * @param mixed $format 日期格式
  406. * @param bool $timestamp 是否进行时间戳转换
  407. * @return mixed
  408. */
  409. protected function formatDateTime($time, $format, $timestamp = false)
  410. {
  411. if (false !== strpos($format, '\\')) {
  412. $time = new $format($time);
  413. } elseif (!$timestamp && false !== $format) {
  414. $time = date($format, $time);
  415. }
  416. return $time;
  417. }
  418. /**
  419. * 数据写入 类型转换
  420. * @access public
  421. * @param mixed $value 值
  422. * @param string|array $type 要转换的类型
  423. * @return mixed
  424. */
  425. protected function writeTransform($value, $type)
  426. {
  427. if (is_null($value)) {
  428. return;
  429. }
  430. if (is_array($type)) {
  431. list($type, $param) = $type;
  432. } elseif (strpos($type, ':')) {
  433. list($type, $param) = explode(':', $type, 2);
  434. }
  435. switch ($type) {
  436. case 'integer':
  437. $value = (int) $value;
  438. break;
  439. case 'float':
  440. if (empty($param)) {
  441. $value = (float) $value;
  442. } else {
  443. $value = (float) number_format($value, $param, '.', '');
  444. }
  445. break;
  446. case 'boolean':
  447. $value = (bool) $value;
  448. break;
  449. case 'timestamp':
  450. if (!is_numeric($value)) {
  451. $value = strtotime($value);
  452. }
  453. break;
  454. case 'datetime':
  455. $format = !empty($param) ? $param : $this->dateFormat;
  456. $value = is_numeric($value) ? $value : strtotime($value);
  457. $value = $this->formatDateTime($value, $format);
  458. break;
  459. case 'object':
  460. if (is_object($value)) {
  461. $value = json_encode($value, JSON_FORCE_OBJECT);
  462. }
  463. break;
  464. case 'array':
  465. $value = (array) $value;
  466. case 'json':
  467. $option = !empty($param) ? (int) $param : JSON_UNESCAPED_UNICODE;
  468. $value = json_encode($value, $option);
  469. break;
  470. case 'serialize':
  471. $value = serialize($value);
  472. break;
  473. }
  474. return $value;
  475. }
  476. /**
  477. * 获取器 获取数据对象的值
  478. * @access public
  479. * @param string $name 名称
  480. * @return mixed
  481. * @throws InvalidArgumentException
  482. */
  483. public function getAttr($name)
  484. {
  485. try {
  486. $notFound = false;
  487. $value = $this->getData($name);
  488. } catch (InvalidArgumentException $e) {
  489. $notFound = true;
  490. $value = null;
  491. }
  492. // 检测属性获取器
  493. $method = 'get' . Loader::parseName($name, 1) . 'Attr';
  494. if (method_exists($this, $method)) {
  495. $value = $this->$method($value, $this->data, $this->relation);
  496. } elseif (isset($this->type[$name])) {
  497. // 类型转换
  498. $value = $this->readTransform($value, $this->type[$name]);
  499. } elseif (in_array($name, [$this->createTime, $this->updateTime])) {
  500. if (is_string($this->autoWriteTimestamp) && in_array(strtolower($this->autoWriteTimestamp), [
  501. 'datetime',
  502. 'date',
  503. 'timestamp',
  504. ])
  505. ) {
  506. $value = $this->formatDateTime(strtotime($value), $this->dateFormat);
  507. } else {
  508. $value = $this->formatDateTime($value, $this->dateFormat);
  509. }
  510. } elseif ($notFound) {
  511. $relation = Loader::parseName($name, 1, false);
  512. if (method_exists($this, $relation)) {
  513. $modelRelation = $this->$relation();
  514. // 不存在该字段 获取关联数据
  515. $value = $this->getRelationData($modelRelation);
  516. // 保存关联对象值
  517. $this->relation[$name] = $value;
  518. } else {
  519. throw new InvalidArgumentException('property not exists:' . $this->class . '->' . $name);
  520. }
  521. }
  522. return $value;
  523. }
  524. /**
  525. * 获取关联模型数据
  526. * @access public
  527. * @param Relation $modelRelation 模型关联对象
  528. * @return mixed
  529. */
  530. protected function getRelationData(Relation $modelRelation)
  531. {
  532. if ($this->parent && get_class($this->parent) == $modelRelation->getModel()) {
  533. $value = $this->parent;
  534. } else {
  535. // 首先获取关联数据
  536. $value = $modelRelation->getRelation();
  537. }
  538. return $value;
  539. }
  540. /**
  541. * 数据读取 类型转换
  542. * @access public
  543. * @param mixed $value 值
  544. * @param string|array $type 要转换的类型
  545. * @return mixed
  546. */
  547. protected function readTransform($value, $type)
  548. {
  549. if (is_null($value)) {
  550. return;
  551. }
  552. if (is_array($type)) {
  553. list($type, $param) = $type;
  554. } elseif (strpos($type, ':')) {
  555. list($type, $param) = explode(':', $type, 2);
  556. }
  557. switch ($type) {
  558. case 'integer':
  559. $value = (int) $value;
  560. break;
  561. case 'float':
  562. if (empty($param)) {
  563. $value = (float) $value;
  564. } else {
  565. $value = (float) number_format($value, $param, '.', '');
  566. }
  567. break;
  568. case 'boolean':
  569. $value = (bool) $value;
  570. break;
  571. case 'timestamp':
  572. if (!is_null($value)) {
  573. $format = !empty($param) ? $param : $this->dateFormat;
  574. $value = $this->formatDateTime($value, $format);
  575. }
  576. break;
  577. case 'datetime':
  578. if (!is_null($value)) {
  579. $format = !empty($param) ? $param : $this->dateFormat;
  580. $value = $this->formatDateTime(strtotime($value), $format);
  581. }
  582. break;
  583. case 'json':
  584. $value = json_decode($value, true);
  585. break;
  586. case 'array':
  587. $value = empty($value) ? [] : json_decode($value, true);
  588. break;
  589. case 'object':
  590. $value = empty($value) ? new \stdClass() : json_decode($value);
  591. break;
  592. case 'serialize':
  593. $value = unserialize($value);
  594. break;
  595. default:
  596. if (false !== strpos($type, '\\')) {
  597. // 对象类型
  598. $value = new $type($value);
  599. }
  600. }
  601. return $value;
  602. }
  603. /**
  604. * 设置需要追加的输出属性
  605. * @access public
  606. * @param array $append 属性列表
  607. * @param bool $override 是否覆盖
  608. * @return $this
  609. */
  610. public function append($append = [], $override = false)
  611. {
  612. $this->append = $override ? $append : array_merge($this->append, $append);
  613. return $this;
  614. }
  615. /**
  616. * 设置附加关联对象的属性
  617. * @access public
  618. * @param string $relation 关联方法
  619. * @param string|array $append 追加属性名
  620. * @return $this
  621. * @throws Exception
  622. */
  623. public function appendRelationAttr($relation, $append)
  624. {
  625. if (is_string($append)) {
  626. $append = explode(',', $append);
  627. }
  628. $relation = Loader::parseName($relation, 1, false);
  629. // 获取关联数据
  630. if (isset($this->relation[$relation])) {
  631. $model = $this->relation[$relation];
  632. } else {
  633. $model = $this->getRelationData($this->$relation());
  634. }
  635. if ($model instanceof Model) {
  636. foreach ($append as $key => $attr) {
  637. $key = is_numeric($key) ? $attr : $key;
  638. if (isset($this->data[$key])) {
  639. throw new Exception('bind attr has exists:' . $key);
  640. } else {
  641. $this->data[$key] = $model->$attr;
  642. }
  643. }
  644. }
  645. return $this;
  646. }
  647. /**
  648. * 设置需要隐藏的输出属性
  649. * @access public
  650. * @param array $hidden 属性列表
  651. * @param bool $override 是否覆盖
  652. * @return $this
  653. */
  654. public function hidden($hidden = [], $override = false)
  655. {
  656. $this->hidden = $override ? $hidden : array_merge($this->hidden, $hidden);
  657. return $this;
  658. }
  659. /**
  660. * 设置需要输出的属性
  661. * @access public
  662. * @param array $visible
  663. * @param bool $override 是否覆盖
  664. * @return $this
  665. */
  666. public function visible($visible = [], $override = false)
  667. {
  668. $this->visible = $override ? $visible : array_merge($this->visible, $visible);
  669. return $this;
  670. }
  671. /**
  672. * 解析隐藏及显示属性
  673. * @access protected
  674. * @param array $attrs 属性
  675. * @param array $result 结果集
  676. * @param bool $visible
  677. * @return array
  678. */
  679. protected function parseAttr($attrs, &$result, $visible = true)
  680. {
  681. $array = [];
  682. foreach ($attrs as $key => $val) {
  683. if (is_array($val)) {
  684. if ($visible) {
  685. $array[] = $key;
  686. }
  687. $result[$key] = $val;
  688. } elseif (strpos($val, '.')) {
  689. list($key, $name) = explode('.', $val);
  690. if ($visible) {
  691. $array[] = $key;
  692. }
  693. $result[$key][] = $name;
  694. } else {
  695. $array[] = $val;
  696. }
  697. }
  698. return $array;
  699. }
  700. /**
  701. * 转换子模型对象
  702. * @access protected
  703. * @param Model|ModelCollection $model
  704. * @param $visible
  705. * @param $hidden
  706. * @param $key
  707. * @return array
  708. */
  709. protected function subToArray($model, $visible, $hidden, $key)
  710. {
  711. // 关联模型对象
  712. if (isset($visible[$key])) {
  713. $model->visible($visible[$key]);
  714. } elseif (isset($hidden[$key])) {
  715. $model->hidden($hidden[$key]);
  716. }
  717. return $model->toArray();
  718. }
  719. /**
  720. * 转换当前模型对象为数组
  721. * @access public
  722. * @return array
  723. */
  724. public function toArray()
  725. {
  726. $item = [];
  727. $visible = [];
  728. $hidden = [];
  729. $data = array_merge($this->data, $this->relation);
  730. // 过滤属性
  731. if (!empty($this->visible)) {
  732. $array = $this->parseAttr($this->visible, $visible);
  733. $data = array_intersect_key($data, array_flip($array));
  734. } elseif (!empty($this->hidden)) {
  735. $array = $this->parseAttr($this->hidden, $hidden, false);
  736. $data = array_diff_key($data, array_flip($array));
  737. }
  738. foreach ($data as $key => $val) {
  739. if ($val instanceof Model || $val instanceof ModelCollection) {
  740. // 关联模型对象
  741. $item[$key] = $this->subToArray($val, $visible, $hidden, $key);
  742. } elseif (is_array($val) && reset($val) instanceof Model) {
  743. // 关联模型数据集
  744. $arr = [];
  745. foreach ($val as $k => $value) {
  746. $arr[$k] = $this->subToArray($value, $visible, $hidden, $key);
  747. }
  748. $item[$key] = $arr;
  749. } else {
  750. // 模型属性
  751. $item[$key] = $this->getAttr($key);
  752. }
  753. }
  754. // 追加属性(必须定义获取器)
  755. if (!empty($this->append)) {
  756. foreach ($this->append as $key => $name) {
  757. if (is_array($name)) {
  758. // 追加关联对象属性
  759. $relation = $this->getAttr($key);
  760. $item[$key] = $relation->append($name)->toArray();
  761. } elseif (strpos($name, '.')) {
  762. list($key, $attr) = explode('.', $name);
  763. // 追加关联对象属性
  764. $relation = $this->getAttr($key);
  765. $item[$key] = $relation->append([$attr])->toArray();
  766. } else {
  767. $item[$name] = $this->getAttr($name);
  768. }
  769. }
  770. }
  771. return !empty($item) ? $item : [];
  772. }
  773. /**
  774. * 转换当前模型对象为JSON字符串
  775. * @access public
  776. * @param integer $options json参数
  777. * @return string
  778. */
  779. public function toJson($options = JSON_UNESCAPED_UNICODE)
  780. {
  781. return json_encode($this->toArray(), $options);
  782. }
  783. /**
  784. * 移除当前模型的关联属性
  785. * @access public
  786. * @return $this
  787. */
  788. public function removeRelation()
  789. {
  790. $this->relation = [];
  791. return $this;
  792. }
  793. /**
  794. * 转换当前模型数据集为数据集对象
  795. * @access public
  796. * @param array|\think\Collection $collection 数据集
  797. * @return \think\Collection
  798. */
  799. public function toCollection($collection)
  800. {
  801. if ($this->resultSetType) {
  802. if ('collection' == $this->resultSetType) {
  803. $collection = new ModelCollection($collection);
  804. } elseif (false !== strpos($this->resultSetType, '\\')) {
  805. $class = $this->resultSetType;
  806. $collection = new $class($collection);
  807. }
  808. }
  809. return $collection;
  810. }
  811. /**
  812. * 关联数据一起更新
  813. * @access public
  814. * @param mixed $relation 关联
  815. * @return $this
  816. */
  817. public function together($relation)
  818. {
  819. if (is_string($relation)) {
  820. $relation = explode(',', $relation);
  821. }
  822. $this->relationWrite = $relation;
  823. return $this;
  824. }
  825. /**
  826. * 获取模型对象的主键
  827. * @access public
  828. * @param string $name 模型名
  829. * @return mixed
  830. */
  831. public function getPk($name = '')
  832. {
  833. if (!empty($name)) {
  834. $table = $this->getQuery()->getTable($name);
  835. return $this->getQuery()->getPk($table);
  836. } elseif (empty($this->pk)) {
  837. $this->pk = $this->getQuery()->getPk();
  838. }
  839. return $this->pk;
  840. }
  841. /**
  842. * 判断一个字段名是否为主键字段
  843. * @access public
  844. * @param string $key 名称
  845. * @return bool
  846. */
  847. protected function isPk($key)
  848. {
  849. $pk = $this->getPk();
  850. if (is_string($pk) && $pk == $key) {
  851. return true;
  852. } elseif (is_array($pk) && in_array($key, $pk)) {
  853. return true;
  854. }
  855. return false;
  856. }
  857. /**
  858. * 保存当前数据对象
  859. * @access public
  860. * @param array $data 数据
  861. * @param array $where 更新条件
  862. * @param string $sequence 自增序列名
  863. * @return integer|false
  864. */
  865. public function save($data = [], $where = [], $sequence = null)
  866. {
  867. if (!empty($data)) {
  868. // 数据自动验证
  869. if (!$this->validateData($data)) {
  870. return false;
  871. }
  872. // 数据对象赋值
  873. foreach ($data as $key => $value) {
  874. $this->setAttr($key, $value, $data);
  875. }
  876. if (!empty($where)) {
  877. $this->isUpdate = true;
  878. }
  879. }
  880. // 自动关联写入
  881. if (!empty($this->relationWrite)) {
  882. $relation = [];
  883. foreach ($this->relationWrite as $key => $name) {
  884. if (is_array($name)) {
  885. if (key($name) === 0) {
  886. $relation[$key] = [];
  887. foreach ($name as $val) {
  888. if (isset($this->data[$val])) {
  889. $relation[$key][$val] = $this->data[$val];
  890. unset($this->data[$val]);
  891. }
  892. }
  893. } else {
  894. $relation[$key] = $name;
  895. }
  896. } elseif (isset($this->relation[$name])) {
  897. $relation[$name] = $this->relation[$name];
  898. } elseif (isset($this->data[$name])) {
  899. $relation[$name] = $this->data[$name];
  900. unset($this->data[$name]);
  901. }
  902. }
  903. }
  904. // 数据自动完成
  905. $this->autoCompleteData($this->auto);
  906. // 事件回调
  907. if (false === $this->trigger('before_write', $this)) {
  908. return false;
  909. }
  910. $pk = $this->getPk();
  911. if ($this->isUpdate) {
  912. // 自动更新
  913. $this->autoCompleteData($this->update);
  914. // 事件回调
  915. if (false === $this->trigger('before_update', $this)) {
  916. return false;
  917. }
  918. // 获取有更新的数据
  919. $data = $this->getChangedData();
  920. if (empty($data) || (count($data) == 1 && is_string($pk) && isset($data[$pk]))) {
  921. // 关联更新
  922. if (isset($relation)) {
  923. $this->autoRelationUpdate($relation);
  924. }
  925. return 0;
  926. } elseif ($this->autoWriteTimestamp && $this->updateTime && !isset($data[$this->updateTime])) {
  927. // 自动写入更新时间
  928. $data[$this->updateTime] = $this->autoWriteTimestamp($this->updateTime);
  929. $this->data[$this->updateTime] = $data[$this->updateTime];
  930. }
  931. if (empty($where) && !empty($this->updateWhere)) {
  932. $where = $this->updateWhere;
  933. }
  934. // 保留主键数据
  935. foreach ($this->data as $key => $val) {
  936. if ($this->isPk($key)) {
  937. $data[$key] = $val;
  938. }
  939. }
  940. if (is_string($pk) && isset($data[$pk])) {
  941. if (!isset($where[$pk])) {
  942. unset($where);
  943. $where[$pk] = $data[$pk];
  944. }
  945. unset($data[$pk]);
  946. }
  947. // 检测字段
  948. $allowFields = $this->checkAllowField(array_merge($this->auto, $this->update));
  949. // 模型更新
  950. if (!empty($allowFields)) {
  951. $result = $this->getQuery()->where($where)->strict(false)->field($allowFields)->update($data);
  952. } else {
  953. $result = $this->getQuery()->where($where)->update($data);
  954. }
  955. // 关联更新
  956. if (isset($relation)) {
  957. $this->autoRelationUpdate($relation);
  958. }
  959. // 更新回调
  960. $this->trigger('after_update', $this);
  961. } else {
  962. // 自动写入
  963. $this->autoCompleteData($this->insert);
  964. // 自动写入创建时间和更新时间
  965. if ($this->autoWriteTimestamp) {
  966. if ($this->createTime && !isset($this->data[$this->createTime])) {
  967. $this->data[$this->createTime] = $this->autoWriteTimestamp($this->createTime);
  968. }
  969. if ($this->updateTime && !isset($this->data[$this->updateTime])) {
  970. $this->data[$this->updateTime] = $this->autoWriteTimestamp($this->updateTime);
  971. }
  972. }
  973. if (false === $this->trigger('before_insert', $this)) {
  974. return false;
  975. }
  976. // 检测字段
  977. $allowFields = $this->checkAllowField(array_merge($this->auto, $this->insert));
  978. if (!empty($allowFields)) {
  979. $result = $this->getQuery()->strict(false)->field($allowFields)->insert($this->data);
  980. } else {
  981. $result = $this->getQuery()->insert($this->data);
  982. }
  983. // 获取自动增长主键
  984. if ($result && is_string($pk) && (!isset($this->data[$pk]) || '' == $this->data[$pk])) {
  985. $insertId = $this->getQuery()->getLastInsID($sequence);
  986. if ($insertId) {
  987. $this->data[$pk] = $insertId;
  988. }
  989. }
  990. // 关联写入
  991. if (isset($relation)) {
  992. foreach ($relation as $name => $val) {
  993. $method = Loader::parseName($name, 1, false);
  994. $this->$method()->save($val);
  995. }
  996. }
  997. // 标记为更新
  998. $this->isUpdate = true;
  999. // 新增回调
  1000. $this->trigger('after_insert', $this);
  1001. }
  1002. // 写入回调
  1003. $this->trigger('after_write', $this);
  1004. // 重新记录原始数据
  1005. $this->origin = $this->data;
  1006. return $result;
  1007. }
  1008. protected function checkAllowField($auto = [])
  1009. {
  1010. if (true === $this->field) {
  1011. $this->field = $this->getQuery()->getTableInfo('', 'fields');
  1012. $field = $this->field;
  1013. } elseif (!empty($this->field)) {
  1014. $field = array_merge($this->field, $auto);
  1015. } else {
  1016. $field = [];
  1017. }
  1018. return $field;
  1019. }
  1020. protected function autoRelationUpdate($relation)
  1021. {
  1022. foreach ($relation as $name => $val) {
  1023. if ($val instanceof Model) {
  1024. $val->save();
  1025. } else {
  1026. unset($this->data[$name]);
  1027. $model = $this->getAttr($name);
  1028. if ($model instanceof Model) {
  1029. $model->save($val);
  1030. }
  1031. }
  1032. }
  1033. }
  1034. /**
  1035. * 获取变化的数据 并排除只读数据
  1036. * @access public
  1037. * @return array
  1038. */
  1039. public function getChangedData()
  1040. {
  1041. $data = array_udiff_assoc($this->data, $this->origin, function ($a, $b) {
  1042. if ((empty($b) || empty($b)) && $a !== $b) {
  1043. return 1;
  1044. }
  1045. return is_object($a) || $a != $b ? 1 : 0;
  1046. });
  1047. if (!empty($this->readonly)) {
  1048. // 只读字段不允许更新
  1049. foreach ($this->readonly as $key => $field) {
  1050. if (isset($data[$field])) {
  1051. unset($data[$field]);
  1052. }
  1053. }
  1054. }
  1055. return $data;
  1056. }
  1057. /**
  1058. * 字段值(延迟)增长
  1059. * @access public
  1060. * @param string $field 字段名
  1061. * @param integer $step 增长值
  1062. * @param integer $lazyTime 延时时间(s)
  1063. * @return integer|true
  1064. * @throws Exception
  1065. */
  1066. public function setInc($field, $step = 1, $lazyTime = 0)
  1067. {
  1068. // 删除条件
  1069. $pk = $this->getPk();
  1070. if (is_string($pk) && isset($this->data[$pk])) {
  1071. $where = [$pk => $this->data[$pk]];
  1072. } elseif (!empty($this->updateWhere)) {
  1073. $where = $this->updateWhere;
  1074. } else {
  1075. $where = null;
  1076. }
  1077. $result = $this->getQuery()->where($where)->setInc($field, $step, $lazyTime);
  1078. if (true !== $result) {
  1079. $this->data[$field] += $step;
  1080. }
  1081. return $result;
  1082. }
  1083. /**
  1084. * 字段值(延迟)增长
  1085. * @access public
  1086. * @param string $field 字段名
  1087. * @param integer $step 增长值
  1088. * @param integer $lazyTime 延时时间(s)
  1089. * @return integer|true
  1090. * @throws Exception
  1091. */
  1092. public function setDec($field, $step = 1, $lazyTime = 0)
  1093. {
  1094. // 删除条件
  1095. $pk = $this->getPk();
  1096. if (is_string($pk) && isset($this->data[$pk])) {
  1097. $where = [$pk => $this->data[$pk]];
  1098. } elseif (!empty($this->updateWhere)) {
  1099. $where = $this->updateWhere;
  1100. } else {
  1101. $where = null;
  1102. }
  1103. $result = $this->getQuery()->where($where)->setDec($field, $step, $lazyTime);
  1104. if (true !== $result) {
  1105. $this->data[$field] -= $step;
  1106. }
  1107. return $result;
  1108. }
  1109. /**
  1110. * 保存多个数据到当前数据对象
  1111. * @access public
  1112. * @param array $dataSet 数据
  1113. * @param boolean $replace 是否自动识别更新和写入
  1114. * @return array|false
  1115. * @throws \Exception
  1116. */
  1117. public function saveAll($dataSet, $replace = true)
  1118. {
  1119. if ($this->validate) {
  1120. // 数据批量验证
  1121. $validate = $this->validate;
  1122. foreach ($dataSet as $data) {
  1123. if (!$this->validateData($data, $validate)) {
  1124. return false;
  1125. }
  1126. }
  1127. }
  1128. $result = [];
  1129. $db = $this->getQuery();
  1130. $db->startTrans();
  1131. try {
  1132. $pk = $this->getPk();
  1133. if (is_string($pk) && $replace) {
  1134. $auto = true;
  1135. }
  1136. foreach ($dataSet as $key => $data) {
  1137. if (!empty($auto) && isset($data[$pk])) {
  1138. $result[$key] = self::update($data, [], $this->field);
  1139. } else {
  1140. $result[$key] = self::create($data, $this->field);
  1141. }
  1142. }
  1143. $db->commit();
  1144. return $result;
  1145. } catch (\Exception $e) {
  1146. $db->rollback();
  1147. throw $e;
  1148. }
  1149. }
  1150. /**
  1151. * 设置允许写入的字段
  1152. * @access public
  1153. * @param mixed $field 允许写入的字段 如果为true只允许写入数据表字段
  1154. * @return $this
  1155. */
  1156. public function allowField($field)
  1157. {
  1158. if (is_string($field)) {
  1159. $field = explode(',', $field);
  1160. }
  1161. $this->field = $field;
  1162. return $this;
  1163. }
  1164. /**
  1165. * 设置只读字段
  1166. * @access public
  1167. * @param mixed $field 只读字段
  1168. * @return $this
  1169. */
  1170. public function readonly($field)
  1171. {
  1172. if (is_string($field)) {
  1173. $field = explode(',', $field);
  1174. }
  1175. $this->readonly = $field;
  1176. return $this;
  1177. }
  1178. /**
  1179. * 是否为更新数据
  1180. * @access public
  1181. * @param bool $update
  1182. * @param mixed $where
  1183. * @return $this
  1184. */
  1185. public function isUpdate($update = true, $where = null)
  1186. {
  1187. $this->isUpdate = $update;
  1188. if (!empty($where)) {
  1189. $this->updateWhere = $where;
  1190. }
  1191. return $this;
  1192. }
  1193. /**
  1194. * 数据自动完成
  1195. * @access public
  1196. * @param array $auto 要自动更新的字段列表
  1197. * @return void
  1198. */
  1199. protected function autoCompleteData($auto = [])
  1200. {
  1201. foreach ($auto as $field => $value) {
  1202. if (is_integer($field)) {
  1203. $field = $value;
  1204. $value = null;
  1205. }
  1206. if (!isset($this->data[$field])) {
  1207. $default = null;
  1208. } else {
  1209. $default = $this->data[$field];
  1210. }
  1211. $this->setAttr($field, !is_null($value) ? $value : $default);
  1212. }
  1213. }
  1214. /**
  1215. * 删除当前的记录
  1216. * @access public
  1217. * @return integer
  1218. */
  1219. public function delete()
  1220. {
  1221. if (false === $this->trigger('before_delete', $this)) {
  1222. return false;
  1223. }
  1224. // 删除条件
  1225. $pk = $this->getPk();
  1226. if (is_string($pk) && isset($this->data[$pk])) {
  1227. $where = [$pk => $this->data[$pk]];
  1228. } elseif (!empty($this->updateWhere)) {
  1229. $where = $this->updateWhere;
  1230. } else {
  1231. $where = null;
  1232. }
  1233. // 删除当前模型数据
  1234. $result = $this->getQuery()->where($where)->delete();
  1235. // 关联删除
  1236. if (!empty($this->relationWrite)) {
  1237. foreach ($this->relationWrite as $key => $name) {
  1238. $name = is_numeric($key) ? $name : $key;
  1239. $model = $this->getAttr($name);
  1240. if ($model instanceof Model) {
  1241. $model->delete();
  1242. }
  1243. }
  1244. }
  1245. $this->trigger('after_delete', $this);
  1246. // 清空原始数据
  1247. $this->origin = [];
  1248. return $result;
  1249. }
  1250. /**
  1251. * 设置自动完成的字段( 规则通过修改器定义)
  1252. * @access public
  1253. * @param array $fields 需要自动完成的字段
  1254. * @return $this
  1255. */
  1256. public function auto($fields)
  1257. {
  1258. $this->auto = $fields;
  1259. return $this;
  1260. }
  1261. /**
  1262. * 设置字段验证
  1263. * @access public
  1264. * @param array|string|bool $rule 验证规则 true表示自动读取验证器类
  1265. * @param array $msg 提示信息
  1266. * @param bool $batch 批量验证
  1267. * @return $this
  1268. */
  1269. public function validate($rule = true, $msg = [], $batch = false)
  1270. {
  1271. if (is_array($rule)) {
  1272. $this->validate = [
  1273. 'rule' => $rule,
  1274. 'msg' => $msg,
  1275. ];
  1276. } else {
  1277. $this->validate = true === $rule ? $this->name : $rule;
  1278. }
  1279. $this->batchValidate = $batch;
  1280. return $this;
  1281. }
  1282. /**
  1283. * 设置验证失败后是否抛出异常
  1284. * @access public
  1285. * @param bool $fail 是否抛出异常
  1286. * @return $this
  1287. */
  1288. public function validateFailException($fail = true)
  1289. {
  1290. $this->failException = $fail;
  1291. return $this;
  1292. }
  1293. /**
  1294. * 自动验证数据
  1295. * @access protected
  1296. * @param array $data 验证数据
  1297. * @param mixed $rule 验证规则
  1298. * @param bool $batch 批量验证
  1299. * @return bool
  1300. */
  1301. protected function validateData($data, $rule = null, $batch = null)
  1302. {
  1303. $info = is_null($rule) ? $this->validate : $rule;
  1304. if (!empty($info)) {
  1305. if (is_array($info)) {
  1306. $validate = Loader::validate();
  1307. $validate->rule($info['rule']);
  1308. $validate->message($info['msg']);
  1309. } else {
  1310. $name = is_string($info) ? $info : $this->name;
  1311. if (strpos($name, '.')) {
  1312. list($name, $scene) = explode('.', $name);
  1313. }
  1314. $validate = Loader::validate($name);
  1315. if (!empty($scene)) {
  1316. $validate->scene($scene);
  1317. }
  1318. }
  1319. $batch = is_null($batch) ? $this->batchValidate : $batch;
  1320. if (!$validate->batch($batch)->check($data)) {
  1321. $this->error = $validate->getError();
  1322. if ($this->failException) {
  1323. throw new ValidateException($this->error);
  1324. } else {
  1325. return false;
  1326. }
  1327. }
  1328. $this->validate = null;
  1329. }
  1330. return true;
  1331. }
  1332. /**
  1333. * 返回模型的错误信息
  1334. * @access public
  1335. * @return string|array
  1336. */
  1337. public function getError()
  1338. {
  1339. return $this->error;
  1340. }
  1341. /**
  1342. * 注册回调方法
  1343. * @access public
  1344. * @param string $event 事件名
  1345. * @param callable $callback 回调方法
  1346. * @param bool $override 是否覆盖
  1347. * @return void
  1348. */
  1349. public static function event($event, $callback, $override = false)
  1350. {
  1351. $class = get_called_class();
  1352. if ($override) {
  1353. self::$event[$class][$event] = [];
  1354. }
  1355. self::$event[$class][$event][] = $callback;
  1356. }
  1357. /**
  1358. * 触发事件
  1359. * @access protected
  1360. * @param string $event 事件名
  1361. * @param mixed $params 传入参数(引用)
  1362. * @return bool
  1363. */
  1364. protected function trigger($event, &$params)
  1365. {
  1366. if (isset(self::$event[$this->class][$event])) {
  1367. foreach (self::$event[$this->class][$event] as $callback) {
  1368. if (is_callable($callback)) {
  1369. $result = call_user_func_array($callback, [ & $params]);
  1370. if (false === $result) {
  1371. return false;
  1372. }
  1373. }
  1374. }
  1375. }
  1376. return true;
  1377. }
  1378. /**
  1379. * 写入数据
  1380. * @access public
  1381. * @param array $data 数据数组
  1382. * @param array|true $field 允许字段
  1383. * @return $this
  1384. */
  1385. public static function create($data = [], $field = null)
  1386. {
  1387. $model = new static();
  1388. if (!empty($field)) {
  1389. $model->allowField($field);
  1390. }
  1391. $model->isUpdate(false)->save($data, []);
  1392. return $model;
  1393. }
  1394. /**
  1395. * 更新数据
  1396. * @access public
  1397. * @param array $data 数据数组
  1398. * @param array $where 更新条件
  1399. * @param array|true $field 允许字段
  1400. * @return $this
  1401. */
  1402. public static function update($data = [], $where = [], $field = null)
  1403. {
  1404. $model = new static();
  1405. if (!empty($field)) {
  1406. $model->allowField($field);
  1407. }
  1408. $result = $model->isUpdate(true)->save($data, $where);
  1409. return $model;
  1410. }
  1411. /**
  1412. * 查找单条记录
  1413. * @access public
  1414. * @param mixed $data 主键值或者查询条件(闭包)
  1415. * @param array|string $with 关联预查询
  1416. * @param bool $cache 是否缓存
  1417. * @return static|null
  1418. * @throws exception\DbException
  1419. */
  1420. public static function get($data, $with = [], $cache = false)
  1421. {
  1422. if (is_null($data)) {
  1423. return;
  1424. }
  1425. if (true === $with || is_int($with)) {
  1426. $cache = $with;
  1427. $with = [];
  1428. }
  1429. $query = static::parseQuery($data, $with, $cache);
  1430. return $query->find($data);
  1431. }
  1432. /**
  1433. * 查找所有记录
  1434. * @access public
  1435. * @param mixed $data 主键列表或者查询条件(闭包)
  1436. * @param array|string $with 关联预查询
  1437. * @param bool $cache 是否缓存
  1438. * @return static[]|false
  1439. * @throws exception\DbException
  1440. */
  1441. public static function all($data = null, $with = [], $cache = false)
  1442. {
  1443. if (true === $with || is_int($with)) {
  1444. $cache = $with;
  1445. $with = [];
  1446. }
  1447. $query = static::parseQuery($data, $with, $cache);
  1448. return $query->select($data);
  1449. }
  1450. /**
  1451. * 分析查询表达式
  1452. * @access public
  1453. * @param mixed $data 主键列表或者查询条件(闭包)
  1454. * @param string $with 关联预查询
  1455. * @param bool $cache 是否缓存
  1456. * @return Query
  1457. */
  1458. protected static function parseQuery(&$data, $with, $cache)
  1459. {
  1460. $result = self::with($with)->cache($cache);
  1461. if (is_array($data) && key($data) !== 0) {
  1462. $result = $result->where($data);
  1463. $data = null;
  1464. } elseif ($data instanceof \Closure) {
  1465. call_user_func_array($data, [ & $result]);
  1466. $data = null;
  1467. } elseif ($data instanceof Query) {
  1468. $result = $data->with($with)->cache($cache);
  1469. $data = null;
  1470. }
  1471. return $result;
  1472. }
  1473. /**
  1474. * 删除记录
  1475. * @access public
  1476. * @param mixed $data 主键列表 支持闭包查询条件
  1477. * @return integer 成功删除的记录数
  1478. */
  1479. public static function destroy($data)
  1480. {
  1481. $model = new static();
  1482. $query = $model->db();
  1483. if (is_array($data) && key($data) !== 0) {
  1484. $query->where($data);
  1485. $data = null;
  1486. } elseif ($data instanceof \Closure) {
  1487. call_user_func_array($data, [ & $query]);
  1488. $data = null;
  1489. } elseif (empty($data) && 0 !== $data) {
  1490. return 0;
  1491. }
  1492. $resultSet = $query->select($data);
  1493. $count = 0;
  1494. if ($resultSet) {
  1495. foreach ($resultSet as $data) {
  1496. $result = $data->delete();
  1497. $count += $result;
  1498. }
  1499. }
  1500. return $count;
  1501. }
  1502. /**
  1503. * 命名范围
  1504. * @access public
  1505. * @param string|array|\Closure $name 命名范围名称 逗号分隔
  1506. * @internal mixed ...$params 参数调用
  1507. * @return Query
  1508. */
  1509. public static function scope($name)
  1510. {
  1511. $model = new static();
  1512. $query = $model->db();
  1513. $params = func_get_args();
  1514. array_shift($params);
  1515. array_unshift($params, $query);
  1516. if ($name instanceof \Closure) {
  1517. call_user_func_array($name, $params);
  1518. } elseif (is_string($name)) {
  1519. $name = explode(',', $name);
  1520. }
  1521. if (is_array($name)) {
  1522. foreach ($name as $scope) {
  1523. $method = 'scope' . trim($scope);
  1524. if (method_exists($model, $method)) {
  1525. call_user_func_array([$model, $method], $params);
  1526. }
  1527. }
  1528. }
  1529. return $query;
  1530. }
  1531. /**
  1532. * 设置是否使用全局查询范围
  1533. * @param bool $use 是否启用全局查询范围
  1534. * @access public
  1535. * @return Model
  1536. */
  1537. public static function useGlobalScope($use)
  1538. {
  1539. $model = new static();
  1540. return $model->db($use);
  1541. }
  1542. /**
  1543. * 根据关联条件查询当前模型
  1544. * @access public
  1545. * @param string $relation 关联方法名
  1546. * @param mixed $operator 比较操作符
  1547. * @param integer $count 个数
  1548. * @param string $id 关联表的统计字段
  1549. * @return Relation|Query
  1550. */
  1551. public static function has($relation, $operator = '>=', $count = 1, $id = '*')
  1552. {
  1553. $relation = (new static())->$relation();
  1554. if (is_array($operator) || $operator instanceof \Closure) {
  1555. return $relation->hasWhere($operator);
  1556. }
  1557. return $relation->has($operator, $count, $id);
  1558. }
  1559. /**
  1560. * 根据关联条件查询当前模型
  1561. * @access public
  1562. * @param string $relation 关联方法名
  1563. * @param mixed $where 查询条件(数组或者闭包)
  1564. * @return Relation|Query
  1565. */
  1566. public static function hasWhere($relation, $where = [])
  1567. {
  1568. return (new static())->$relation()->hasWhere($where);
  1569. }
  1570. /**
  1571. * 解析模型的完整命名空间
  1572. * @access public
  1573. * @param string $model 模型名(或者完整类名)
  1574. * @return string
  1575. */
  1576. protected function parseModel($model)
  1577. {
  1578. if (false === strpos($model, '\\')) {
  1579. $path = explode('\\', get_called_class());
  1580. array_pop($path);
  1581. array_push($path, Loader::parseName($model, 1));
  1582. $model = implode('\\', $path);
  1583. }
  1584. return $model;
  1585. }
  1586. /**
  1587. * 查询当前模型的关联数据
  1588. * @access public
  1589. * @param string|array $relations 关联名
  1590. * @return $this
  1591. */
  1592. public function relationQuery($relations)
  1593. {
  1594. if (is_string($relations)) {
  1595. $relations = explode(',', $relations);
  1596. }
  1597. foreach ($relations as $key => $relation) {
  1598. $subRelation = '';
  1599. $closure = null;
  1600. if ($relation instanceof \Closure) {
  1601. // 支持闭包查询过滤关联条件
  1602. $closure = $relation;
  1603. $relation = $key;
  1604. }
  1605. if (is_array($relation)) {
  1606. $subRelation = $relation;
  1607. $relation = $key;
  1608. } elseif (strpos($relation, '.')) {
  1609. list($relation, $subRelation) = explode('.', $relation, 2);
  1610. }
  1611. $method = Loader::parseName($relation, 1, false);
  1612. $this->data[$relation] = $this->$method()->getRelation($subRelation, $closure);
  1613. }
  1614. return $this;
  1615. }
  1616. /**
  1617. * 预载入关联查询 返回数据集
  1618. * @access public
  1619. * @param array $resultSet 数据集
  1620. * @param string $relation 关联名
  1621. * @return array
  1622. */
  1623. public function eagerlyResultSet(&$resultSet, $relation)
  1624. {
  1625. $relations = is_string($relation) ? explode(',', $relation) : $relation;
  1626. foreach ($relations as $key => $relation) {
  1627. $subRelation = '';
  1628. $closure = false;
  1629. if ($relation instanceof \Closure) {
  1630. $closure = $relation;
  1631. $relation = $key;
  1632. }
  1633. if (is_array($relation)) {
  1634. $subRelation = $relation;
  1635. $relation = $key;
  1636. } elseif (strpos($relation, '.')) {
  1637. list($relation, $subRelation) = explode('.', $relation, 2);
  1638. }
  1639. $relation = Loader::parseName($relation, 1, false);
  1640. $this->$relation()->eagerlyResultSet($resultSet, $relation, $subRelation, $closure);
  1641. }
  1642. }
  1643. /**
  1644. * 预载入关联查询 返回模型对象
  1645. * @access public
  1646. * @param Model $result 数据对象
  1647. * @param string $relation 关联名
  1648. * @return Model
  1649. */
  1650. public function eagerlyResult(&$result, $relation)
  1651. {
  1652. $relations = is_string($relation) ? explode(',', $relation) : $relation;
  1653. foreach ($relations as $key => $relation) {
  1654. $subRelation = '';
  1655. $closure = false;
  1656. if ($relation instanceof \Closure) {
  1657. $closure = $relation;
  1658. $relation = $key;
  1659. }
  1660. if (is_array($relation)) {
  1661. $subRelation = $relation;
  1662. $relation = $key;
  1663. } elseif (strpos($relation, '.')) {
  1664. list($relation, $subRelation) = explode('.', $relation, 2);
  1665. }
  1666. $relation = Loader::parseName($relation, 1, false);
  1667. $this->$relation()->eagerlyResult($result, $relation, $subRelation, $closure);
  1668. }
  1669. }
  1670. /**
  1671. * 关联统计
  1672. * @access public
  1673. * @param Model $result 数据对象
  1674. * @param string|array $relation 关联名
  1675. * @return void
  1676. */
  1677. public function relationCount(&$result, $relation)
  1678. {
  1679. $relations = is_string($relation) ? explode(',', $relation) : $relation;
  1680. foreach ($relations as $key => $relation) {
  1681. $closure = false;
  1682. if ($relation instanceof \Closure) {
  1683. $closure = $relation;
  1684. $relation = $key;
  1685. } elseif (is_string($key)) {
  1686. $name = $relation;
  1687. $relation = $key;
  1688. }
  1689. $relation = Loader::parseName($relation, 1, false);
  1690. $count = $this->$relation()->relationCount($result, $closure);
  1691. if (!isset($name)) {
  1692. $name = Loader::parseName($relation) . '_count';
  1693. }
  1694. $result->setAttr($name, $count);
  1695. }
  1696. }
  1697. /**
  1698. * 获取模型的默认外键名
  1699. * @access public
  1700. * @param string $name 模型名
  1701. * @return string
  1702. */
  1703. protected function getForeignKey($name)
  1704. {
  1705. if (strpos($name, '\\')) {
  1706. $name = basename(str_replace('\\', '/', $name));
  1707. }
  1708. return Loader::parseName($name) . '_id';
  1709. }
  1710. /**
  1711. * HAS ONE 关联定义
  1712. * @access public
  1713. * @param string $model 模型名
  1714. * @param string $foreignKey 关联外键
  1715. * @param string $localKey 关联主键
  1716. * @param array $alias 别名定义(已经废弃)
  1717. * @param string $joinType JOIN类型
  1718. * @return HasOne
  1719. */
  1720. public function hasOne($model, $foreignKey = '', $localKey = '', $alias = [], $joinType = 'INNER')
  1721. {
  1722. // 记录当前关联信息
  1723. $model = $this->parseModel($model);
  1724. $localKey = $localKey ?: $this->getPk();
  1725. $foreignKey = $foreignKey ?: $this->getForeignKey($this->name);
  1726. return new HasOne($this, $model, $foreignKey, $localKey, $joinType);
  1727. }
  1728. /**
  1729. * BELONGS TO 关联定义
  1730. * @access public
  1731. * @param string $model 模型名
  1732. * @param string $foreignKey 关联外键
  1733. * @param string $localKey 关联主键
  1734. * @param array $alias 别名定义(已经废弃)
  1735. * @param string $joinType JOIN类型
  1736. * @return BelongsTo
  1737. */
  1738. public function belongsTo($model, $foreignKey = '', $localKey = '', $alias = [], $joinType = 'INNER')
  1739. {
  1740. // 记录当前关联信息
  1741. $model = $this->parseModel($model);
  1742. $foreignKey = $foreignKey ?: $this->getForeignKey($model);
  1743. $localKey = $localKey ?: (new $model)->getPk();
  1744. $trace = debug_backtrace(false, 2);
  1745. $relation = Loader::parseName($trace[1]['function']);
  1746. return new BelongsTo($this, $model, $foreignKey, $localKey, $joinType, $relation);
  1747. }
  1748. /**
  1749. * HAS MANY 关联定义
  1750. * @access public
  1751. * @param string $model 模型名
  1752. * @param string $foreignKey 关联外键
  1753. * @param string $localKey 关联主键
  1754. * @return HasMany
  1755. */
  1756. public function hasMany($model, $foreignKey = '', $localKey = '')
  1757. {
  1758. // 记录当前关联信息
  1759. $model = $this->parseModel($model);
  1760. $localKey = $localKey ?: $this->getPk();
  1761. $foreignKey = $foreignKey ?: $this->getForeignKey($this->name);
  1762. return new HasMany($this, $model, $foreignKey, $localKey);
  1763. }
  1764. /**
  1765. * HAS MANY 远程关联定义
  1766. * @access public
  1767. * @param string $model 模型名
  1768. * @param string $through 中间模型名
  1769. * @param string $foreignKey 关联外键
  1770. * @param string $throughKey 关联外键
  1771. * @param string $localKey 关联主键
  1772. * @return HasManyThrough
  1773. */
  1774. public function hasManyThrough($model, $through, $foreignKey = '', $throughKey = '', $localKey = '')
  1775. {
  1776. // 记录当前关联信息
  1777. $model = $this->parseModel($model);
  1778. $through = $this->parseModel($through);
  1779. $localKey = $localKey ?: $this->getPk();
  1780. $foreignKey = $foreignKey ?: $this->getForeignKey($this->name);
  1781. $throughKey = $throughKey ?: $this->getForeignKey($through);
  1782. return new HasManyThrough($this, $model, $through, $foreignKey, $throughKey, $localKey);
  1783. }
  1784. /**
  1785. * BELONGS TO MANY 关联定义
  1786. * @access public
  1787. * @param string $model 模型名
  1788. * @param string $table 中间表名
  1789. * @param string $foreignKey 关联外键
  1790. * @param string $localKey 当前模型关联键
  1791. * @return BelongsToMany
  1792. */
  1793. public function belongsToMany($model, $table = '', $foreignKey = '', $localKey = '')
  1794. {
  1795. // 记录当前关联信息
  1796. $model = $this->parseModel($model);
  1797. $name = Loader::parseName(basename(str_replace('\\', '/', $model)));
  1798. $table = $table ?: Loader::parseName($this->name) . '_' . $name;
  1799. $foreignKey = $foreignKey ?: $name . '_id';
  1800. $localKey = $localKey ?: $this->getForeignKey($this->name);
  1801. return new BelongsToMany($this, $model, $table, $foreignKey, $localKey);
  1802. }
  1803. /**
  1804. * MORPH MANY 关联定义
  1805. * @access public
  1806. * @param string $model 模型名
  1807. * @param string|array $morph 多态字段信息
  1808. * @param string $type 多态类型
  1809. * @return MorphMany
  1810. */
  1811. public function morphMany($model, $morph = null, $type = '')
  1812. {
  1813. // 记录当前关联信息
  1814. $model = $this->parseModel($model);
  1815. if (is_null($morph)) {
  1816. $trace = debug_backtrace(false, 2);
  1817. $morph = Loader::parseName($trace[1]['function']);
  1818. }
  1819. $type = $type ?: Loader::parseName($this->name);
  1820. if (is_array($morph)) {
  1821. list($morphType, $foreignKey) = $morph;
  1822. } else {
  1823. $morphType = $morph . '_type';
  1824. $foreignKey = $morph . '_id';
  1825. }
  1826. return new MorphMany($this, $model, $foreignKey, $morphType, $type);
  1827. }
  1828. /**
  1829. * MORPH One 关联定义
  1830. * @access public
  1831. * @param string $model 模型名
  1832. * @param string|array $morph 多态字段信息
  1833. * @param string $type 多态类型
  1834. * @return MorphOne
  1835. */
  1836. public function morphOne($model, $morph = null, $type = '')
  1837. {
  1838. // 记录当前关联信息
  1839. $model = $this->parseModel($model);
  1840. if (is_null($morph)) {
  1841. $trace = debug_backtrace(false, 2);
  1842. $morph = Loader::parseName($trace[1]['function']);
  1843. }
  1844. $type = $type ?: Loader::parseName($this->name);
  1845. if (is_array($morph)) {
  1846. list($morphType, $foreignKey) = $morph;
  1847. } else {
  1848. $morphType = $morph . '_type';
  1849. $foreignKey = $morph . '_id';
  1850. }
  1851. return new MorphOne($this, $model, $foreignKey, $morphType, $type);
  1852. }
  1853. /**
  1854. * MORPH TO 关联定义
  1855. * @access public
  1856. * @param string|array $morph 多态字段信息
  1857. * @param array $alias 多态别名定义
  1858. * @return MorphTo
  1859. */
  1860. public function morphTo($morph = null, $alias = [])
  1861. {
  1862. $trace = debug_backtrace(false, 2);
  1863. $relation = Loader::parseName($trace[1]['function']);
  1864. if (is_null($morph)) {
  1865. $morph = $relation;
  1866. }
  1867. // 记录当前关联信息
  1868. if (is_array($morph)) {
  1869. list($morphType, $foreignKey) = $morph;
  1870. } else {
  1871. $morphType = $morph . '_type';
  1872. $foreignKey = $morph . '_id';
  1873. }
  1874. return new MorphTo($this, $morphType, $foreignKey, $alias, $relation);
  1875. }
  1876. public function __call($method, $args)
  1877. {
  1878. $query = $this->db(true, false);
  1879. if (method_exists($this, 'scope' . $method)) {
  1880. // 动态调用命名范围
  1881. $method = 'scope' . $method;
  1882. array_unshift($args, $query);
  1883. call_user_func_array([$this, $method], $args);
  1884. return $this;
  1885. } else {
  1886. return call_user_func_array([$query, $method], $args);
  1887. }
  1888. }
  1889. public static function __callStatic($method, $args)
  1890. {
  1891. $model = new static();
  1892. $query = $model->db();
  1893. if (method_exists($model, 'scope' . $method)) {
  1894. // 动态调用命名范围
  1895. $method = 'scope' . $method;
  1896. array_unshift($args, $query);
  1897. call_user_func_array([$model, $method], $args);
  1898. return $query;
  1899. } else {
  1900. return call_user_func_array([$query, $method], $args);
  1901. }
  1902. }
  1903. /**
  1904. * 修改器 设置数据对象的值
  1905. * @access public
  1906. * @param string $name 名称
  1907. * @param mixed $value 值
  1908. * @return void
  1909. */
  1910. public function __set($name, $value)
  1911. {
  1912. $this->setAttr($name, $value);
  1913. }
  1914. /**
  1915. * 获取器 获取数据对象的值
  1916. * @access public
  1917. * @param string $name 名称
  1918. * @return mixed
  1919. */
  1920. public function __get($name)
  1921. {
  1922. return $this->getAttr($name);
  1923. }
  1924. /**
  1925. * 检测数据对象的值
  1926. * @access public
  1927. * @param string $name 名称
  1928. * @return boolean
  1929. */
  1930. public function __isset($name)
  1931. {
  1932. try {
  1933. if (array_key_exists($name, $this->data) || array_key_exists($name, $this->relation)) {
  1934. return true;
  1935. } else {
  1936. $this->getAttr($name);
  1937. return true;
  1938. }
  1939. } catch (InvalidArgumentException $e) {
  1940. return false;
  1941. }
  1942. }
  1943. /**
  1944. * 销毁数据对象的值
  1945. * @access public
  1946. * @param string $name 名称
  1947. * @return void
  1948. */
  1949. public function __unset($name)
  1950. {
  1951. unset($this->data[$name], $this->relation[$name]);
  1952. }
  1953. public function __toString()
  1954. {
  1955. return $this->toJson();
  1956. }
  1957. // JsonSerializable
  1958. public function jsonSerialize()
  1959. {
  1960. return $this->toArray();
  1961. }
  1962. // ArrayAccess
  1963. public function offsetSet($name, $value)
  1964. {
  1965. $this->setAttr($name, $value);
  1966. }
  1967. public function offsetExists($name)
  1968. {
  1969. return $this->__isset($name);
  1970. }
  1971. public function offsetUnset($name)
  1972. {
  1973. $this->__unset($name);
  1974. }
  1975. public function offsetGet($name)
  1976. {
  1977. return $this->getAttr($name);
  1978. }
  1979. /**
  1980. * 解序列化后处理
  1981. */
  1982. public function __wakeup()
  1983. {
  1984. $this->initialize();
  1985. }
  1986. /**
  1987. * 模型事件快捷方法
  1988. * @param $callback
  1989. * @param bool $override
  1990. */
  1991. protected static function beforeInsert($callback, $override = false)
  1992. {
  1993. self::event('before_insert', $callback, $override);
  1994. }
  1995. protected static function afterInsert($callback, $override = false)
  1996. {
  1997. self::event('after_insert', $callback, $override);
  1998. }
  1999. protected static function beforeUpdate($callback, $override = false)
  2000. {
  2001. self::event('before_update', $callback, $override);
  2002. }
  2003. protected static function afterUpdate($callback, $override = false)
  2004. {
  2005. self::event('after_update', $callback, $override);
  2006. }
  2007. protected static function beforeWrite($callback, $override = false)
  2008. {
  2009. self::event('before_write', $callback, $override);
  2010. }
  2011. protected static function afterWrite($callback, $override = false)
  2012. {
  2013. self::event('after_write', $callback, $override);
  2014. }
  2015. protected static function beforeDelete($callback, $override = false)
  2016. {
  2017. self::event('before_delete', $callback, $override);
  2018. }
  2019. protected static function afterDelete($callback, $override = false)
  2020. {
  2021. self::event('after_delete', $callback, $override);
  2022. }
  2023. }