Laravel:如何正确重写SoftDeletes trait实现带状态更新的软删除
Great question! Copying the entire source code of runSoftDelete and restore from Laravel's SoftDeletes trait is not the optimal approach—here's why, plus a cleaner, more maintainable alternative:
Why Copying Full Methods Is a Bad Idea
- Maintenance overhead: If Laravel updates the underlying logic of
SoftDeletes(e.g., adding new timestamp handling, optimizing queries), your copied code will become outdated and potentially introduce bugs or inconsistencies. - Violates DRY (Don't Repeat Yourself): You're duplicating code that already exists in the framework, which increases the chance of human error when making changes.
- Harder to debug: Any issues with your soft delete/restore flow will require checking both your custom trait and Laravel's source, rather than just your added logic.
Better Approach: Override Methods Strategically
Instead of copying full methods, override only the parts you need to modify, and leverage parent class logic wherever possible. Here's how to implement your SoftDeletesWithStatus trait properly:
1. Sync Status on Soft Delete
The runSoftDelete method is protected and handles direct database updates for soft deletes. Since it doesn’t use the model’s save() method (which auto-includes modified attributes), you’ll need to adjust the update column array to include your status field:
use Illuminate\Database\Eloquent\SoftDeletes; trait SoftDeletesWithStatus { use SoftDeletes; /** * Perform the actual delete query on this model instance. * * @return void */ protected function runSoftDelete() { $query = $this->newModelQuery()->where($this->getKeyName(), $this->getKey()); $time = $this->freshTimestamp(); $statusValue = 'deleted'; // Replace with your desired status value // Build update columns (mirroring Laravel's core logic) $columns = [ $this->getDeletedAtColumn() => $this->fromDateTime($time), 'status' => $statusValue // Add your status sync here ]; // Include updated_at if timestamps are enabled if ($this->timestamps && !is_null($this->getUpdatedAtColumn())) { $this->{$this->getUpdatedAtColumn()} = $time; $columns[$this->getUpdatedAtColumn()] = $this->fromDateTime($time); } // Execute combined update for atomicity $query->update($columns); // Update model instance attributes to match database state $this->{$this->getDeletedAtColumn()} = $time; $this->status = $statusValue; $this->fireModelEvent('deleted', false); }
2. Sync Status on Restore
The restore method uses the model’s save() method, which simplifies this step—just set the status attribute before calling the parent method, and it will be saved automatically:
/** * Restore a soft-deleted model instance. * * @return bool|null */ public function restore() { // Trigger restoring event first (matches Laravel's default flow) if ($this->fireModelEvent('restoring') === false) { return false; } // Set your desired status for restored records $this->status = 'active'; // Replace with your desired status value // Let Laravel handle the rest of the restore logic $result = parent::restore(); return $result; } }
Key Tips for Long-Term Maintainability
- Comment clearly: Note where you’ve modified Laravel’s default logic, so you can easily compare against future framework updates.
- Use constants: Define status values (e.g.,
const STATUS_DELETED = 'deleted') in the trait or model to avoid hardcoding. - Test thoroughly: Verify that both soft delete and restore operations correctly update the
statusfield while preserving all default Laravel soft delete behavior (e.g., timestamps, events).
内容的提问来源于stack exchange,提问作者JacopoStanchi

