为什么软删除的实体会出现在查询结果中?

37

我正在尝试实现软删除的概念。

这是我的对象:

class Post extends Eloquent {

    /**
     * The database table used by the model.
     *
     * @var string
     */
    protected $table = 'posts';
    protected $softDelete = true;

    ...

启用软删除功能。

现在,如果我“删除”一篇文章,它会得到一个“deleted_at”时间戳:

description

问题是,当我搜索或只使用all()来显示文章时,软删除的项目也会出现。出了什么问题?


2
你能够发布删除对象的代码以及请求他们在页面上显示的位置的代码吗? - msturdy
3
谢谢,我已经解决了这个问题...我之前使用的是 Fluent 查询而不是 Eloquent,这就是出现这种情况的原因。 - Sergey Sob
对于未来的访问者:如果您正在使用4.2,请阅读此内容 - AturSams
7个回答

58
有时,即使使用 Eloquent 和 protected $softDelete = true;,您仍然会通过get()获取soft deleted 表条目。
因此,为避免这个问题,请使用
...->whereNull('deleted_at')->get();

例如,此查询将检索所有包括软删除的行。
DB::table('pages')->select('id','title', 'slug')
                                   ->where('is_navigation','=','yes')
                                   ->where('parent_id','=',$parent_id)
                                   ->orderBy('page_order')
                                   ->get();

所以正确的方法是:

DB::table('pages')->select('id','title', 'slug')
                                   ->where('is_navigation','=','yes')
                                   ->where('parent_id','=',$parent_id)
                                   ->whereNull('deleted_at')
                                   ->orderBy('page_order')
                                   ->get();

37
软删除功能是在使用Eloquent时生效。如果您使用查询构建器查询结果,您最终会看到所有已删除和未删除的记录。

在Laravel 4当前文档中并不清楚,但是既然软删除的概念只出现在Eloquent ORM - 软删除而不是查询构建器下面,我们只能假设:软删除仅与Eloquent ORM兼容


我最初认为我的开发人员正在使用Eloquent,因为查询首先提到了模型名称,就像Laravel的Eloquent区域文档中所示:SomeModel:join('table2', 'column1', 'column2'),而QueryBuilder文档中的查询则专门使用DB::table,而我们没有使用。但是,如果您使用这种语法进行连接,则仍将包括软删除数据,因此我不得不在我的查询中痛苦地添加->whereNull('table1.deleted_at')->whereNull('table2.deleted_at')(我希望有一个替代SoftDeletes插件可以将其放入单独的归档表/架构中)。 - armyofda12mnkeys
你必须使用 ->with() 来优雅地进行联接。 - Aditya Pratama

36

在Laravel中有一个小技巧,可以使用软删除表和查询:

当我们创建类似下面这样的内容时:

$objCars = Car::where("color","blue");

系统执行类似这样的操作:

SELECT
  *
FROM
  cars
WHERE
  deleted_at IS NULL
AND
  "color" = 'blue'

目前为止,一切都很好。但是,当我们使用"orWhere"方法时,会发生一些有趣的事情。

$objCars = Car::where("color","blue")->orWhere("color","red");
系统将执行类似以下内容的操作:
SELECT 
  * 
FROM
  cars
WHERE
  deleted_at IS NULL 
AND 
  "color" = 'blue'
OR
  "color" = 'red'

这个新查询将返回所有 deleted_at 字段为 null 并且颜色为蓝色的汽车,或者颜色为红色即使 deleted_at 字段不为 null。它与另一个查询的行为相同,该查询更明确地显示了问题:

SELECT 
  * 
FROM
  cars
WHERE
  (
    deleted_at IS NULL 
  AND 
    "color" = 'blue'
  )
OR
  "color" = 'red'
为了解决这个问题,您应该更改传递闭包的“where”方法。像这样:
$objCars = Car::where(
  function ( $query ) {
    $query->where("color","blue");
    $query->orWhere("color","red");
  }
);

然后,系统将执行类似于以下内容的操作:

SELECT
  *
FROM
  cars
WHERE
  deleted_at IS NULL
AND
  (
    "color" = 'blue' 
  OR
    "color" = 'red'
  )

这个最后的查询会查找所有被删除时间为null并且颜色可以是红色或蓝色的汽车,正如我们希望它做的那样。


1
这是一个更好、更全面的答案。 - Bhargav Nanekalva
有人知道在Laravel的后续版本中这是否仍然正确吗? - waterloomatt
在我看来,使用whereIn('color', ['red', 'blue'])会更好。 - Aditya Pratama

8
我曾经遇到同样的问题,但这里提供的方法都没有帮助我解决。
我的问题出在构造函数中,我忘记调用父类的构造函数:
public function __construct()
{
   parent::__construct();

   //Rest of my code
}

希望这对某些人有所帮助!

2

Laravel 5.2.44新增了withoutTrashed()方法到SoftDeletingScope。例如,您可以使用类似以下代码:

Post::withoutTrashed()->get();

1

我在 Laravel 5.6 中测试过,您需要在模型中使用 SoftDeletes Trait。

use Illuminate\Database\Eloquent\SoftDeletes;

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;

class Banners extends Model
{
    use SoftDeletes;
    //no need of this below line
    //protected $softDelete = true;
}

当您发起查询时

$banners = Banners::where('status', 1)->get();

它不会返回已经软删除的数据。


0

我在Laravel 8中使用Eloquent和软删除解决了联接表的问题。

$query = Model_table_1::join('user', 'user.id', '=', 'table_1.user_id')
->join('table_2', 'table_2.id', '=', 'table_1.table2_id')
->join('table_3', 'table_3.id', '=', 'table_1.table3_id')
->join('table_4', 'table_4.id', '=', 'table_3.table4_id')
->select('table_1.id','table_1.name','table_1.created_at',
'user.name','table_2.name2','table_3.name3','table_4.name4')
->get();

在数据表中使用 where

$query = Model_table_1::join('user', 'user.id', '=', 'table_1.user_id')
->join('table_2', 'table_2.id', '=', 'table_1.table2_id')
->join('table_3', 'table_3.id', '=', 'table_1.table3_id')
->join('table_4', 'table_4.id', '=', 'table_3.table4_id')
->select('table_1.id','table_1.name','table_1.created_at','user.name','table_2.name2','table_3.name3','table_4.name4')
->where('table_1.user_id', '=', Auth::user()->id)
->get();

在视图中使用where语句

$query = Model_table_1::join('user', 'user.id', '=', 'table_1.user_id')
->join('table_2', 'table_2.id', '=', 'table_1.table2_id')
->join('table_3', 'table_3.id', '=', 'table_1.table3_id')
->join('table_4', 'table_4.id', '=', 'table_3.table4_id')
->select('table_1.id','table_1.name','table_1.created_at','user.name','table_2.name2','table_3.name3','table_4.name4')
->where('table_1.user_id', '=', Auth::user()->id)
->first();

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接