BelongsTo.php 10KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363
  1. <?php
  2. namespace Illuminate\Database\Eloquent\Relations;
  3. use Illuminate\Database\Eloquent\Model;
  4. use Illuminate\Database\Eloquent\Builder;
  5. use Illuminate\Database\Eloquent\Collection;
  6. use Illuminate\Database\Eloquent\Relations\Concerns\SupportsDefaultModels;
  7. /**
  8. * @mixin \Illuminate\Database\Eloquent\Builder
  9. */
  10. class BelongsTo extends Relation
  11. {
  12. use SupportsDefaultModels;
  13. /**
  14. * The child model instance of the relation.
  15. */
  16. protected $child;
  17. /**
  18. * The foreign key of the parent model.
  19. *
  20. * @var string
  21. */
  22. protected $foreignKey;
  23. /**
  24. * The associated key on the parent model.
  25. *
  26. * @var string
  27. */
  28. protected $ownerKey;
  29. /**
  30. * The name of the relationship.
  31. *
  32. * @var string
  33. */
  34. protected $relation;
  35. /**
  36. * The count of self joins.
  37. *
  38. * @var int
  39. */
  40. protected static $selfJoinCount = 0;
  41. /**
  42. * Create a new belongs to relationship instance.
  43. *
  44. * @param \Illuminate\Database\Eloquent\Builder $query
  45. * @param \Illuminate\Database\Eloquent\Model $child
  46. * @param string $foreignKey
  47. * @param string $ownerKey
  48. * @param string $relation
  49. * @return void
  50. */
  51. public function __construct(Builder $query, Model $child, $foreignKey, $ownerKey, $relation)
  52. {
  53. $this->ownerKey = $ownerKey;
  54. $this->relation = $relation;
  55. $this->foreignKey = $foreignKey;
  56. // In the underlying base relationship class, this variable is referred to as
  57. // the "parent" since most relationships are not inversed. But, since this
  58. // one is we will create a "child" variable for much better readability.
  59. $this->child = $child;
  60. parent::__construct($query, $child);
  61. }
  62. /**
  63. * Get the results of the relationship.
  64. *
  65. * @return mixed
  66. */
  67. public function getResults()
  68. {
  69. return $this->query->first() ?: $this->getDefaultFor($this->parent);
  70. }
  71. /**
  72. * Set the base constraints on the relation query.
  73. *
  74. * @return void
  75. */
  76. public function addConstraints()
  77. {
  78. if (static::$constraints) {
  79. // For belongs to relationships, which are essentially the inverse of has one
  80. // or has many relationships, we need to actually query on the primary key
  81. // of the related models matching on the foreign key that's on a parent.
  82. $table = $this->related->getTable();
  83. $this->query->where($table.'.'.$this->ownerKey, '=', $this->child->{$this->foreignKey});
  84. }
  85. }
  86. /**
  87. * Set the constraints for an eager load of the relation.
  88. *
  89. * @param array $models
  90. * @return void
  91. */
  92. public function addEagerConstraints(array $models)
  93. {
  94. // We'll grab the primary key name of the related models since it could be set to
  95. // a non-standard name and not "id". We will then construct the constraint for
  96. // our eagerly loading query so it returns the proper models from execution.
  97. $key = $this->related->getTable().'.'.$this->ownerKey;
  98. $this->query->whereIn($key, $this->getEagerModelKeys($models));
  99. }
  100. /**
  101. * Gather the keys from an array of related models.
  102. *
  103. * @param array $models
  104. * @return array
  105. */
  106. protected function getEagerModelKeys(array $models)
  107. {
  108. $keys = [];
  109. // First we need to gather all of the keys from the parent models so we know what
  110. // to query for via the eager loading query. We will add them to an array then
  111. // execute a "where in" statement to gather up all of those related records.
  112. foreach ($models as $model) {
  113. if (! is_null($value = $model->{$this->foreignKey})) {
  114. $keys[] = $value;
  115. }
  116. }
  117. // If there are no keys that were not null we will just return an array with null
  118. // so this query wont fail plus returns zero results, which should be what the
  119. // developer expects to happen in this situation. Otherwise we'll sort them.
  120. if (count($keys) === 0) {
  121. return [null];
  122. }
  123. sort($keys);
  124. return array_values(array_unique($keys));
  125. }
  126. /**
  127. * Initialize the relation on a set of models.
  128. *
  129. * @param array $models
  130. * @param string $relation
  131. * @return array
  132. */
  133. public function initRelation(array $models, $relation)
  134. {
  135. foreach ($models as $model) {
  136. $model->setRelation($relation, $this->getDefaultFor($model));
  137. }
  138. return $models;
  139. }
  140. /**
  141. * Match the eagerly loaded results to their parents.
  142. *
  143. * @param array $models
  144. * @param \Illuminate\Database\Eloquent\Collection $results
  145. * @param string $relation
  146. * @return array
  147. */
  148. public function match(array $models, Collection $results, $relation)
  149. {
  150. $foreign = $this->foreignKey;
  151. $owner = $this->ownerKey;
  152. // First we will get to build a dictionary of the child models by their primary
  153. // key of the relationship, then we can easily match the children back onto
  154. // the parents using that dictionary and the primary key of the children.
  155. $dictionary = [];
  156. foreach ($results as $result) {
  157. $dictionary[$result->getAttribute($owner)] = $result;
  158. }
  159. // Once we have the dictionary constructed, we can loop through all the parents
  160. // and match back onto their children using these keys of the dictionary and
  161. // the primary key of the children to map them onto the correct instances.
  162. foreach ($models as $model) {
  163. if (isset($dictionary[$model->{$foreign}])) {
  164. $model->setRelation($relation, $dictionary[$model->{$foreign}]);
  165. }
  166. }
  167. return $models;
  168. }
  169. /**
  170. * Update the parent model on the relationship.
  171. *
  172. * @param array $attributes
  173. * @return mixed
  174. */
  175. public function update(array $attributes)
  176. {
  177. return $this->getResults()->fill($attributes)->save();
  178. }
  179. /**
  180. * Associate the model instance to the given parent.
  181. *
  182. * @param \Illuminate\Database\Eloquent\Model|int|string $model
  183. * @return \Illuminate\Database\Eloquent\Model
  184. */
  185. public function associate($model)
  186. {
  187. $ownerKey = $model instanceof Model ? $model->getAttribute($this->ownerKey) : $model;
  188. $this->child->setAttribute($this->foreignKey, $ownerKey);
  189. if ($model instanceof Model) {
  190. $this->child->setRelation($this->relation, $model);
  191. }
  192. return $this->child;
  193. }
  194. /**
  195. * Dissociate previously associated model from the given parent.
  196. *
  197. * @return \Illuminate\Database\Eloquent\Model
  198. */
  199. public function dissociate()
  200. {
  201. $this->child->setAttribute($this->foreignKey, null);
  202. return $this->child->setRelation($this->relation, null);
  203. }
  204. /**
  205. * Add the constraints for a relationship query.
  206. *
  207. * @param \Illuminate\Database\Eloquent\Builder $query
  208. * @param \Illuminate\Database\Eloquent\Builder $parentQuery
  209. * @param array|mixed $columns
  210. * @return \Illuminate\Database\Eloquent\Builder
  211. */
  212. public function getRelationExistenceQuery(Builder $query, Builder $parentQuery, $columns = ['*'])
  213. {
  214. if ($parentQuery->getQuery()->from == $query->getQuery()->from) {
  215. return $this->getRelationExistenceQueryForSelfRelation($query, $parentQuery, $columns);
  216. }
  217. return $query->select($columns)->whereColumn(
  218. $this->getQualifiedForeignKey(), '=', $query->qualifyColumn($this->ownerKey)
  219. );
  220. }
  221. /**
  222. * Add the constraints for a relationship query on the same table.
  223. *
  224. * @param \Illuminate\Database\Eloquent\Builder $query
  225. * @param \Illuminate\Database\Eloquent\Builder $parentQuery
  226. * @param array|mixed $columns
  227. * @return \Illuminate\Database\Eloquent\Builder
  228. */
  229. public function getRelationExistenceQueryForSelfRelation(Builder $query, Builder $parentQuery, $columns = ['*'])
  230. {
  231. $query->select($columns)->from(
  232. $query->getModel()->getTable().' as '.$hash = $this->getRelationCountHash()
  233. );
  234. $query->getModel()->setTable($hash);
  235. return $query->whereColumn(
  236. $hash.'.'.$query->getModel()->getKeyName(), '=', $this->getQualifiedForeignKey()
  237. );
  238. }
  239. /**
  240. * Get a relationship join table hash.
  241. *
  242. * @return string
  243. */
  244. public function getRelationCountHash()
  245. {
  246. return 'laravel_reserved_'.static::$selfJoinCount++;
  247. }
  248. /**
  249. * Determine if the related model has an auto-incrementing ID.
  250. *
  251. * @return bool
  252. */
  253. protected function relationHasIncrementingId()
  254. {
  255. return $this->related->getIncrementing() &&
  256. $this->related->getKeyType() === 'int';
  257. }
  258. /**
  259. * Make a new related instance for the given model.
  260. *
  261. * @param \Illuminate\Database\Eloquent\Model $parent
  262. * @return \Illuminate\Database\Eloquent\Model
  263. */
  264. protected function newRelatedInstanceFor(Model $parent)
  265. {
  266. return $this->related->newInstance();
  267. }
  268. /**
  269. * Get the foreign key of the relationship.
  270. *
  271. * @return string
  272. */
  273. public function getForeignKey()
  274. {
  275. return $this->foreignKey;
  276. }
  277. /**
  278. * Get the fully qualified foreign key of the relationship.
  279. *
  280. * @return string
  281. */
  282. public function getQualifiedForeignKey()
  283. {
  284. return $this->child->qualifyColumn($this->foreignKey);
  285. }
  286. /**
  287. * Get the associated key of the relationship.
  288. *
  289. * @return string
  290. */
  291. public function getOwnerKey()
  292. {
  293. return $this->ownerKey;
  294. }
  295. /**
  296. * Get the fully qualified associated key of the relationship.
  297. *
  298. * @return string
  299. */
  300. public function getQualifiedOwnerKeyName()
  301. {
  302. return $this->related->qualifyColumn($this->ownerKey);
  303. }
  304. /**
  305. * Get the name of the relationship.
  306. *
  307. * @return string
  308. */
  309. public function getRelation()
  310. {
  311. return $this->relation;
  312. }
  313. }