MorphTo.php 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275
  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\relation;
  12. use think\Exception;
  13. use think\Loader;
  14. use think\Model;
  15. use think\model\Relation;
  16. class MorphTo extends Relation
  17. {
  18. // 多态字段
  19. protected $morphKey;
  20. protected $morphType;
  21. // 多态别名
  22. protected $alias;
  23. protected $relation;
  24. /**
  25. * 构造函数
  26. * @access public
  27. * @param Model $parent 上级模型对象
  28. * @param string $morphType 多态字段名
  29. * @param string $morphKey 外键名
  30. * @param array $alias 多态别名定义
  31. * @param string $relation 关联名
  32. */
  33. public function __construct(Model $parent, $morphType, $morphKey, $alias = [], $relation = null)
  34. {
  35. $this->parent = $parent;
  36. $this->morphType = $morphType;
  37. $this->morphKey = $morphKey;
  38. $this->alias = $alias;
  39. $this->relation = $relation;
  40. }
  41. /**
  42. * 延迟获取关联数据
  43. * @param string $subRelation 子关联名
  44. * @param \Closure $closure 闭包查询条件
  45. * @return mixed
  46. */
  47. public function getRelation($subRelation = '', $closure = null)
  48. {
  49. $morphKey = $this->morphKey;
  50. $morphType = $this->morphType;
  51. // 多态模型
  52. $model = $this->parseModel($this->parent->$morphType);
  53. // 主键数据
  54. $pk = $this->parent->$morphKey;
  55. $relationModel = (new $model)->relation($subRelation)->find($pk);
  56. if ($relationModel) {
  57. $relationModel->setParent(clone $this->parent);
  58. }
  59. return $relationModel;
  60. }
  61. /**
  62. * 根据关联条件查询当前模型
  63. * @access public
  64. * @param string $operator 比较操作符
  65. * @param integer $count 个数
  66. * @param string $id 关联表的统计字段
  67. * @param string $joinType JOIN类型
  68. * @return Query
  69. */
  70. public function has($operator = '>=', $count = 1, $id = '*', $joinType = 'INNER')
  71. {
  72. return $this->parent;
  73. }
  74. /**
  75. * 根据关联条件查询当前模型
  76. * @access public
  77. * @param mixed $where 查询条件(数组或者闭包)
  78. * @return Query
  79. */
  80. public function hasWhere($where = [])
  81. {
  82. throw new Exception('relation not support: hasWhere');
  83. }
  84. /**
  85. * 解析模型的完整命名空间
  86. * @access public
  87. * @param string $model 模型名(或者完整类名)
  88. * @return string
  89. */
  90. protected function parseModel($model)
  91. {
  92. if (isset($this->alias[$model])) {
  93. $model = $this->alias[$model];
  94. }
  95. if (false === strpos($model, '\\')) {
  96. $path = explode('\\', get_class($this->parent));
  97. array_pop($path);
  98. array_push($path, Loader::parseName($model, 1));
  99. $model = implode('\\', $path);
  100. }
  101. return $model;
  102. }
  103. /**
  104. * 设置多态别名
  105. * @access public
  106. * @param array $alias 别名定义
  107. * @return $this
  108. */
  109. public function setAlias($alias)
  110. {
  111. $this->alias = $alias;
  112. return $this;
  113. }
  114. /**
  115. * 移除关联查询参数
  116. * @access public
  117. * @return $this
  118. */
  119. public function removeOption()
  120. {
  121. return $this;
  122. }
  123. /**
  124. * 预载入关联查询
  125. * @access public
  126. * @param array $resultSet 数据集
  127. * @param string $relation 当前关联名
  128. * @param string $subRelation 子关联名
  129. * @param \Closure $closure 闭包
  130. * @return void
  131. * @throws Exception
  132. */
  133. public function eagerlyResultSet(&$resultSet, $relation, $subRelation, $closure)
  134. {
  135. $morphKey = $this->morphKey;
  136. $morphType = $this->morphType;
  137. $range = [];
  138. foreach ($resultSet as $result) {
  139. // 获取关联外键列表
  140. if (!empty($result->$morphKey)) {
  141. $range[$result->$morphType][] = $result->$morphKey;
  142. }
  143. }
  144. if (!empty($range)) {
  145. // 关联属性名
  146. $attr = Loader::parseName($relation);
  147. foreach ($range as $key => $val) {
  148. // 多态类型映射
  149. $model = $this->parseModel($key);
  150. $obj = new $model;
  151. $pk = $obj->getPk();
  152. $list = $obj->all($val, $subRelation);
  153. $data = [];
  154. foreach ($list as $k => $vo) {
  155. $data[$vo->$pk] = $vo;
  156. }
  157. foreach ($resultSet as $result) {
  158. if ($key == $result->$morphType) {
  159. // 关联模型
  160. if (!isset($data[$result->$morphKey])) {
  161. throw new Exception('relation data not exists :' . $this->model);
  162. } else {
  163. $relationModel = $data[$result->$morphKey];
  164. $relationModel->setParent(clone $result);
  165. $relationModel->isUpdate(true);
  166. $result->setRelation($attr, $relationModel);
  167. }
  168. }
  169. }
  170. }
  171. }
  172. }
  173. /**
  174. * 预载入关联查询
  175. * @access public
  176. * @param Model $result 数据对象
  177. * @param string $relation 当前关联名
  178. * @param string $subRelation 子关联名
  179. * @param \Closure $closure 闭包
  180. * @return void
  181. */
  182. public function eagerlyResult(&$result, $relation, $subRelation, $closure)
  183. {
  184. $morphKey = $this->morphKey;
  185. $morphType = $this->morphType;
  186. // 多态类型映射
  187. $model = $this->parseModel($result->{$this->morphType});
  188. $this->eagerlyMorphToOne($model, $relation, $result, $subRelation);
  189. }
  190. /**
  191. * 关联统计
  192. * @access public
  193. * @param Model $result 数据对象
  194. * @param \Closure $closure 闭包
  195. * @return integer
  196. */
  197. public function relationCount($result, $closure)
  198. {
  199. }
  200. /**
  201. * 多态MorphTo 关联模型预查询
  202. * @access public
  203. * @param object $model 关联模型对象
  204. * @param string $relation 关联名
  205. * @param $result
  206. * @param string $subRelation 子关联
  207. * @return void
  208. */
  209. protected function eagerlyMorphToOne($model, $relation, &$result, $subRelation = '')
  210. {
  211. // 预载入关联查询 支持嵌套预载入
  212. $pk = $this->parent->{$this->morphKey};
  213. $data = (new $model)->with($subRelation)->find($pk);
  214. if ($data) {
  215. $data->setParent(clone $result);
  216. $data->isUpdate(true);
  217. }
  218. $result->setRelation(Loader::parseName($relation), $data ?: null);
  219. }
  220. /**
  221. * 添加关联数据
  222. * @access public
  223. * @param Model $model 关联模型对象
  224. * @param string $type 多态类型
  225. * @return Model
  226. */
  227. public function associate($model, $type = '')
  228. {
  229. $morphKey = $this->morphKey;
  230. $morphType = $this->morphType;
  231. $pk = $model->getPk();
  232. $this->parent->setAttr($morphKey, $model->$pk);
  233. $this->parent->setAttr($morphType, $type ?: get_class($model));
  234. $this->parent->save();
  235. return $this->parent->setRelation($this->relation, $model);
  236. }
  237. /**
  238. * 注销关联数据
  239. * @access public
  240. * @return Model
  241. */
  242. public function dissociate()
  243. {
  244. $morphKey = $this->morphKey;
  245. $morphType = $this->morphType;
  246. $this->parent->setAttr($morphKey, null);
  247. $this->parent->setAttr($morphType, null);
  248. $this->parent->save();
  249. return $this->parent->setRelation($this->relation, null);
  250. }
  251. }