BelongsTo.php 11 KB

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