HasRelationships.php 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929
  1. <?php
  2. namespace Illuminate\Database\Eloquent\Concerns;
  3. use Closure;
  4. use Illuminate\Database\ClassMorphViolationException;
  5. use Illuminate\Database\Eloquent\Builder;
  6. use Illuminate\Database\Eloquent\Collection;
  7. use Illuminate\Database\Eloquent\Model;
  8. use Illuminate\Database\Eloquent\PendingHasThroughRelationship;
  9. use Illuminate\Database\Eloquent\Relations\BelongsTo;
  10. use Illuminate\Database\Eloquent\Relations\BelongsToMany;
  11. use Illuminate\Database\Eloquent\Relations\HasMany;
  12. use Illuminate\Database\Eloquent\Relations\HasManyThrough;
  13. use Illuminate\Database\Eloquent\Relations\HasOne;
  14. use Illuminate\Database\Eloquent\Relations\HasOneThrough;
  15. use Illuminate\Database\Eloquent\Relations\MorphMany;
  16. use Illuminate\Database\Eloquent\Relations\MorphOne;
  17. use Illuminate\Database\Eloquent\Relations\MorphTo;
  18. use Illuminate\Database\Eloquent\Relations\MorphToMany;
  19. use Illuminate\Database\Eloquent\Relations\Pivot;
  20. use Illuminate\Database\Eloquent\Relations\Relation;
  21. use Illuminate\Support\Arr;
  22. use Illuminate\Support\Str;
  23. trait HasRelationships
  24. {
  25. /**
  26. * The loaded relationships for the model.
  27. *
  28. * @var array
  29. */
  30. protected $relations = [];
  31. /**
  32. * The relationships that should be touched on save.
  33. *
  34. * @var array
  35. */
  36. protected $touches = [];
  37. /**
  38. * The many to many relationship methods.
  39. *
  40. * @var string[]
  41. */
  42. public static $manyMethods = [
  43. 'belongsToMany', 'morphToMany', 'morphedByMany',
  44. ];
  45. /**
  46. * The relation resolver callbacks.
  47. *
  48. * @var array
  49. */
  50. protected static $relationResolvers = [];
  51. /**
  52. * Get the dynamic relation resolver if defined or inherited, or return null.
  53. *
  54. * @param string $class
  55. * @param string $key
  56. * @return mixed
  57. */
  58. public function relationResolver($class, $key)
  59. {
  60. if ($resolver = static::$relationResolvers[$class][$key] ?? null) {
  61. return $resolver;
  62. }
  63. if ($parent = get_parent_class($class)) {
  64. return $this->relationResolver($parent, $key);
  65. }
  66. return null;
  67. }
  68. /**
  69. * Define a dynamic relation resolver.
  70. *
  71. * @param string $name
  72. * @param \Closure $callback
  73. * @return void
  74. */
  75. public static function resolveRelationUsing($name, Closure $callback)
  76. {
  77. static::$relationResolvers = array_replace_recursive(
  78. static::$relationResolvers,
  79. [static::class => [$name => $callback]]
  80. );
  81. }
  82. /**
  83. * Define a one-to-one relationship.
  84. *
  85. * @param string $related
  86. * @param string|null $foreignKey
  87. * @param string|null $localKey
  88. * @return \Illuminate\Database\Eloquent\Relations\HasOne
  89. */
  90. public function hasOne($related, $foreignKey = null, $localKey = null)
  91. {
  92. $instance = $this->newRelatedInstance($related);
  93. $foreignKey = $foreignKey ?: $this->getForeignKey();
  94. $localKey = $localKey ?: $this->getKeyName();
  95. return $this->newHasOne($instance->newQuery(), $this, $instance->getTable().'.'.$foreignKey, $localKey);
  96. }
  97. /**
  98. * Instantiate a new HasOne relationship.
  99. *
  100. * @param \Illuminate\Database\Eloquent\Builder $query
  101. * @param \Illuminate\Database\Eloquent\Model $parent
  102. * @param string $foreignKey
  103. * @param string $localKey
  104. * @return \Illuminate\Database\Eloquent\Relations\HasOne
  105. */
  106. protected function newHasOne(Builder $query, Model $parent, $foreignKey, $localKey)
  107. {
  108. return new HasOne($query, $parent, $foreignKey, $localKey);
  109. }
  110. /**
  111. * Define a has-one-through relationship.
  112. *
  113. * @param string $related
  114. * @param string $through
  115. * @param string|null $firstKey
  116. * @param string|null $secondKey
  117. * @param string|null $localKey
  118. * @param string|null $secondLocalKey
  119. * @return \Illuminate\Database\Eloquent\Relations\HasOneThrough
  120. */
  121. public function hasOneThrough($related, $through, $firstKey = null, $secondKey = null, $localKey = null, $secondLocalKey = null)
  122. {
  123. $through = $this->newRelatedThroughInstance($through);
  124. $firstKey = $firstKey ?: $this->getForeignKey();
  125. $secondKey = $secondKey ?: $through->getForeignKey();
  126. return $this->newHasOneThrough(
  127. $this->newRelatedInstance($related)->newQuery(), $this, $through,
  128. $firstKey, $secondKey, $localKey ?: $this->getKeyName(),
  129. $secondLocalKey ?: $through->getKeyName()
  130. );
  131. }
  132. /**
  133. * Instantiate a new HasOneThrough relationship.
  134. *
  135. * @param \Illuminate\Database\Eloquent\Builder $query
  136. * @param \Illuminate\Database\Eloquent\Model $farParent
  137. * @param \Illuminate\Database\Eloquent\Model $throughParent
  138. * @param string $firstKey
  139. * @param string $secondKey
  140. * @param string $localKey
  141. * @param string $secondLocalKey
  142. * @return \Illuminate\Database\Eloquent\Relations\HasOneThrough
  143. */
  144. protected function newHasOneThrough(Builder $query, Model $farParent, Model $throughParent, $firstKey, $secondKey, $localKey, $secondLocalKey)
  145. {
  146. return new HasOneThrough($query, $farParent, $throughParent, $firstKey, $secondKey, $localKey, $secondLocalKey);
  147. }
  148. /**
  149. * Define a polymorphic one-to-one relationship.
  150. *
  151. * @param string $related
  152. * @param string $name
  153. * @param string|null $type
  154. * @param string|null $id
  155. * @param string|null $localKey
  156. * @return \Illuminate\Database\Eloquent\Relations\MorphOne
  157. */
  158. public function morphOne($related, $name, $type = null, $id = null, $localKey = null)
  159. {
  160. $instance = $this->newRelatedInstance($related);
  161. [$type, $id] = $this->getMorphs($name, $type, $id);
  162. $table = $instance->getTable();
  163. $localKey = $localKey ?: $this->getKeyName();
  164. return $this->newMorphOne($instance->newQuery(), $this, $table.'.'.$type, $table.'.'.$id, $localKey);
  165. }
  166. /**
  167. * Instantiate a new MorphOne relationship.
  168. *
  169. * @param \Illuminate\Database\Eloquent\Builder $query
  170. * @param \Illuminate\Database\Eloquent\Model $parent
  171. * @param string $type
  172. * @param string $id
  173. * @param string $localKey
  174. * @return \Illuminate\Database\Eloquent\Relations\MorphOne
  175. */
  176. protected function newMorphOne(Builder $query, Model $parent, $type, $id, $localKey)
  177. {
  178. return new MorphOne($query, $parent, $type, $id, $localKey);
  179. }
  180. /**
  181. * Define an inverse one-to-one or many relationship.
  182. *
  183. * @param string $related
  184. * @param string|null $foreignKey
  185. * @param string|null $ownerKey
  186. * @param string|null $relation
  187. * @return \Illuminate\Database\Eloquent\Relations\BelongsTo
  188. */
  189. public function belongsTo($related, $foreignKey = null, $ownerKey = null, $relation = null)
  190. {
  191. // If no relation name was given, we will use this debug backtrace to extract
  192. // the calling method's name and use that as the relationship name as most
  193. // of the time this will be what we desire to use for the relationships.
  194. if (is_null($relation)) {
  195. $relation = $this->guessBelongsToRelation();
  196. }
  197. $instance = $this->newRelatedInstance($related);
  198. // If no foreign key was supplied, we can use a backtrace to guess the proper
  199. // foreign key name by using the name of the relationship function, which
  200. // when combined with an "_id" should conventionally match the columns.
  201. if (is_null($foreignKey)) {
  202. $foreignKey = Str::snake($relation).'_'.$instance->getKeyName();
  203. }
  204. // Once we have the foreign key names we'll just create a new Eloquent query
  205. // for the related models and return the relationship instance which will
  206. // actually be responsible for retrieving and hydrating every relation.
  207. $ownerKey = $ownerKey ?: $instance->getKeyName();
  208. return $this->newBelongsTo(
  209. $instance->newQuery(), $this, $foreignKey, $ownerKey, $relation
  210. );
  211. }
  212. /**
  213. * Instantiate a new BelongsTo relationship.
  214. *
  215. * @param \Illuminate\Database\Eloquent\Builder $query
  216. * @param \Illuminate\Database\Eloquent\Model $child
  217. * @param string $foreignKey
  218. * @param string $ownerKey
  219. * @param string $relation
  220. * @return \Illuminate\Database\Eloquent\Relations\BelongsTo
  221. */
  222. protected function newBelongsTo(Builder $query, Model $child, $foreignKey, $ownerKey, $relation)
  223. {
  224. return new BelongsTo($query, $child, $foreignKey, $ownerKey, $relation);
  225. }
  226. /**
  227. * Define a polymorphic, inverse one-to-one or many relationship.
  228. *
  229. * @param string|null $name
  230. * @param string|null $type
  231. * @param string|null $id
  232. * @param string|null $ownerKey
  233. * @return \Illuminate\Database\Eloquent\Relations\MorphTo
  234. */
  235. public function morphTo($name = null, $type = null, $id = null, $ownerKey = null)
  236. {
  237. // If no name is provided, we will use the backtrace to get the function name
  238. // since that is most likely the name of the polymorphic interface. We can
  239. // use that to get both the class and foreign key that will be utilized.
  240. $name = $name ?: $this->guessBelongsToRelation();
  241. [$type, $id] = $this->getMorphs(
  242. Str::snake($name), $type, $id
  243. );
  244. // If the type value is null it is probably safe to assume we're eager loading
  245. // the relationship. In this case we'll just pass in a dummy query where we
  246. // need to remove any eager loads that may already be defined on a model.
  247. return is_null($class = $this->getAttributeFromArray($type)) || $class === ''
  248. ? $this->morphEagerTo($name, $type, $id, $ownerKey)
  249. : $this->morphInstanceTo($class, $name, $type, $id, $ownerKey);
  250. }
  251. /**
  252. * Define a polymorphic, inverse one-to-one or many relationship.
  253. *
  254. * @param string $name
  255. * @param string $type
  256. * @param string $id
  257. * @param string $ownerKey
  258. * @return \Illuminate\Database\Eloquent\Relations\MorphTo
  259. */
  260. protected function morphEagerTo($name, $type, $id, $ownerKey)
  261. {
  262. return $this->newMorphTo(
  263. $this->newQuery()->setEagerLoads([]), $this, $id, $ownerKey, $type, $name
  264. );
  265. }
  266. /**
  267. * Define a polymorphic, inverse one-to-one or many relationship.
  268. *
  269. * @param string $target
  270. * @param string $name
  271. * @param string $type
  272. * @param string $id
  273. * @param string $ownerKey
  274. * @return \Illuminate\Database\Eloquent\Relations\MorphTo
  275. */
  276. protected function morphInstanceTo($target, $name, $type, $id, $ownerKey)
  277. {
  278. $instance = $this->newRelatedInstance(
  279. static::getActualClassNameForMorph($target)
  280. );
  281. return $this->newMorphTo(
  282. $instance->newQuery(), $this, $id, $ownerKey ?? $instance->getKeyName(), $type, $name
  283. );
  284. }
  285. /**
  286. * Instantiate a new MorphTo relationship.
  287. *
  288. * @param \Illuminate\Database\Eloquent\Builder $query
  289. * @param \Illuminate\Database\Eloquent\Model $parent
  290. * @param string $foreignKey
  291. * @param string $ownerKey
  292. * @param string $type
  293. * @param string $relation
  294. * @return \Illuminate\Database\Eloquent\Relations\MorphTo
  295. */
  296. protected function newMorphTo(Builder $query, Model $parent, $foreignKey, $ownerKey, $type, $relation)
  297. {
  298. return new MorphTo($query, $parent, $foreignKey, $ownerKey, $type, $relation);
  299. }
  300. /**
  301. * Retrieve the actual class name for a given morph class.
  302. *
  303. * @param string $class
  304. * @return string
  305. */
  306. public static function getActualClassNameForMorph($class)
  307. {
  308. return Arr::get(Relation::morphMap() ?: [], $class, $class);
  309. }
  310. /**
  311. * Guess the "belongs to" relationship name.
  312. *
  313. * @return string
  314. */
  315. protected function guessBelongsToRelation()
  316. {
  317. [$one, $two, $caller] = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 3);
  318. return $caller['function'];
  319. }
  320. /**
  321. * Create a pending has-many-through or has-one-through relationship.
  322. *
  323. * @param string|\Illuminate\Database\Eloquent\Relations\HasMany|\Illuminate\Database\Eloquent\Relations\HasOne $relationship
  324. * @return \Illuminate\Database\Eloquent\PendingHasThroughRelationship
  325. */
  326. public function through($relationship)
  327. {
  328. if (is_string($relationship)) {
  329. $relationship = $this->{$relationship}();
  330. }
  331. return new PendingHasThroughRelationship($this, $relationship);
  332. }
  333. /**
  334. * Define a one-to-many relationship.
  335. *
  336. * @param string $related
  337. * @param string|null $foreignKey
  338. * @param string|null $localKey
  339. * @return \Illuminate\Database\Eloquent\Relations\HasMany
  340. */
  341. public function hasMany($related, $foreignKey = null, $localKey = null)
  342. {
  343. $instance = $this->newRelatedInstance($related);
  344. $foreignKey = $foreignKey ?: $this->getForeignKey();
  345. $localKey = $localKey ?: $this->getKeyName();
  346. return $this->newHasMany(
  347. $instance->newQuery(), $this, $instance->getTable().'.'.$foreignKey, $localKey
  348. );
  349. }
  350. /**
  351. * Instantiate a new HasMany relationship.
  352. *
  353. * @param \Illuminate\Database\Eloquent\Builder $query
  354. * @param \Illuminate\Database\Eloquent\Model $parent
  355. * @param string $foreignKey
  356. * @param string $localKey
  357. * @return \Illuminate\Database\Eloquent\Relations\HasMany
  358. */
  359. protected function newHasMany(Builder $query, Model $parent, $foreignKey, $localKey)
  360. {
  361. return new HasMany($query, $parent, $foreignKey, $localKey);
  362. }
  363. /**
  364. * Define a has-many-through relationship.
  365. *
  366. * @param string $related
  367. * @param string $through
  368. * @param string|null $firstKey
  369. * @param string|null $secondKey
  370. * @param string|null $localKey
  371. * @param string|null $secondLocalKey
  372. * @return \Illuminate\Database\Eloquent\Relations\HasManyThrough
  373. */
  374. public function hasManyThrough($related, $through, $firstKey = null, $secondKey = null, $localKey = null, $secondLocalKey = null)
  375. {
  376. $through = $this->newRelatedThroughInstance($through);
  377. $firstKey = $firstKey ?: $this->getForeignKey();
  378. $secondKey = $secondKey ?: $through->getForeignKey();
  379. return $this->newHasManyThrough(
  380. $this->newRelatedInstance($related)->newQuery(),
  381. $this,
  382. $through,
  383. $firstKey,
  384. $secondKey,
  385. $localKey ?: $this->getKeyName(),
  386. $secondLocalKey ?: $through->getKeyName()
  387. );
  388. }
  389. /**
  390. * Instantiate a new HasManyThrough relationship.
  391. *
  392. * @param \Illuminate\Database\Eloquent\Builder $query
  393. * @param \Illuminate\Database\Eloquent\Model $farParent
  394. * @param \Illuminate\Database\Eloquent\Model $throughParent
  395. * @param string $firstKey
  396. * @param string $secondKey
  397. * @param string $localKey
  398. * @param string $secondLocalKey
  399. * @return \Illuminate\Database\Eloquent\Relations\HasManyThrough
  400. */
  401. protected function newHasManyThrough(Builder $query, Model $farParent, Model $throughParent, $firstKey, $secondKey, $localKey, $secondLocalKey)
  402. {
  403. return new HasManyThrough($query, $farParent, $throughParent, $firstKey, $secondKey, $localKey, $secondLocalKey);
  404. }
  405. /**
  406. * Define a polymorphic one-to-many relationship.
  407. *
  408. * @param string $related
  409. * @param string $name
  410. * @param string|null $type
  411. * @param string|null $id
  412. * @param string|null $localKey
  413. * @return \Illuminate\Database\Eloquent\Relations\MorphMany
  414. */
  415. public function morphMany($related, $name, $type = null, $id = null, $localKey = null)
  416. {
  417. $instance = $this->newRelatedInstance($related);
  418. // Here we will gather up the morph type and ID for the relationship so that we
  419. // can properly query the intermediate table of a relation. Finally, we will
  420. // get the table and create the relationship instances for the developers.
  421. [$type, $id] = $this->getMorphs($name, $type, $id);
  422. $table = $instance->getTable();
  423. $localKey = $localKey ?: $this->getKeyName();
  424. return $this->newMorphMany($instance->newQuery(), $this, $table.'.'.$type, $table.'.'.$id, $localKey);
  425. }
  426. /**
  427. * Instantiate a new MorphMany relationship.
  428. *
  429. * @param \Illuminate\Database\Eloquent\Builder $query
  430. * @param \Illuminate\Database\Eloquent\Model $parent
  431. * @param string $type
  432. * @param string $id
  433. * @param string $localKey
  434. * @return \Illuminate\Database\Eloquent\Relations\MorphMany
  435. */
  436. protected function newMorphMany(Builder $query, Model $parent, $type, $id, $localKey)
  437. {
  438. return new MorphMany($query, $parent, $type, $id, $localKey);
  439. }
  440. /**
  441. * Define a many-to-many relationship.
  442. *
  443. * @param string $related
  444. * @param string|class-string<\Illuminate\Database\Eloquent\Model>|null $table
  445. * @param string|null $foreignPivotKey
  446. * @param string|null $relatedPivotKey
  447. * @param string|null $parentKey
  448. * @param string|null $relatedKey
  449. * @param string|null $relation
  450. * @return \Illuminate\Database\Eloquent\Relations\BelongsToMany
  451. */
  452. public function belongsToMany($related, $table = null, $foreignPivotKey = null, $relatedPivotKey = null,
  453. $parentKey = null, $relatedKey = null, $relation = null)
  454. {
  455. // If no relationship name was passed, we will pull backtraces to get the
  456. // name of the calling function. We will use that function name as the
  457. // title of this relation since that is a great convention to apply.
  458. if (is_null($relation)) {
  459. $relation = $this->guessBelongsToManyRelation();
  460. }
  461. // First, we'll need to determine the foreign key and "other key" for the
  462. // relationship. Once we have determined the keys we'll make the query
  463. // instances as well as the relationship instances we need for this.
  464. $instance = $this->newRelatedInstance($related);
  465. $foreignPivotKey = $foreignPivotKey ?: $this->getForeignKey();
  466. $relatedPivotKey = $relatedPivotKey ?: $instance->getForeignKey();
  467. // If no table name was provided, we can guess it by concatenating the two
  468. // models using underscores in alphabetical order. The two model names
  469. // are transformed to snake case from their default CamelCase also.
  470. if (is_null($table)) {
  471. $table = $this->joiningTable($related, $instance);
  472. }
  473. return $this->newBelongsToMany(
  474. $instance->newQuery(), $this, $table, $foreignPivotKey,
  475. $relatedPivotKey, $parentKey ?: $this->getKeyName(),
  476. $relatedKey ?: $instance->getKeyName(), $relation
  477. );
  478. }
  479. /**
  480. * Instantiate a new BelongsToMany relationship.
  481. *
  482. * @param \Illuminate\Database\Eloquent\Builder $query
  483. * @param \Illuminate\Database\Eloquent\Model $parent
  484. * @param string|class-string<\Illuminate\Database\Eloquent\Model> $table
  485. * @param string $foreignPivotKey
  486. * @param string $relatedPivotKey
  487. * @param string $parentKey
  488. * @param string $relatedKey
  489. * @param string|null $relationName
  490. * @return \Illuminate\Database\Eloquent\Relations\BelongsToMany
  491. */
  492. protected function newBelongsToMany(Builder $query, Model $parent, $table, $foreignPivotKey, $relatedPivotKey,
  493. $parentKey, $relatedKey, $relationName = null)
  494. {
  495. return new BelongsToMany($query, $parent, $table, $foreignPivotKey, $relatedPivotKey, $parentKey, $relatedKey, $relationName);
  496. }
  497. /**
  498. * Define a polymorphic many-to-many relationship.
  499. *
  500. * @param string $related
  501. * @param string $name
  502. * @param string|null $table
  503. * @param string|null $foreignPivotKey
  504. * @param string|null $relatedPivotKey
  505. * @param string|null $parentKey
  506. * @param string|null $relatedKey
  507. * @param string|null $relation
  508. * @param bool $inverse
  509. * @return \Illuminate\Database\Eloquent\Relations\MorphToMany
  510. */
  511. public function morphToMany($related, $name, $table = null, $foreignPivotKey = null,
  512. $relatedPivotKey = null, $parentKey = null,
  513. $relatedKey = null, $relation = null, $inverse = false)
  514. {
  515. $relation = $relation ?: $this->guessBelongsToManyRelation();
  516. // First, we will need to determine the foreign key and "other key" for the
  517. // relationship. Once we have determined the keys we will make the query
  518. // instances, as well as the relationship instances we need for these.
  519. $instance = $this->newRelatedInstance($related);
  520. $foreignPivotKey = $foreignPivotKey ?: $name.'_id';
  521. $relatedPivotKey = $relatedPivotKey ?: $instance->getForeignKey();
  522. // Now we're ready to create a new query builder for the related model and
  523. // the relationship instances for this relation. This relation will set
  524. // appropriate query constraints then entirely manage the hydrations.
  525. if (! $table) {
  526. $words = preg_split('/(_)/u', $name, -1, PREG_SPLIT_DELIM_CAPTURE);
  527. $lastWord = array_pop($words);
  528. $table = implode('', $words).Str::plural($lastWord);
  529. }
  530. return $this->newMorphToMany(
  531. $instance->newQuery(), $this, $name, $table,
  532. $foreignPivotKey, $relatedPivotKey, $parentKey ?: $this->getKeyName(),
  533. $relatedKey ?: $instance->getKeyName(), $relation, $inverse
  534. );
  535. }
  536. /**
  537. * Instantiate a new MorphToMany relationship.
  538. *
  539. * @param \Illuminate\Database\Eloquent\Builder $query
  540. * @param \Illuminate\Database\Eloquent\Model $parent
  541. * @param string $name
  542. * @param string $table
  543. * @param string $foreignPivotKey
  544. * @param string $relatedPivotKey
  545. * @param string $parentKey
  546. * @param string $relatedKey
  547. * @param string|null $relationName
  548. * @param bool $inverse
  549. * @return \Illuminate\Database\Eloquent\Relations\MorphToMany
  550. */
  551. protected function newMorphToMany(Builder $query, Model $parent, $name, $table, $foreignPivotKey,
  552. $relatedPivotKey, $parentKey, $relatedKey,
  553. $relationName = null, $inverse = false)
  554. {
  555. return new MorphToMany($query, $parent, $name, $table, $foreignPivotKey, $relatedPivotKey, $parentKey, $relatedKey,
  556. $relationName, $inverse);
  557. }
  558. /**
  559. * Define a polymorphic, inverse many-to-many relationship.
  560. *
  561. * @param string $related
  562. * @param string $name
  563. * @param string|null $table
  564. * @param string|null $foreignPivotKey
  565. * @param string|null $relatedPivotKey
  566. * @param string|null $parentKey
  567. * @param string|null $relatedKey
  568. * @param string|null $relation
  569. * @return \Illuminate\Database\Eloquent\Relations\MorphToMany
  570. */
  571. public function morphedByMany($related, $name, $table = null, $foreignPivotKey = null,
  572. $relatedPivotKey = null, $parentKey = null, $relatedKey = null, $relation = null)
  573. {
  574. $foreignPivotKey = $foreignPivotKey ?: $this->getForeignKey();
  575. // For the inverse of the polymorphic many-to-many relations, we will change
  576. // the way we determine the foreign and other keys, as it is the opposite
  577. // of the morph-to-many method since we're figuring out these inverses.
  578. $relatedPivotKey = $relatedPivotKey ?: $name.'_id';
  579. return $this->morphToMany(
  580. $related, $name, $table, $foreignPivotKey,
  581. $relatedPivotKey, $parentKey, $relatedKey, $relation, true
  582. );
  583. }
  584. /**
  585. * Get the relationship name of the belongsToMany relationship.
  586. *
  587. * @return string|null
  588. */
  589. protected function guessBelongsToManyRelation()
  590. {
  591. $caller = Arr::first(debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS), function ($trace) {
  592. return ! in_array(
  593. $trace['function'],
  594. array_merge(static::$manyMethods, ['guessBelongsToManyRelation'])
  595. );
  596. });
  597. return ! is_null($caller) ? $caller['function'] : null;
  598. }
  599. /**
  600. * Get the joining table name for a many-to-many relation.
  601. *
  602. * @param string $related
  603. * @param \Illuminate\Database\Eloquent\Model|null $instance
  604. * @return string
  605. */
  606. public function joiningTable($related, $instance = null)
  607. {
  608. // The joining table name, by convention, is simply the snake cased models
  609. // sorted alphabetically and concatenated with an underscore, so we can
  610. // just sort the models and join them together to get the table name.
  611. $segments = [
  612. $instance ? $instance->joiningTableSegment()
  613. : Str::snake(class_basename($related)),
  614. $this->joiningTableSegment(),
  615. ];
  616. // Now that we have the model names in an array we can just sort them and
  617. // use the implode function to join them together with an underscores,
  618. // which is typically used by convention within the database system.
  619. sort($segments);
  620. return strtolower(implode('_', $segments));
  621. }
  622. /**
  623. * Get this model's half of the intermediate table name for belongsToMany relationships.
  624. *
  625. * @return string
  626. */
  627. public function joiningTableSegment()
  628. {
  629. return Str::snake(class_basename($this));
  630. }
  631. /**
  632. * Determine if the model touches a given relation.
  633. *
  634. * @param string $relation
  635. * @return bool
  636. */
  637. public function touches($relation)
  638. {
  639. return in_array($relation, $this->getTouchedRelations());
  640. }
  641. /**
  642. * Touch the owning relations of the model.
  643. *
  644. * @return void
  645. */
  646. public function touchOwners()
  647. {
  648. foreach ($this->getTouchedRelations() as $relation) {
  649. $this->$relation()->touch();
  650. if ($this->$relation instanceof self) {
  651. $this->$relation->fireModelEvent('saved', false);
  652. $this->$relation->touchOwners();
  653. } elseif ($this->$relation instanceof Collection) {
  654. $this->$relation->each->touchOwners();
  655. }
  656. }
  657. }
  658. /**
  659. * Get the polymorphic relationship columns.
  660. *
  661. * @param string $name
  662. * @param string $type
  663. * @param string $id
  664. * @return array
  665. */
  666. protected function getMorphs($name, $type, $id)
  667. {
  668. return [$type ?: $name.'_type', $id ?: $name.'_id'];
  669. }
  670. /**
  671. * Get the class name for polymorphic relations.
  672. *
  673. * @return string
  674. */
  675. public function getMorphClass()
  676. {
  677. $morphMap = Relation::morphMap();
  678. if (! empty($morphMap) && in_array(static::class, $morphMap)) {
  679. return array_search(static::class, $morphMap, true);
  680. }
  681. if (static::class === Pivot::class) {
  682. return static::class;
  683. }
  684. if (Relation::requiresMorphMap()) {
  685. throw new ClassMorphViolationException($this);
  686. }
  687. return static::class;
  688. }
  689. /**
  690. * Create a new model instance for a related model.
  691. *
  692. * @param string $class
  693. * @return mixed
  694. */
  695. protected function newRelatedInstance($class)
  696. {
  697. return tap(new $class, function ($instance) {
  698. if (! $instance->getConnectionName()) {
  699. $instance->setConnection($this->connection);
  700. }
  701. });
  702. }
  703. /**
  704. * Create a new model instance for a related "through" model.
  705. *
  706. * @param string $class
  707. * @return mixed
  708. */
  709. protected function newRelatedThroughInstance($class)
  710. {
  711. return new $class;
  712. }
  713. /**
  714. * Get all the loaded relations for the instance.
  715. *
  716. * @return array
  717. */
  718. public function getRelations()
  719. {
  720. return $this->relations;
  721. }
  722. /**
  723. * Get a specified relationship.
  724. *
  725. * @param string $relation
  726. * @return mixed
  727. */
  728. public function getRelation($relation)
  729. {
  730. return $this->relations[$relation];
  731. }
  732. /**
  733. * Determine if the given relation is loaded.
  734. *
  735. * @param string $key
  736. * @return bool
  737. */
  738. public function relationLoaded($key)
  739. {
  740. return array_key_exists($key, $this->relations);
  741. }
  742. /**
  743. * Set the given relationship on the model.
  744. *
  745. * @param string $relation
  746. * @param mixed $value
  747. * @return $this
  748. */
  749. public function setRelation($relation, $value)
  750. {
  751. $this->relations[$relation] = $value;
  752. return $this;
  753. }
  754. /**
  755. * Unset a loaded relationship.
  756. *
  757. * @param string $relation
  758. * @return $this
  759. */
  760. public function unsetRelation($relation)
  761. {
  762. unset($this->relations[$relation]);
  763. return $this;
  764. }
  765. /**
  766. * Set the entire relations array on the model.
  767. *
  768. * @param array $relations
  769. * @return $this
  770. */
  771. public function setRelations(array $relations)
  772. {
  773. $this->relations = $relations;
  774. return $this;
  775. }
  776. /**
  777. * Duplicate the instance and unset all the loaded relations.
  778. *
  779. * @return $this
  780. */
  781. public function withoutRelations()
  782. {
  783. $model = clone $this;
  784. return $model->unsetRelations();
  785. }
  786. /**
  787. * Unset all the loaded relations for the instance.
  788. *
  789. * @return $this
  790. */
  791. public function unsetRelations()
  792. {
  793. $this->relations = [];
  794. return $this;
  795. }
  796. /**
  797. * Get the relationships that are touched on save.
  798. *
  799. * @return array
  800. */
  801. public function getTouchedRelations()
  802. {
  803. return $this->touches;
  804. }
  805. /**
  806. * Set the relationships that are touched on save.
  807. *
  808. * @param array $touches
  809. * @return $this
  810. */
  811. public function setTouchedRelations(array $touches)
  812. {
  813. $this->touches = $touches;
  814. return $this;
  815. }
  816. }