SoftDeletes.php 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249
  1. <?php
  2. namespace Illuminate\Database\Eloquent;
  3. /**
  4. * @method static \Illuminate\Database\Eloquent\Builder|\Illuminate\Database\Query\Builder withTrashed(bool $withTrashed = true)
  5. * @method static \Illuminate\Database\Eloquent\Builder|\Illuminate\Database\Query\Builder onlyTrashed()
  6. * @method static \Illuminate\Database\Eloquent\Builder|\Illuminate\Database\Query\Builder withoutTrashed()
  7. */
  8. trait SoftDeletes
  9. {
  10. /**
  11. * Indicates if the model is currently force deleting.
  12. *
  13. * @var bool
  14. */
  15. protected $forceDeleting = false;
  16. /**
  17. * Boot the soft deleting trait for a model.
  18. *
  19. * @return void
  20. */
  21. public static function bootSoftDeletes()
  22. {
  23. static::addGlobalScope(new SoftDeletingScope);
  24. }
  25. /**
  26. * Initialize the soft deleting trait for an instance.
  27. *
  28. * @return void
  29. */
  30. public function initializeSoftDeletes()
  31. {
  32. if (! isset($this->casts[$this->getDeletedAtColumn()])) {
  33. $this->casts[$this->getDeletedAtColumn()] = 'datetime';
  34. }
  35. }
  36. /**
  37. * Force a hard delete on a soft deleted model.
  38. *
  39. * @return bool|null
  40. */
  41. public function forceDelete()
  42. {
  43. if ($this->fireModelEvent('forceDeleting') === false) {
  44. return false;
  45. }
  46. $this->forceDeleting = true;
  47. return tap($this->delete(), function ($deleted) {
  48. $this->forceDeleting = false;
  49. if ($deleted) {
  50. $this->fireModelEvent('forceDeleted', false);
  51. }
  52. });
  53. }
  54. /**
  55. * Force a hard delete on a soft deleted model without raising any events.
  56. *
  57. * @return bool|null
  58. */
  59. public function forceDeleteQuietly()
  60. {
  61. return static::withoutEvents(fn () => $this->forceDelete());
  62. }
  63. /**
  64. * Perform the actual delete query on this model instance.
  65. *
  66. * @return mixed
  67. */
  68. protected function performDeleteOnModel()
  69. {
  70. if ($this->forceDeleting) {
  71. return tap($this->setKeysForSaveQuery($this->newModelQuery())->forceDelete(), function () {
  72. $this->exists = false;
  73. });
  74. }
  75. return $this->runSoftDelete();
  76. }
  77. /**
  78. * Perform the actual delete query on this model instance.
  79. *
  80. * @return void
  81. */
  82. protected function runSoftDelete()
  83. {
  84. $query = $this->setKeysForSaveQuery($this->newModelQuery());
  85. $time = $this->freshTimestamp();
  86. $columns = [$this->getDeletedAtColumn() => $this->fromDateTime($time)];
  87. $this->{$this->getDeletedAtColumn()} = $time;
  88. if ($this->usesTimestamps() && ! is_null($this->getUpdatedAtColumn())) {
  89. $this->{$this->getUpdatedAtColumn()} = $time;
  90. $columns[$this->getUpdatedAtColumn()] = $this->fromDateTime($time);
  91. }
  92. $query->update($columns);
  93. $this->syncOriginalAttributes(array_keys($columns));
  94. $this->fireModelEvent('trashed', false);
  95. }
  96. /**
  97. * Restore a soft-deleted model instance.
  98. *
  99. * @return bool
  100. */
  101. public function restore()
  102. {
  103. // If the restoring event does not return false, we will proceed with this
  104. // restore operation. Otherwise, we bail out so the developer will stop
  105. // the restore totally. We will clear the deleted timestamp and save.
  106. if ($this->fireModelEvent('restoring') === false) {
  107. return false;
  108. }
  109. $this->{$this->getDeletedAtColumn()} = null;
  110. // Once we have saved the model, we will fire the "restored" event so this
  111. // developer will do anything they need to after a restore operation is
  112. // totally finished. Then we will return the result of the save call.
  113. $this->exists = true;
  114. $result = $this->save();
  115. $this->fireModelEvent('restored', false);
  116. return $result;
  117. }
  118. /**
  119. * Restore a soft-deleted model instance without raising any events.
  120. *
  121. * @return bool
  122. */
  123. public function restoreQuietly()
  124. {
  125. return static::withoutEvents(fn () => $this->restore());
  126. }
  127. /**
  128. * Determine if the model instance has been soft-deleted.
  129. *
  130. * @return bool
  131. */
  132. public function trashed()
  133. {
  134. return ! is_null($this->{$this->getDeletedAtColumn()});
  135. }
  136. /**
  137. * Register a "softDeleted" model event callback with the dispatcher.
  138. *
  139. * @param \Illuminate\Events\QueuedClosure|\Closure|string $callback
  140. * @return void
  141. */
  142. public static function softDeleted($callback)
  143. {
  144. static::registerModelEvent('trashed', $callback);
  145. }
  146. /**
  147. * Register a "restoring" model event callback with the dispatcher.
  148. *
  149. * @param \Illuminate\Events\QueuedClosure|\Closure|string $callback
  150. * @return void
  151. */
  152. public static function restoring($callback)
  153. {
  154. static::registerModelEvent('restoring', $callback);
  155. }
  156. /**
  157. * Register a "restored" model event callback with the dispatcher.
  158. *
  159. * @param \Illuminate\Events\QueuedClosure|\Closure|string $callback
  160. * @return void
  161. */
  162. public static function restored($callback)
  163. {
  164. static::registerModelEvent('restored', $callback);
  165. }
  166. /**
  167. * Register a "forceDeleting" model event callback with the dispatcher.
  168. *
  169. * @param \Illuminate\Events\QueuedClosure|\Closure|string $callback
  170. * @return void
  171. */
  172. public static function forceDeleting($callback)
  173. {
  174. static::registerModelEvent('forceDeleting', $callback);
  175. }
  176. /**
  177. * Register a "forceDeleted" model event callback with the dispatcher.
  178. *
  179. * @param \Illuminate\Events\QueuedClosure|\Closure|string $callback
  180. * @return void
  181. */
  182. public static function forceDeleted($callback)
  183. {
  184. static::registerModelEvent('forceDeleted', $callback);
  185. }
  186. /**
  187. * Determine if the model is currently force deleting.
  188. *
  189. * @return bool
  190. */
  191. public function isForceDeleting()
  192. {
  193. return $this->forceDeleting;
  194. }
  195. /**
  196. * Get the name of the "deleted at" column.
  197. *
  198. * @return string
  199. */
  200. public function getDeletedAtColumn()
  201. {
  202. return defined(static::class.'::DELETED_AT') ? static::DELETED_AT : 'deleted_at';
  203. }
  204. /**
  205. * Get the fully qualified "deleted at" column.
  206. *
  207. * @return string
  208. */
  209. public function getQualifiedDeletedAtColumn()
  210. {
  211. return $this->qualifyColumn($this->getDeletedAtColumn());
  212. }
  213. }