Relation.php 9.2KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384
  1. <?php
  2. namespace Illuminate\Database\Eloquent\Relations;
  3. use Closure;
  4. use Illuminate\Support\Arr;
  5. use Illuminate\Database\Eloquent\Model;
  6. use Illuminate\Support\Traits\Macroable;
  7. use Illuminate\Database\Eloquent\Builder;
  8. use Illuminate\Database\Query\Expression;
  9. use Illuminate\Database\Eloquent\Collection;
  10. /**
  11. * @mixin \Illuminate\Database\Eloquent\Builder
  12. */
  13. abstract class Relation
  14. {
  15. use Macroable {
  16. __call as macroCall;
  17. }
  18. /**
  19. * The Eloquent query builder instance.
  20. *
  21. * @var \Illuminate\Database\Eloquent\Builder
  22. */
  23. protected $query;
  24. /**
  25. * The parent model instance.
  26. *
  27. * @var \Illuminate\Database\Eloquent\Model
  28. */
  29. protected $parent;
  30. /**
  31. * The related model instance.
  32. *
  33. * @var \Illuminate\Database\Eloquent\Model
  34. */
  35. protected $related;
  36. /**
  37. * Indicates if the relation is adding constraints.
  38. *
  39. * @var bool
  40. */
  41. protected static $constraints = true;
  42. /**
  43. * An array to map class names to their morph names in database.
  44. *
  45. * @var array
  46. */
  47. public static $morphMap = [];
  48. /**
  49. * Create a new relation instance.
  50. *
  51. * @param \Illuminate\Database\Eloquent\Builder $query
  52. * @param \Illuminate\Database\Eloquent\Model $parent
  53. * @return void
  54. */
  55. public function __construct(Builder $query, Model $parent)
  56. {
  57. $this->query = $query;
  58. $this->parent = $parent;
  59. $this->related = $query->getModel();
  60. $this->addConstraints();
  61. }
  62. /**
  63. * Run a callback with constraints disabled on the relation.
  64. *
  65. * @param \Closure $callback
  66. * @return mixed
  67. */
  68. public static function noConstraints(Closure $callback)
  69. {
  70. $previous = static::$constraints;
  71. static::$constraints = false;
  72. // When resetting the relation where clause, we want to shift the first element
  73. // off of the bindings, leaving only the constraints that the developers put
  74. // as "extra" on the relationships, and not original relation constraints.
  75. try {
  76. return call_user_func($callback);
  77. } finally {
  78. static::$constraints = $previous;
  79. }
  80. }
  81. /**
  82. * Set the base constraints on the relation query.
  83. *
  84. * @return void
  85. */
  86. abstract public function addConstraints();
  87. /**
  88. * Set the constraints for an eager load of the relation.
  89. *
  90. * @param array $models
  91. * @return void
  92. */
  93. abstract public function addEagerConstraints(array $models);
  94. /**
  95. * Initialize the relation on a set of models.
  96. *
  97. * @param array $models
  98. * @param string $relation
  99. * @return array
  100. */
  101. abstract public function initRelation(array $models, $relation);
  102. /**
  103. * Match the eagerly loaded results to their parents.
  104. *
  105. * @param array $models
  106. * @param \Illuminate\Database\Eloquent\Collection $results
  107. * @param string $relation
  108. * @return array
  109. */
  110. abstract public function match(array $models, Collection $results, $relation);
  111. /**
  112. * Get the results of the relationship.
  113. *
  114. * @return mixed
  115. */
  116. abstract public function getResults();
  117. /**
  118. * Get the relationship for eager loading.
  119. *
  120. * @return \Illuminate\Database\Eloquent\Collection
  121. */
  122. public function getEager()
  123. {
  124. return $this->get();
  125. }
  126. /**
  127. * Execute the query as a "select" statement.
  128. *
  129. * @param array $columns
  130. * @return \Illuminate\Database\Eloquent\Collection
  131. */
  132. public function get($columns = ['*'])
  133. {
  134. return $this->query->get($columns);
  135. }
  136. /**
  137. * Touch all of the related models for the relationship.
  138. *
  139. * @return void
  140. */
  141. public function touch()
  142. {
  143. $column = $this->getRelated()->getUpdatedAtColumn();
  144. $this->rawUpdate([$column => $this->getRelated()->freshTimestampString()]);
  145. }
  146. /**
  147. * Run a raw update against the base query.
  148. *
  149. * @param array $attributes
  150. * @return int
  151. */
  152. public function rawUpdate(array $attributes = [])
  153. {
  154. return $this->query->withoutGlobalScopes()->update($attributes);
  155. }
  156. /**
  157. * Add the constraints for a relationship count query.
  158. *
  159. * @param \Illuminate\Database\Eloquent\Builder $query
  160. * @param \Illuminate\Database\Eloquent\Builder $parentQuery
  161. * @return \Illuminate\Database\Eloquent\Builder
  162. */
  163. public function getRelationExistenceCountQuery(Builder $query, Builder $parentQuery)
  164. {
  165. return $this->getRelationExistenceQuery(
  166. $query, $parentQuery, new Expression('count(*)')
  167. )->setBindings([], 'select');
  168. }
  169. /**
  170. * Add the constraints for an internal relationship existence query.
  171. *
  172. * Essentially, these queries compare on column names like whereColumn.
  173. *
  174. * @param \Illuminate\Database\Eloquent\Builder $query
  175. * @param \Illuminate\Database\Eloquent\Builder $parentQuery
  176. * @param array|mixed $columns
  177. * @return \Illuminate\Database\Eloquent\Builder
  178. */
  179. public function getRelationExistenceQuery(Builder $query, Builder $parentQuery, $columns = ['*'])
  180. {
  181. return $query->select($columns)->whereColumn(
  182. $this->getQualifiedParentKeyName(), '=', $this->getExistenceCompareKey()
  183. );
  184. }
  185. /**
  186. * Get all of the primary keys for an array of models.
  187. *
  188. * @param array $models
  189. * @param string $key
  190. * @return array
  191. */
  192. protected function getKeys(array $models, $key = null)
  193. {
  194. return collect($models)->map(function ($value) use ($key) {
  195. return $key ? $value->getAttribute($key) : $value->getKey();
  196. })->values()->unique()->sort()->all();
  197. }
  198. /**
  199. * Get the underlying query for the relation.
  200. *
  201. * @return \Illuminate\Database\Eloquent\Builder
  202. */
  203. public function getQuery()
  204. {
  205. return $this->query;
  206. }
  207. /**
  208. * Get the base query builder driving the Eloquent builder.
  209. *
  210. * @return \Illuminate\Database\Query\Builder
  211. */
  212. public function getBaseQuery()
  213. {
  214. return $this->query->getQuery();
  215. }
  216. /**
  217. * Get the parent model of the relation.
  218. *
  219. * @return \Illuminate\Database\Eloquent\Model
  220. */
  221. public function getParent()
  222. {
  223. return $this->parent;
  224. }
  225. /**
  226. * Get the fully qualified parent key name.
  227. *
  228. * @return string
  229. */
  230. public function getQualifiedParentKeyName()
  231. {
  232. return $this->parent->getQualifiedKeyName();
  233. }
  234. /**
  235. * Get the related model of the relation.
  236. *
  237. * @return \Illuminate\Database\Eloquent\Model
  238. */
  239. public function getRelated()
  240. {
  241. return $this->related;
  242. }
  243. /**
  244. * Get the name of the "created at" column.
  245. *
  246. * @return string
  247. */
  248. public function createdAt()
  249. {
  250. return $this->parent->getCreatedAtColumn();
  251. }
  252. /**
  253. * Get the name of the "updated at" column.
  254. *
  255. * @return string
  256. */
  257. public function updatedAt()
  258. {
  259. return $this->parent->getUpdatedAtColumn();
  260. }
  261. /**
  262. * Get the name of the related model's "updated at" column.
  263. *
  264. * @return string
  265. */
  266. public function relatedUpdatedAt()
  267. {
  268. return $this->related->getUpdatedAtColumn();
  269. }
  270. /**
  271. * Set or get the morph map for polymorphic relations.
  272. *
  273. * @param array|null $map
  274. * @param bool $merge
  275. * @return array
  276. */
  277. public static function morphMap(array $map = null, $merge = true)
  278. {
  279. $map = static::buildMorphMapFromModels($map);
  280. if (is_array($map)) {
  281. static::$morphMap = $merge && static::$morphMap
  282. ? $map + static::$morphMap : $map;
  283. }
  284. return static::$morphMap;
  285. }
  286. /**
  287. * Builds a table-keyed array from model class names.
  288. *
  289. * @param string[]|null $models
  290. * @return array|null
  291. */
  292. protected static function buildMorphMapFromModels(array $models = null)
  293. {
  294. if (is_null($models) || Arr::isAssoc($models)) {
  295. return $models;
  296. }
  297. return array_combine(array_map(function ($model) {
  298. return (new $model)->getTable();
  299. }, $models), $models);
  300. }
  301. /**
  302. * Get the model associated with a custom polymorphic type.
  303. *
  304. * @param string $alias
  305. * @return string|null
  306. */
  307. public static function getMorphedModel($alias)
  308. {
  309. return self::$morphMap[$alias] ?? null;
  310. }
  311. /**
  312. * Handle dynamic method calls to the relationship.
  313. *
  314. * @param string $method
  315. * @param array $parameters
  316. * @return mixed
  317. */
  318. public function __call($method, $parameters)
  319. {
  320. if (static::hasMacro($method)) {
  321. return $this->macroCall($method, $parameters);
  322. }
  323. $result = $this->query->{$method}(...$parameters);
  324. if ($result === $this->query) {
  325. return $this;
  326. }
  327. return $result;
  328. }
  329. /**
  330. * Force a clone of the underlying query builder when cloning.
  331. *
  332. * @return void
  333. */
  334. public function __clone()
  335. {
  336. $this->query = clone $this->query;
  337. }
  338. }