pmiranda
pmiranda

Reputation: 8470

Laravel, Can't update a soft-deleted value

I have this values in my db:

id - name - created_at - updated_at - deleted_at
------------------------------------------------
1  - John - 2018-11-11 - 2018-11-11 -  (NULL)
2  - John - 2018-11-11 - 2018-11-11 -  2018-11-11

If I search for "John" with my Datatable (Yajra) I only see the John with id=1 because I'm using softdeletes. My model is this:

namespace App\Models;

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

class MyModel extends Model
{
    use SoftDeletes;

    protected $fillable = ['name'];

    protected $dates = ['deleted_at'];
}

When I delete (destroy) a registry it puts a date at deleted_at which is correct. But when I want to edit (update) John the Validator is giving me the error that that value is already in use. My update method is this:

public function update(Request $request, $id)
{
    $rules = array(
        'name' => 'unique:my_table'
    );

    $validator = Validator::make($request->all(), $rules);

    if ($validator->passes()) {
        MyModel::find($id)->update($request->input());
        return redirect()->route('myroute')->withFlashSuccess('Ok!');
    } else {
        return redirect()->back()->withInput()->withErrors($validator);
    }
}

What I'm doing wrong?

Upvotes: 1

Views: 2153

Answers (2)

Namoshek
Namoshek

Reputation: 6544

The problem has nothing to do with the SoftDeletes, it is a validation issue. The unique validation rule is very special, because in case of an update it needs to know which entry it may ignore when performing the validation. In the background, the rule is performing an SQL query like

IF EXISTS (
    SELECT id
    FROM   my_table
    WHERE  name = 'some value'
) 
    SELECT  1
ELSE 
    SELECT  0

(it may not be the exact query, but similar).

As you can see, the query does not take into account if you perform an update or not. Because your entity already exists, it will return 1 and therefore fail the validation because it thinks that the value under validation is not unique.

But there is actually a way to make the validation work for updates. You simply have to add the id of the existing entity (which is being validated) as third parameter to the validation rule. So your rules should look something like this:

$rules = [
    'name' => 'unique:my_table,name,'.$id
];

Please be aware that there is also a second parameter for the unique validation rule - the column of the database table you want to search in.


Edit:

In case the unique constraint does only relate to not deleted entries, meaning that a unique value may be reused if other occurences of the same value are flagged as deleted, then it may be necessary to add an additional where() clause to the unique validation rule. For this, the fourth parameter needs to be set to the identifier column name and then we can add the additional where clauses as pairs of two parameters.

$rules = [
    'name' => 'unique:my_table,name,'.$id.',id,deleted_at,NULL'
];

This will add where('deleted_at', 'NULL') (or whereNull('deleted_at')) to the query.

Upvotes: 1

nlyn
nlyn

Reputation: 606

This person has written a blog post that looks like it would solve your problem:

https://wisdmlabs.com/blog/laravel-soft-delete-unique-validations/

I'm not 100% sure you want to do this though as you may later wish to use the restore() method to bring the soft-deleted data back. At which point, you'll have a collision.

Upvotes: 2

Related Questions