I am using a scenario where every other model inherits from the BaseModel. In BaseModel, I am listening to the following events:
public static function boot()
{
parent::boot();
self::creating(array('BaseModel', 'creatingCreatedBy'));
self::updating(array('BaseModel', 'updatingUpdatedBy'));
self::deleting(array('BaseModel', 'deletingDeletedBy'));
}
public static function creatingCreatedBy($model)
{
$model->created_by = App::make('user')->id;
}
public static function updatingUpdatedBy($model)
{
$model->updated_by = App::make('user')->id;
}
public static function deletingDeletedBy($model)
{
$model->deleted_by = App::make('user')->id;
$model->save();
}
However, only "deleting" method does not allow me to set any model attributes before the model is soft-deleted, unless I explicitly save it. This is inconsistent with other "_ing" events. A fix should be created that if the model's $softDelete is set to true, model is also updated before and after soft-deleting.
I think the behaviour is probably fine the way it is. No sense in adding an additional update when you can just do what you have done if you want to update a field.
But then it is inconsistent with 'updating' and 'deleting' methods, I know that update or insert is 'imminent' in those cases, but soft delete is just an update too.
Feel free to make PR on this repository.
Used my own softdeletes trait to fix this small 'bug'.
I needed to updated a field to known it needed to be synced.
Note: It will fire the save() and update() events as well.
trait SoftDeletes
{
use \Illuminate\Database\Eloquent\SoftDeletes;
/**
* @see https://github.com/laravel/framework/issues/4990
*/
protected function runSoftDelete()
{
$this->{$this->getDeletedAtColumn()} = $this->freshTimestamp();
$this->save();
}
}
Calling $model->save(); in deleting event will always trigger saving event. Here is a borrowed from SoftDeletes trait update logic which was added to a trait that logs deletedBy user id:
static::deleting(function($item) {
if ($item->softDeletingEnabled()) {
$deletedByColumn = $item->deletedBy()->getForeignKey();
$updatedByColumn = $item->updatedBy()->getForeignKey();
$user = Auth::guard('api')->user();
$query = $item->newQueryWithoutScopes()->where($item->getKeyName(), $item->getKey());
$columns = [$deletedByColumn => $user->id];
if (is_null($item->{$updatedByColumn})) {
$columns[$updatedByColumn] = $user->id;
}
$query->update($columns);
}
});
This implementation doesn't trigger saving event and logs updatedBy user id only when updated_by field is null.
Thank you @maqduni !! That is just what I needed. For anyone else looking for a similar solution I added a custom Trait.
<?php
namespace Traits;
use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Support\Facades\Auth;
trait SoftDeletesWithUser{
use SoftDeletes;
/**
* Perform the actual delete query on this model instance.
*
* @return void
*/
protected function runSoftDelete()
{
$query = $this->newQueryWithoutScopes()->where($this->getKeyName(), $this->getKey());
$time = $this->freshTimestamp();
$columns = [$this->getDeletedAtColumn() => $this->fromDateTime($time)];
$this->{$this->getDeletedAtColumn()} = $time;
if ($this->timestamps) {
$this->{$this->getUpdatedAtColumn()} = $time;
$columns[$this->getUpdatedAtColumn()] = $this->fromDateTime($time);
}
if($user = Auth::user()){
$columns['deleted_by'] = $user->id;
}
$query->update($columns);
}
}
I'm keeping up with tradition and keeping this thread up to date. Using the same concept as @mycarrysun . This trait inherits the parent SoftDeletes trait. But this function will grab ANY changes that occur within the observer. All you have to do is use it in place of SoftDeletes trait calls.
so in any model you want to use soft deletes with, instead of inheriting SoftDeletes, you would inherit SoftDeletesTrait. I would put it in a directory called app\Traits, but that's your call.
Edit: I put a gist together here https://gist.github.com/casperwilkes/10a2bee6b9e94dd4a663d7cb0e4ae3ff
But the meat is in merging the changes from the model into the columns of the update:
///
protected function runSoftDelete(): void {
///
$columns[$this->getUpdatedAtColumn()] = $this->fromDateTime($time);
}
/**
* Add changes from observer here,
* overrides $columns, but leaves timestamps in tact
*/
$columns = array_merge($query->getModel()->getDirty(), $columns);
///
$query->update($columns);
}
Most helpful comment
Thank you @maqduni !! That is just what I needed. For anyone else looking for a similar solution I added a custom Trait.