Pivot.php 7.7KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303
  1. <?php
  2. namespace Illuminate\Database\Eloquent\Relations;
  3. use Illuminate\Support\Str;
  4. use Illuminate\Database\Eloquent\Model;
  5. use Illuminate\Database\Eloquent\Builder;
  6. class Pivot extends Model
  7. {
  8. /**
  9. * The parent model of the relationship.
  10. *
  11. * @var \Illuminate\Database\Eloquent\Model
  12. */
  13. public $pivotParent;
  14. /**
  15. * The name of the foreign key column.
  16. *
  17. * @var string
  18. */
  19. protected $foreignKey;
  20. /**
  21. * The name of the "other key" column.
  22. *
  23. * @var string
  24. */
  25. protected $relatedKey;
  26. /**
  27. * The attributes that aren't mass assignable.
  28. *
  29. * @var array
  30. */
  31. protected $guarded = [];
  32. /**
  33. * Create a new pivot model instance.
  34. *
  35. * @param \Illuminate\Database\Eloquent\Model $parent
  36. * @param array $attributes
  37. * @param string $table
  38. * @param bool $exists
  39. * @return static
  40. */
  41. public static function fromAttributes(Model $parent, $attributes, $table, $exists = false)
  42. {
  43. $instance = new static;
  44. // The pivot model is a "dynamic" model since we will set the tables dynamically
  45. // for the instance. This allows it work for any intermediate tables for the
  46. // many to many relationship that are defined by this developer's classes.
  47. $instance->setConnection($parent->getConnectionName())
  48. ->setTable($table)
  49. ->forceFill($attributes)
  50. ->syncOriginal();
  51. // We store off the parent instance so we will access the timestamp column names
  52. // for the model, since the pivot model timestamps aren't easily configurable
  53. // from the developer's point of view. We can use the parents to get these.
  54. $instance->pivotParent = $parent;
  55. $instance->exists = $exists;
  56. $instance->timestamps = $instance->hasTimestampAttributes();
  57. return $instance;
  58. }
  59. /**
  60. * Create a new pivot model from raw values returned from a query.
  61. *
  62. * @param \Illuminate\Database\Eloquent\Model $parent
  63. * @param array $attributes
  64. * @param string $table
  65. * @param bool $exists
  66. * @return static
  67. */
  68. public static function fromRawAttributes(Model $parent, $attributes, $table, $exists = false)
  69. {
  70. $instance = static::fromAttributes($parent, [], $table, $exists);
  71. $instance->setRawAttributes($attributes, true);
  72. $instance->timestamps = $instance->hasTimestampAttributes();
  73. return $instance;
  74. }
  75. /**
  76. * Set the keys for a save update query.
  77. *
  78. * @param \Illuminate\Database\Eloquent\Builder $query
  79. * @return \Illuminate\Database\Eloquent\Builder
  80. */
  81. protected function setKeysForSaveQuery(Builder $query)
  82. {
  83. if (isset($this->attributes[$this->getKeyName()])) {
  84. return parent::setKeysForSaveQuery($query);
  85. }
  86. $query->where($this->foreignKey, $this->getOriginal(
  87. $this->foreignKey, $this->getAttribute($this->foreignKey)
  88. ));
  89. return $query->where($this->relatedKey, $this->getOriginal(
  90. $this->relatedKey, $this->getAttribute($this->relatedKey)
  91. ));
  92. }
  93. /**
  94. * Delete the pivot model record from the database.
  95. *
  96. * @return int
  97. */
  98. public function delete()
  99. {
  100. if (isset($this->attributes[$this->getKeyName()])) {
  101. return parent::delete();
  102. }
  103. return $this->getDeleteQuery()->delete();
  104. }
  105. /**
  106. * Get the query builder for a delete operation on the pivot.
  107. *
  108. * @return \Illuminate\Database\Eloquent\Builder
  109. */
  110. protected function getDeleteQuery()
  111. {
  112. return $this->newQuery()->where([
  113. $this->foreignKey => $this->getOriginal($this->foreignKey, $this->getAttribute($this->foreignKey)),
  114. $this->relatedKey => $this->getOriginal($this->relatedKey, $this->getAttribute($this->relatedKey)),
  115. ]);
  116. }
  117. /**
  118. * Get the table associated with the model.
  119. *
  120. * @return string
  121. */
  122. public function getTable()
  123. {
  124. if (! isset($this->table)) {
  125. $this->setTable(str_replace(
  126. '\\', '', Str::snake(Str::singular(class_basename($this)))
  127. ));
  128. }
  129. return $this->table;
  130. }
  131. /**
  132. * Get the foreign key column name.
  133. *
  134. * @return string
  135. */
  136. public function getForeignKey()
  137. {
  138. return $this->foreignKey;
  139. }
  140. /**
  141. * Get the "related key" column name.
  142. *
  143. * @return string
  144. */
  145. public function getRelatedKey()
  146. {
  147. return $this->relatedKey;
  148. }
  149. /**
  150. * Get the "related key" column name.
  151. *
  152. * @return string
  153. */
  154. public function getOtherKey()
  155. {
  156. return $this->getRelatedKey();
  157. }
  158. /**
  159. * Set the key names for the pivot model instance.
  160. *
  161. * @param string $foreignKey
  162. * @param string $relatedKey
  163. * @return $this
  164. */
  165. public function setPivotKeys($foreignKey, $relatedKey)
  166. {
  167. $this->foreignKey = $foreignKey;
  168. $this->relatedKey = $relatedKey;
  169. return $this;
  170. }
  171. /**
  172. * Determine if the pivot model has timestamp attributes.
  173. *
  174. * @return bool
  175. */
  176. public function hasTimestampAttributes()
  177. {
  178. return array_key_exists($this->getCreatedAtColumn(), $this->attributes);
  179. }
  180. /**
  181. * Get the name of the "created at" column.
  182. *
  183. * @return string
  184. */
  185. public function getCreatedAtColumn()
  186. {
  187. return ($this->pivotParent)
  188. ? $this->pivotParent->getCreatedAtColumn()
  189. : parent::getCreatedAtColumn();
  190. }
  191. /**
  192. * Get the name of the "updated at" column.
  193. *
  194. * @return string
  195. */
  196. public function getUpdatedAtColumn()
  197. {
  198. return ($this->pivotParent)
  199. ? $this->pivotParent->getUpdatedAtColumn()
  200. : parent::getUpdatedAtColumn();
  201. }
  202. /**
  203. * Get the queueable identity for the entity.
  204. *
  205. * @return mixed
  206. */
  207. public function getQueueableId()
  208. {
  209. if (isset($this->attributes[$this->getKeyName()])) {
  210. return $this->getKey();
  211. }
  212. return sprintf(
  213. '%s:%s:%s:%s',
  214. $this->foreignKey, $this->getAttribute($this->foreignKey),
  215. $this->relatedKey, $this->getAttribute($this->relatedKey)
  216. );
  217. }
  218. /**
  219. * Get a new query to restore one or more models by their queueable IDs.
  220. *
  221. * @param array|int $ids
  222. * @return \Illuminate\Database\Eloquent\Builder
  223. */
  224. public function newQueryForRestoration($ids)
  225. {
  226. if (is_array($ids)) {
  227. return $this->newQueryForCollectionRestoration($ids);
  228. }
  229. if (! Str::contains($ids, ':')) {
  230. return parent::newQueryForRestoration($ids);
  231. }
  232. $segments = explode(':', $ids);
  233. return $this->newQueryWithoutScopes()
  234. ->where($segments[0], $segments[1])
  235. ->where($segments[2], $segments[3]);
  236. }
  237. /**
  238. * Get a new query to restore multiple models by their queueable IDs.
  239. *
  240. * @param array|int $ids
  241. * @return \Illuminate\Database\Eloquent\Builder
  242. */
  243. protected function newQueryForCollectionRestoration(array $ids)
  244. {
  245. if (! Str::contains($ids[0], ':')) {
  246. return parent::newQueryForRestoration($ids);
  247. }
  248. $query = $this->newQueryWithoutScopes();
  249. foreach ($ids as $id) {
  250. $segments = explode(':', $id);
  251. $query->orWhere(function ($query) use ($segments) {
  252. return $query->where($segments[0], $segments[1])
  253. ->where($segments[2], $segments[3]);
  254. });
  255. }
  256. return $query;
  257. }
  258. }