关于软删除问题

需求:软删除时,记录一下删除的操作用户是谁,
所以设计数据表的时候,增加字段 deleted_by , deleted_at
发现执行 Book::destroy(1); 只修改了deleted_at。这时候我想增加一个模型事件

    public static function boot()
    {
        parent::boot();
        static::deleting(function ($model) {
                $model->deleted_by = Auth::id();
        });
    }

发现不生效。
这时候,我手动建了一个Traits。其他代码一样,只是runSoftDelete加一行代码.. 然后我的模型use App\Traits\SoftDeletes;(我自定义的)

<?php

namespace App\Traits;

use App\Repositories\AuthRepository;
use Illuminate\Database\Eloquent\SoftDeletingScope;

/**
 * 自定义软删除,增加一个删除用户ID.
 */
trait SoftDeletes
{
    /**
     * Indicates if the model is currently force deleting.
     *
     * @var bool
     */
    protected $forceDeleting = false;

    /**
     * Boot the soft deleting trait for a model.
     */
    public static function bootSoftDeletes()
    {
        static::addGlobalScope(new SoftDeletingScope());
    }

    /**
     * Initialize the soft deleting trait for an instance.
     */
    public function initializeSoftDeletes()
    {
        $this->dates[] = $this->getDeletedAtColumn();
    }

    /**
     * Force a hard delete on a soft deleted model.
     *
     * @return bool|null
     */
    public function forceDelete()
    {
        $this->forceDeleting = true;

        return tap($this->delete(), function ($deleted) {
            $this->forceDeleting = false;

            if ($deleted) {
                $this->fireModelEvent('forceDeleted', false);
            }
        });
    }

    /**
     * Perform the actual delete query on this model instance.
     *
     * @return mixed
     */
    protected function performDeleteOnModel()
    {
        if ($this->forceDeleting) {
            $this->exists = false;

            return $this->newModelQuery()->where($this->getKeyName(), $this->getKey())->forceDelete();
        }

        return $this->runSoftDelete();
    }

    /**
     * Perform the actual delete query on this model instance.
     */
    protected function runSoftDelete()
    {
        $query = $this->newModelQuery()->where($this->getKeyName(), $this->getKey());

        $time = $this->freshTimestamp();

        $columns = [$this->getDeletedAtColumn() => $this->fromDateTime($time)];

        $this->{$this->getDeletedAtColumn()} = $time;

        if ($this->timestamps && !is_null($this->getUpdatedAtColumn())) {
            $this->{$this->getUpdatedAtColumn()} = $time;

            $columns[$this->getUpdatedAtColumn()] = $this->fromDateTime($time);
        }

        /**
         * 删除时候,增加用户ID.
         */
        $columns['deleted_by'] = Auth::id();

        $query->update($columns);
    }

    /**
     * Restore a soft-deleted model instance.
     *
     * @return bool|null
     */
    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;
    }

    /**
     * Determine if the model instance has been soft-deleted.
     *
     * @return bool
     */
    public function trashed()
    {
        return !is_null($this->{$this->getDeletedAtColumn()});
    }

    /**
     * Register a restoring model event with the dispatcher.
     *
     * @param \Closure|string $callback
     */
    public static function restoring($callback)
    {
        static::registerModelEvent('restoring', $callback);
    }

    /**
     * Register a restored model event with the dispatcher.
     *
     * @param \Closure|string $callback
     */
    public static function restored($callback)
    {
        static::registerModelEvent('restored', $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::DELETED_AT') ? static::DELETED_AT : 'deleted_at';
    }

    /**
     * Get the fully qualified "deleted at" column.
     *
     * @return string
     */
    public function getQualifiedDeletedAtColumn()
    {
        return $this->qualifyColumn($this->getDeletedAtColumn());
    }
}

有没有好一点的方法?

《L04 微信小程序从零到发布》
从小程序个人账户申请开始,带你一步步进行开发一个微信小程序,直到提交微信控制台上线发布。
《G01 Go 实战入门》
从零开始带你一步步开发一个 Go 博客项目,让你在最短的时间内学会使用 Go 进行编码。项目结构很大程度上参考了 Laravel。
讨论数量: 7

关于 Laravel 的的软删除确实有些瑕疵,有时候也不是很方便。
像这个也只能自定义SoftDeletes 来处理了,但我想提示两点

  1. 可以复用 Illuminate\Database\Eloquent\SoftDeletes 这个 trait
  2. 解决你自定义SoftDeletes后部分关联模型的软删除字段处理失效的问题

如在 Illuminate\Database\Eloquent\Relations\HasManyThrough中会判断这个trait

<?php

namespace Illuminate\Database\Eloquent\Relations;

...
use Illuminate\Database\Eloquent\SoftDeletes;

class HasManyThrough extends Relation
{
     /**
     * Determine whether "through" parent of the relation uses Soft Deletes.
     *
     * @return bool
     */
    public function throughParentSoftDeletes()
    {
        return in_array(SoftDeletes::class, class_uses_recursive($this->throughParent));
    }
}

代码如下

<?php
namespace App\Traits;

use Illuminate\Database\Eloquent\SoftDeletes as OriginSoftDeletes;

/**
 * 自定义软删除,增加一个删除用户ID.
 */
trait SoftDeletes
{
    use OriginSoftDeletes;

    /**
     * Perform the actual delete query on this model instance.
     */
    protected function runSoftDelete()
    {
        $query = $this->newModelQuery()->where($this->getKeyName(), $this->getKey());

        $time = $this->freshTimestamp();

        $columns = [$this->getDeletedAtColumn() => $this->fromDateTime($time)];

        $this->{$this->getDeletedAtColumn()} = $time;

        if ($this->timestamps && !is_null($this->getUpdatedAtColumn())) {
                $this->{$this->getUpdatedAtColumn()} = $time;

            $columns[$this->getUpdatedAtColumn()] = $this->fromDateTime($time);
        }

        //删除时候,增加用户ID. 也可以在 Model 自定义模型事件来处理这个或更多字段
        $columns['deleted_by'] = Auth::id();

        $query->update($columns);
    }
}
5年前 评论

是否可以在 updating 事件中,尝试使用 isDirty 方法,来判断当前是否是软删除操作?

5年前 评论

deleting替换成deleted试试?

4年前 评论
幽弥狂

为啥不用Observer 呢?
示例地址

4年前 评论

改下 SoftDeletes 里的 runSoftDelete 方法即可:

    protected function runSoftDelete()
    {
        $query = $this->setKeysForSaveQuery($this->newModelQuery());

        $time = $this->freshTimestamp();

        $columns = $this->getDirty();    // 把这之前已更新的字段取过来
        $columns[$this->getDeletedAtColumn()] = $this->fromDateTime($time);

        $this->{$this->getDeletedAtColumn()} = $time;

        if ($this->timestamps && ! is_null($this->getUpdatedAtColumn())) {
            $this->{$this->getUpdatedAtColumn()} = $time;

            $columns[$this->getUpdatedAtColumn()] = $this->fromDateTime($time);
        }

        $query->update($columns);
    }
class UserDeleting
{
    use Dispatchable, InteractsWithSockets, SerializesModels;

    /**
     * Create a new event instance.
     *
     * @return void
     */
    public function __construct(&$object)
    {
        $object->deleted_by = Auth::id();
    }
}
4年前 评论

请问下作者,我使用 Laravel 6runSoftDelete 增加参数没有作用

3年前 评论

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!