Validating a Unique Slug on Update in Laravel 5
Asked Answered
Y

6

18

I currently have a model that has a text field and a slug field.

I validate that the slug is unique in my form request class:

public function rules()
{
    return [
        'name' => 'required|min:3',
        'slug' => 'required|alpha_dash|unique:questions'
    ];
}

This works fine on create and properly denies the creation of duplicate slugs. However on my update method, it won't let me save a record because the slug already exists. Of course the slug does exist, but it exists on the record being edited, so I would like to continue to allow it to be saved. However, it should not be able to be changed to a slug on ANOTHER record.

Here's what my update ArticlesController method looks like:

public function update(Article $article, ArticleRequest $request)
{
    $article->update($request->all());

    return redirect('articles');
}

Is there a way to make this work in L5?

Yarn answered 22/2, 2015 at 19:31 Comment(2)
I'm going to leave this open for now to see what other options exist, but I have solved this by removing my validation and using Eloquent Sluggable: github.com/cviebrock/eloquent-sluggableYarn
https://mcmap.net/q/741558/-laravel-validation-unique-exists-with-different-database-connectionCayes
S
9

Try to modify your rule like following(in form request class):

public function rules()
{
    return [
      'name'  => 'required,min:3',
      'slug'  => 'required|alpha_dash|unique:categories,slug,'.$this->id')
    ];
}

It works for me.

Seclusion answered 28/8, 2015 at 17:25 Comment(2)
does $this->id relies on the Request object having a parameter called id in the route? Like /whatever/{id}. If it was /whatever/{whatever_id} would I use $this->whatever_id ?Adora
@MarcoAurélioDeleu yes you would.Oilcan
R
4

In unique rule you may specify id you want to ignore.

You can create 2 separate request (one for create and one for update), but you can do it also this way checking if if is set(I assume your update url looks like /questions/2 ):

public function rules()
{
    $rules = [
        'name' => 'required|min:3',
        'slug' => ['required', 'alpha_dash']
    ];

    $rule = 'unique:questions';

    $segments = $this->segments();
    $id = intval(end($segments));
    if ($id != 0) {  
         $rule .= ',slug,' . $id;
    }
    $rules['slug'][] = $rule;

    return $rules;
    }
}
Ronnyronsard answered 22/2, 2015 at 21:16 Comment(1)
I think that my solution is a little cleaner! (but essentially the same)Foxglove
L
2

If you must have the ability to update a slug, projects I've worked on usually require it is not editable after creation, then you can use laravel's built in rule to ignore a certain record on the table by primary key.

$rules['slug'] = "required|unique:questions,slug,{$id}";

http://laravel.com/docs/5.0/validation see "Forcing a unique rule to ignore a given ID"

Landgravine answered 22/2, 2015 at 21:30 Comment(0)
P
2

In EditArticleRequest:

public function $rules () 
{
    $id = $this->id;

    return [
        'name' => 'required|min:3',
        'slug' => "required|alpha_dash|unique:articles,slug,$id",
    ];
}
Petropavlovsk answered 20/3, 2017 at 11:7 Comment(0)
F
1

As already mentioned you can use the ignore feature in the validator functionality.

Just reference the id of the item you wish to ignore and make sure that when you update you use a patch request!

See more info here! http://laravel.com/docs/5.0/validation#rule-unique

protected $rules = [    
    'name' => 'required|min:3',
    'slug' => 'required|alpha_dash|unique:questions'
];

public function rules()
{
    $rules = $this->rules;
    if ($this->isMethod('patch')) 
    {
        $id = $this->articles;
        $rules['slug'] = $rules['slug'].',slug,'.$id;
    }
    return $rules;
}
Foxglove answered 1/8, 2015 at 0:50 Comment(0)
R
1

Here is how I do it in Laravel 5.3 in details:

1- Create a new Form Request class by executing the next command in your terminal:

php artisan make:request ArticleFormRequest

Where ArticleFormRequest is the name of the form request class. This command will create a file called ArticleFormRequest.php in app/Http/Requests directory.

2- Open that created file and remove its content then place the next content in it:

<?php

namespace App\Http\Requests;

use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Http\Request;
use Illuminate\Validation\Rule;
use App\Article;

class ArticleFormRequest extends FormRequest
{
    protected $rules = [
        'name' => 'required|min:3',
        'slug' => 'required|alpha_dash|unique:articles,slug',
    ];

    // protected $user; // in case you want the current authenticated user
    protected $request_method;
    protected $id;

    public function __construct(Request $request)
    {
        // $request->user() returns an instance of the authenticated user
        // $this->user = $request->user(); // in case you want the current authenticated user

        // $request->method() returns method of the request (GET, POST, PUT, DELETE, ...)
        $this->request_method = strtoupper($request->method());

        // segments(): Returns an array containing all of the segments for the request path
        // it is important to assign the returned "segments" array to a variable first before using it, otherwise an error will occur
        $segments = $request->segments();
        // note this way will be valid only if "id" of the element is the last segment
        $this->id = end($segments);
    }

    /**
     * Determine if the user is authorized to make this request.
     *
     * @return bool
     */
    public function authorize()
    {
        return true;
    }

    /**
     * Get the validation rules that apply to the request.
     *
     * @return array
     */
    public function rules()
    {
        $rules = $this->rules;

        if ($this->request_method == "POST") {
            // do nothing..
        } elseif (in_array($this->request_method, ["PUT", "PATCH"])) {
            $article = Article::find($this->id);

            if ($article) {
                // forcing a unique rule to ignore a given id | https://laravel.com/docs/5.3/validation
                $rules["slug"] = [
                    "required",
                    "alpha_dash",
                    Rule::unique("articles", "slug")->ignore($article->id, "id"),
                ];

                // this is also can be used
                // $rules['slug'] = "required|alpha_dash|unique:articles,slug,$article->id,id";
            }
        }

        return $rules;
    }
}

3- In your controller, you can use that ArticleFormRequest in store() and update() methods like this:

<?php

namespace App\Http\Controllers;

use App\Http\Requests\ArticleFormRequest;

class ArticlesController extends Controller
{


    public function store(ArticleFormRequest $request)
    {
        // your code here..
    }

    public function update(ArticleFormRequest $request, $id)
    {
        // Your code here..
    }

}
Raney answered 21/12, 2016 at 14:14 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.