withTrashed(bool $withTrashed = true) * @method static \Illuminate\Database\Eloquent\Builder onlyTrashed() * @method static \Illuminate\Database\Eloquent\Builder withoutTrashed() * @method static static restoreOrCreate(array $attributes = [], array $values = []) * @method static static createOrRestore(array $attributes = [], array $values = []) */ trait SoftDeletes { /** * Indicates if the model is currently force deleting. * * @var bool */ protected $forceDeleting = false; /** * Boot the soft deleting trait for a model. * * @return void */ public static function bootSoftDeletes() { static::addGlobalScope(new SoftDeletingScope); } /** * Initialize the soft deleting trait for an instance. * * @return void */ public function initializeSoftDeletes() { if (! isset($this->casts[$this->getDeletedAtColumn()])) { $this->casts[$this->getDeletedAtColumn()] = 'datetime'; } } /** * Force a hard delete on a soft deleted model. * * @return bool|null */ public function forceDelete() { if ($this->fireModelEvent('forceDeleting') === false) { return false; } $this->forceDeleting = true; return tap($this->delete(), function ($deleted) { $this->forceDeleting = false; if ($deleted) { $this->fireModelEvent('forceDeleted', false); } }); } /** * Force a hard delete on a soft deleted model without raising any events. * * @return bool|null */ public function forceDeleteQuietly() { return static::withoutEvents(fn () => $this->forceDelete()); } /** * Destroy the models for the given IDs. * * @param \Illuminate\Support\Collection|array|int|string $ids * @return int */ public static function forceDestroy($ids) { if ($ids instanceof EloquentCollection) { $ids = $ids->modelKeys(); } if ($ids instanceof BaseCollection) { $ids = $ids->all(); } $ids = is_array($ids) ? $ids : func_get_args(); if (count($ids) === 0) { return 0; } // We will actually pull the models from the database table and call delete on // each of them individually so that their events get fired properly with a // correct set of attributes in case the developers wants to check these. $key = ($instance = new static)->getKeyName(); $count = 0; foreach ($instance->withTrashed()->whereIn($key, $ids)->get() as $model) { if ($model->forceDelete()) { $count++; } } return $count; } /** * Perform the actual delete query on this model instance. * * @return mixed */ protected function performDeleteOnModel() { if ($this->forceDeleting) { return tap($this->setKeysForSaveQuery($this->newModelQuery())->forceDelete(), function () { $this->exists = false; }); } return $this->runSoftDelete(); } /** * Perform the actual delete query on this model instance. * * @return void */ protected function runSoftDelete() { $query = $this->setKeysForSaveQuery($this->newModelQuery()); $time = $this->freshTimestamp(); $columns = [$this->getDeletedAtColumn() => $this->fromDateTime($time)]; $this->{$this->getDeletedAtColumn()} = $time; if ($this->usesTimestamps() && ! is_null($this->getUpdatedAtColumn())) { $this->{$this->getUpdatedAtColumn()} = $time; $columns[$this->getUpdatedAtColumn()] = $this->fromDateTime($time); } $query->update($columns); $this->syncOriginalAttributes(array_keys($columns)); $this->fireModelEvent('trashed', false); } /** * Restore a soft-deleted model instance. * * @return bool */ public function restore() { // If the restoring event does not return false, we will proceed with this // restore operation. Otherwise, we bail out so the developer will stop // the restore totally. We will clear the deleted timestamp and save. if ($this->fireModelEvent('restoring') === false) { return false; } $this->{$this->getDeletedAtColumn()} = null; // Once we have saved the model, we will fire the "restored" event so this // developer will do anything they need to after a restore operation is // totally finished. Then we will return the result of the save call. $this->exists = true; $result = $this->save(); $this->fireModelEvent('restored', false); return $result; } /** * Restore a soft-deleted model instance without raising any events. * * @return bool */ public function restoreQuietly() { return static::withoutEvents(fn () => $this->restore()); } /** * Determine if the model instance has been soft-deleted. * * @return bool */ public function trashed() { return ! is_null($this->{$this->getDeletedAtColumn()}); } /** * Register a "softDeleted" model event callback with the dispatcher. * * @param \Illuminate\Events\QueuedClosure|callable|class-string $callback * @return void */ public static function softDeleted($callback) { static::registerModelEvent('trashed', $callback); } /** * Register a "restoring" model event callback with the dispatcher. * * @param \Illuminate\Events\QueuedClosure|callable|class-string $callback * @return void */ public static function restoring($callback) { static::registerModelEvent('restoring', $callback); } /** * Register a "restored" model event callback with the dispatcher. * * @param \Illuminate\Events\QueuedClosure|callable|class-string $callback * @return void */ public static function restored($callback) { static::registerModelEvent('restored', $callback); } /** * Register a "forceDeleting" model event callback with the dispatcher. * * @param \Illuminate\Events\QueuedClosure|callable|class-string $callback * @return void */ public static function forceDeleting($callback) { static::registerModelEvent('forceDeleting', $callback); } /** * Register a "forceDeleted" model event callback with the dispatcher. * * @param \Illuminate\Events\QueuedClosure|callable|class-string $callback * @return void */ public static function forceDeleted($callback) { static::registerModelEvent('forceDeleted', $callback); } /** * Determine if the model is currently force deleting. * * @return bool */ public function isForceDeleting() { return $this->forceDeleting; } /** * Get the name of the "deleted at" column. * * @return string */ public function getDeletedAtColumn() { return defined(static::class.'::DELETED_AT') ? static::DELETED_AT : 'deleted_at'; } /** * Get the fully qualified "deleted at" column. * * @return string */ public function getQualifiedDeletedAtColumn() { return $this->qualifyColumn($this->getDeletedAtColumn()); } }