Merge.php 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315
  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\model;
  12. use think\db\Query;
  13. use think\Model;
  14. class Merge extends Model
  15. {
  16. protected $relationModel = []; // HAS ONE 关联的模型列表
  17. protected $fk = ''; // 外键名 默认为主表名_id
  18. protected $mapFields = []; // 需要处理的模型映射字段,避免混淆 array( id => 'user.id' )
  19. /**
  20. * 架构函数
  21. * @access public
  22. * @param array|object $data 数据
  23. */
  24. public function __construct($data = [])
  25. {
  26. parent::__construct($data);
  27. // 设置默认外键名 仅支持单一外键
  28. if (empty($this->fk)) {
  29. $this->fk = strtolower($this->name) . '_id';
  30. }
  31. }
  32. /**
  33. * 查找单条记录
  34. * @access public
  35. * @param mixed $data 主键值或者查询条件(闭包)
  36. * @param string|array $with 关联预查询
  37. * @param bool $cache 是否缓存
  38. * @return \think\Model
  39. */
  40. public static function get($data = null, $with = [], $cache = false)
  41. {
  42. $query = self::parseQuery($data, $with, $cache);
  43. $query = self::attachQuery($query);
  44. return $query->find($data);
  45. }
  46. /**
  47. * 附加查询表达式
  48. * @access protected
  49. * @param \think\db\Query $query 查询对象
  50. * @return \think\db\Query
  51. */
  52. protected static function attachQuery($query)
  53. {
  54. $class = new static();
  55. $master = $class->name;
  56. $fields = self::getModelField($query, $master, '', $class->mapFields, $class->field);
  57. $query->alias($master)->field($fields);
  58. foreach ($class->relationModel as $key => $model) {
  59. $name = is_int($key) ? $model : $key;
  60. $table = is_int($key) ? $query->getTable($name) : $model;
  61. $query->join($table . ' ' . $name, $name . '.' . $class->fk . '=' . $master . '.' . $class->getPk());
  62. $fields = self::getModelField($query, $name, $table, $class->mapFields, $class->field);
  63. $query->field($fields);
  64. }
  65. return $query;
  66. }
  67. /**
  68. * 获取关联模型的字段 并解决混淆
  69. * @access protected
  70. * @param \think\db\Query $query 查询对象
  71. * @param string $name 模型名称
  72. * @param string $table 关联表名称
  73. * @param array $map 字段映射
  74. * @param array $fields 查询字段
  75. * @return array
  76. */
  77. protected static function getModelField($query, $name, $table = '', $map = [], $fields = [])
  78. {
  79. // 获取模型的字段信息
  80. $fields = $fields ?: $query->getTableInfo($table, 'fields');
  81. $array = [];
  82. foreach ($fields as $field) {
  83. if ($key = array_search($name . '.' . $field, $map)) {
  84. // 需要处理映射字段
  85. $array[] = $name . '.' . $field . ' AS ' . $key;
  86. } else {
  87. $array[] = $field;
  88. }
  89. }
  90. return $array;
  91. }
  92. /**
  93. * 查找所有记录
  94. * @access public
  95. * @param mixed $data 主键列表或者查询条件(闭包)
  96. * @param array|string $with 关联预查询
  97. * @param bool $cache
  98. * @return array|false|string
  99. */
  100. public static function all($data = null, $with = [], $cache = false)
  101. {
  102. $query = self::parseQuery($data, $with, $cache);
  103. $query = self::attachQuery($query);
  104. return $query->select($data);
  105. }
  106. /**
  107. * 处理写入的模型数据
  108. * @access public
  109. * @param string $model 模型名称
  110. * @param array $data 数据
  111. * @param bool $insert 是否新增
  112. * @return array
  113. */
  114. protected function parseData($model, $data, $insert = false)
  115. {
  116. $item = [];
  117. foreach ($data as $key => $val) {
  118. if ($insert || in_array($key, $this->change) || $this->isPk($key)) {
  119. if ($this->fk != $key && array_key_exists($key, $this->mapFields)) {
  120. list($name, $key) = explode('.', $this->mapFields[$key]);
  121. if ($model == $name) {
  122. $item[$key] = $val;
  123. }
  124. } else {
  125. $item[$key] = $val;
  126. }
  127. }
  128. }
  129. return $item;
  130. }
  131. /**
  132. * 保存模型数据 以及关联数据
  133. * @access public
  134. * @param mixed $data 数据
  135. * @param array $where 更新条件
  136. * @param string $sequence 自增序列名
  137. * @return false|int
  138. * @throws \Exception
  139. */
  140. public function save($data = [], $where = [], $sequence = null)
  141. {
  142. if (!empty($data)) {
  143. // 数据自动验证
  144. if (!$this->validateData($data)) {
  145. return false;
  146. }
  147. // 数据对象赋值
  148. foreach ($data as $key => $value) {
  149. $this->setAttr($key, $value, $data);
  150. }
  151. if (!empty($where)) {
  152. $this->isUpdate = true;
  153. }
  154. }
  155. // 数据自动完成
  156. $this->autoCompleteData($this->auto);
  157. // 自动写入更新时间
  158. if ($this->autoWriteTimestamp && $this->updateTime && !isset($this->data[$this->updateTime])) {
  159. $this->setAttr($this->updateTime, null);
  160. }
  161. $db = $this->db();
  162. $db->startTrans();
  163. $pk = $this->getPk();
  164. try {
  165. if ($this->isUpdate) {
  166. // 自动写入
  167. $this->autoCompleteData($this->update);
  168. if (false === $this->trigger('before_update', $this)) {
  169. return false;
  170. }
  171. if (empty($where) && !empty($this->updateWhere)) {
  172. $where = $this->updateWhere;
  173. }
  174. // 处理模型数据
  175. $data = $this->parseData($this->name, $this->data);
  176. if (is_string($pk) && isset($data[$pk])) {
  177. if (!isset($where[$pk])) {
  178. unset($where);
  179. $where[$pk] = $data[$pk];
  180. }
  181. unset($data[$pk]);
  182. }
  183. // 写入主表数据
  184. $result = $db->strict(false)->where($where)->update($data);
  185. // 写入附表数据
  186. foreach ($this->relationModel as $key => $model) {
  187. $name = is_int($key) ? $model : $key;
  188. $table = is_int($key) ? $db->getTable($model) : $model;
  189. // 处理关联模型数据
  190. $data = $this->parseData($name, $this->data);
  191. $query = new Query;
  192. if ($query->table($table)->strict(false)->where($this->fk, $this->data[$this->getPk()])->update($data)) {
  193. $result = 1;
  194. }
  195. }
  196. // 清空change
  197. $this->change = [];
  198. // 新增回调
  199. $this->trigger('after_update', $this);
  200. } else {
  201. // 自动写入
  202. $this->autoCompleteData($this->insert);
  203. // 自动写入创建时间
  204. if ($this->autoWriteTimestamp && $this->createTime && !isset($this->data[$this->createTime])) {
  205. $this->setAttr($this->createTime, null);
  206. }
  207. if (false === $this->trigger('before_insert', $this)) {
  208. return false;
  209. }
  210. // 处理模型数据
  211. $data = $this->parseData($this->name, $this->data, true);
  212. // 写入主表数据
  213. $result = $db->name($this->name)->strict(false)->insert($data);
  214. if ($result) {
  215. $insertId = $db->getLastInsID($sequence);
  216. // 写入外键数据
  217. if ($insertId) {
  218. if (is_string($pk)) {
  219. $this->data[$pk] = $insertId;
  220. if ($this->fk == $pk) {
  221. $this->change[] = $pk;
  222. }
  223. }
  224. $this->data[$this->fk] = $insertId;
  225. }
  226. // 写入附表数据
  227. $source = $this->data;
  228. if ($insertId && is_string($pk) && isset($source[$pk]) && $this->fk != $pk) {
  229. unset($source[$pk]);
  230. }
  231. foreach ($this->relationModel as $key => $model) {
  232. $name = is_int($key) ? $model : $key;
  233. $table = is_int($key) ? $db->getTable($model) : $model;
  234. // 处理关联模型数据
  235. $data = $this->parseData($name, $source, true);
  236. $query = new Query;
  237. $query->table($table)->strict(false)->insert($data);
  238. }
  239. }
  240. // 标记为更新
  241. $this->isUpdate = true;
  242. // 清空change
  243. $this->change = [];
  244. // 新增回调
  245. $this->trigger('after_insert', $this);
  246. }
  247. $db->commit();
  248. return $result;
  249. } catch (\Exception $e) {
  250. $db->rollback();
  251. throw $e;
  252. }
  253. }
  254. /**
  255. * 删除当前的记录 并删除关联数据
  256. * @access public
  257. * @return int
  258. * @throws \Exception
  259. */
  260. public function delete()
  261. {
  262. if (false === $this->trigger('before_delete', $this)) {
  263. return false;
  264. }
  265. $db = $this->db();
  266. $db->startTrans();
  267. try {
  268. $result = $db->delete($this->data);
  269. if ($result) {
  270. // 获取主键数据
  271. $pk = $this->data[$this->getPk()];
  272. // 删除关联数据
  273. foreach ($this->relationModel as $key => $model) {
  274. $table = is_int($key) ? $db->getTable($model) : $model;
  275. $query = new Query;
  276. $query->table($table)->where($this->fk, $pk)->delete();
  277. }
  278. }
  279. $this->trigger('after_delete', $this);
  280. $db->commit();
  281. return $result;
  282. } catch (\Exception $e) {
  283. $db->rollback();
  284. throw $e;
  285. }
  286. }
  287. }