Search

How To Make Unique Validations Work With Soft Delete In Laravel

Listen to this article

The Laravel Eloquent ORM provides a beautiful, simple way for working with your database. Each database table needs to have a “Model” which is used to interact with that table. Models allow you to query for data in your tables, as well as insert new records into the table. You can also delete the data from these tables by using the delete() method.

Setting Up Soft Delete In Laravel:

In addition to actually removing records from your database, Eloquent can also “soft delete” models. When Soft Delete is set on a Migration, a deleted_at column is created in the database like so.

$table->softDeletes();

The above line will be added to the up() function of a migration. This function is run when the database is being migrated. The down() function is run when the migration is being rolled back.

How Soft Delete works In Laravel

When models are soft deleted, they are not actually removed from your database. Instead, a timestamp is set on the deleted_at column. If a model has a non-null deleted_at value, the model has been soft deleted.

use Illuminate\Database\Eloquent\SoftDeletes;
 
class Posts extends Model
{
    use SoftDeletes;
    public $timestamps = true;
    protected $fillable = [
        'display_name',
        'slug',
        'description',
        'meta_information_id',
        'created_by',
        'updated_by',
    ];
}

In the above snippet of code, Eloquent \ SoftDeletes will have to be imported and Used in the Posts Model.

The Issue

Laravel Request Validation has a unique validation which validates that the provided column does not already exist in the provided table. In the request, the ‘slug’ parameter will have to be unique as it will be used for URL of the Post.

The issue is that when you have a SoftDeleted post with slug=”abc” and you create a new post with slug=”abc”, the unique validation in the request will fail.

Even when the post has been deleted by using SoftDelete which is a part of Eloquent, this deleted post is not ignored by the Unique validation and will give an error stating that a Post with this slug already exists.

I faced this issue in two places.


Case I:

The first case was a regular Request file. Where I had specified an array of all the validations. In this, the deleted option was not being ignored while checking for unique.

use App\Http\Requests\Request;
 
class CreatePostRequest extends Request
{
    $id = $this->route('post');
    public function rules()
    {
        return [
	    'display_name' => 'required|min:3',
            'slug' => "required|unique:post,slug,{$id}",
            'description' => 'required',
            'meta_information_id' => 'required',
        ];
    }
}

It looked something like above.

Case II:

The second was a custom validation in which space was to be ignored while validating unique which was not possible through normal unique validation. In this function, I was modifying the slug to fit my needs and then validating unique.

Validator::extend('uniquenospace', function ($attribute, $value, $parameters) {
            $value = str_replace(' ', '', $value);
            $value = strtolower($value);
            $queries = DB::table($parameters[0])->selectRaw("id,lower(REPLACE({$parameters[1]}, ' ', '')) as {$parameters[1]}");
            for ($i = 3; $i < count($parameters); $i = $i + 2) {
                $queries->where($parameters[$i], $parameters[$i + 1]);
            }
            $queries = $queries->get();
            foreach ($queries as $query) {
                if ($value == $query->$parameters[1] && $query->id != $parameters[2]) {
                    return false;
                }
            }
 
            return true;
        }, 'The :attribute already exists.');

I was hard pressed to find a solution to this problem. I was at my wit’s end and was going to remove Soft Deletes altogether. I finally found a solution to this by passing a deleted_at as null along with other array data.

The Solution for Case I:

use App\Http\Requests\Request;
 
class CreatePostRequest extends Request
{
    $id = $this->route('post');
    public function rules()
    {
        return [
	    'display_name'  => 'required|min:3',
            'slug' => "required|unique:post,slug,{$id},id,deleted_at,NULL",
            'description' => 'required',
            'meta_information_id' => ‘required’,
        ];
    }
}

In a normal Request file in Laravel, we will need to add another parameter to the request unique. This will check if a value was unique in the pool when ‘deleted_at’ was null thus ignoring soft- deleted sets of data.

The Solution for Case II:

Validator::extend('uniquenospace', function ($attribute, $value, $parameters) {
            $value = str_replace(' ', '', $value);
            $value = strtolower($value);
            $queries = DB::table($parameters[0])->selectRaw("id,lower(REPLACE({$parameters[1]}, ' ', '')) as {$parameters[1]}")->where('deleted_at', null);
            for ($i = 3; $i < count($parameters); $i = $i + 2) {
                $queries->where($parameters[$i], $parameters[$i + 1]);
            }
            $queries = $queries->get();
            foreach ($queries as $query) {
                if ($value == $query->$parameters[1] && $query->id != $parameters[2]) {
                    return false;
                }
            }
            return true;
        }, 'The :attribute already exists.');
 

For custom validation, we are getting all records and comparing them with the current slug in the request. Here we can directly select only those rows where deleted_at = NULL so as to filter out deleted results.

If one wants to keep such a filtering optional, then it can be passed to the “uniquenospace” custom validation and handled in the validation. It would work just like the normal validation. If deleted_at, NULL is passed then only the deleted rows will be ignored.

Varun Shanbhag

Varun Shanbhag

3 Responses

  1. I had the same problem and solution for case I solved it. Thank you very much for sharing the solutions…

Leave a Reply

Your email address will not be published. Required fields are marked *

Get The Latest Updates

Subscribe to our Newsletter

A key to unlock the world of open-source. We promise not to spam your inbox.

Suggested Reads

WordPress Development

Custom WordPress Solutions, for You

LearnDash Development

Custom LearnDash Solutions, for You

WooCommerce Development

Custom WooCommerce Solutions, for You

WordPress Plugins

Scale your WordPress Business

WooCommerce Plugins

Scale your WooCommerce Business

LearnDash Plugins

Scale your LearnDash Business

Label

Title

Subtext

Label

Title

Subtext

Join our 55,000+ Subscribers

    The Wisdm Digest delivers all the latest news, and resources from the world of open-source businesses to your inbox.

    Suggested Reads

    WordPress Tips & Tricks
    Ketan Vyawahare

    How to Make Responsive Tables using CSS without Tag Read More »