74

I have 2 columns in table servers.

I have columns ip and hostname.

I have validation:

'data.ip' => ['required', 'unique:servers,ip,'.$this->id]

This working only for column ip. But how to do that it would work and for column hostname?

I want validate data.ip with columns ip and hostname. Because can be duplicates in columns ip and hostname, when user write ip.

Dumitru
  • 2,053
  • 6
  • 20
  • 45
  • do u need to valiate combinely unique for ip and hostname? – arun May 15 '18 at 12:14
  • @arun Yes. I have 1 form for ip. When user write ip, I need validate this ip in column ip and in column hostname.. if exists – Dumitru May 15 '18 at 12:15
  • there is no inbuilt validation like what u wanted. u may extend it, see https://stackoverflow.com/questions/50095328/how-to-create-custom-validation-rule-for-4-combinedly-unique-columns/50096190#50096190 – arun May 15 '18 at 12:17
  • @arun, But I can do this: `'data.ip' => ['required', 'unique:servers,ip,'.$this->id, 'unique:servers,hostname']` ? – Dumitru May 15 '18 at 12:22
  • Do another field validation seperately, `'data.hostname' => ['required', 'unique:servers,hostname']` – arun May 15 '18 at 12:25
  • @arun no, I don't have input hostname. I need validate ip input in two columns – Dumitru May 15 '18 at 12:30
  • Try this one https://stackoverflow.com/questions/33855787/laravel-validation-unique-on-two-columns – JYoThI May 15 '18 at 12:33
  • Do you want check separately or combined ? Update your question with all logic ? – Niklesh Raut May 15 '18 at 12:34
  • @C2486 updated question.. – Dumitru May 15 '18 at 12:36
  • 2
    I just have a question about how you fixed the problem, in the answer you checked you have the line Rule::unique('servers')->where(function ($query) use($ip,$hostname) and i wonder if you defined these variables ($ip,$hostname) somewhere before using them? – Red Eyez Feb 18 '19 at 15:08
  • Referenece Link working on 5.5 https://laravel.io/forum/11-03-2015-laravel-unique-validation-on-multiple-columns-for-unique-relations – Nirav Bhoi Jul 21 '20 at 09:56
  • 'ip' => 'required|unique:servers,ip,NULL,id,deleted_at,NULL,another_column,'.$this->value1.',another_column,'.$this->value2 – Bhagyaraj Dec 06 '21 at 07:38
  • @RedEyez For new visitors, you can get them from the request itself (`$this->ip`) instead of using `use($ip,$hostname)`. – Wesley Gonçalves Mar 11 '22 at 20:31

14 Answers14

105

You can use Rule::unique to achieve your validation rule

$messages = [
    'data.ip.unique' => 'Given ip and hostname are not unique',
];

Validator::make($data, [
    'data.ip' => [
        'required',
        Rule::unique('servers')->where(function ($query) use($ip,$hostname) {
            return $query->where('ip', $ip)
            ->where('hostname', $hostname);
        }),
    ],
],
$messages
);

edit: Fixed message assignation

reeves
  • 173
  • 1
  • 1
  • 11
Niklesh Raut
  • 34,013
  • 16
  • 75
  • 109
  • absolutely perfect solution, but need to also update operation. – Razib Al Mamun Jan 02 '19 at 10:57
  • 1
    i tried something similar but I got undefined variable when using " use($variable1, $variable2) ". do i have to define those somewhere? – Red Eyez Feb 18 '19 at 15:02
  • Use it this way: ` $text = $this->request->text; $branch_id = $this->request->branch_id; $validator = \Validator::make($this->request->all(), array( 'text' => [ 'required', Rule::unique('cpl_tax')->where(function ($query) use($text, $branch_id) { return $query->where('text', $text) ->where('branch_id', $branch_id); }), ], )); ` @RedEyez – maruf najm Aug 22 '19 at 07:05
  • 2
    @RazibAlMamun For update use this `$text = $this->request->text; $branch_id = $this->request->branch_id; $tax_id = $this->request->tax_id; $validator = \Validator::make($this->request->all(), array( 'text' => [ 'required', Rule::unique('cpl_tax')->where(function ($query) use($tax_id, $text, $branch_id) { return $query->where('text', $text) ->where('branch_id', $branch_id)->where('tax_id', '!=', $tax_id); }), ], ));` – maruf najm Aug 22 '19 at 07:15
  • 6
    For update use ingore. `->ignore($serverid);` see the answer https://stackoverflow.com/a/58028505/9978078 – Dilip Hirapara Nov 04 '19 at 05:41
46

The following will work on the create

'data.ip' => ['required', 'unique:servers,ip,'.$this->id.',NULL,id,hostname,'.$request->input('hostname')]

and the following for the update

'data.ip' => ['required', 'unique:servers,ip,'.$this->id.','.$request->input('id').',id,hostname,'.$request->input('hostname')]

I'm presuming that id is your primary key in the table. Substitute it for your environment.


The (undocumented) format for the unique rule is:

table[,column[,ignore value[,ignore column[,where column,where value]...]]]

Multiple "where" conditions can be specified, but only equality can be checked. A closure (as in the accepted answer) is needed for any other comparisons.

miken32
  • 42,008
  • 16
  • 111
  • 154
Leon Vismer
  • 4,925
  • 1
  • 20
  • 22
  • I use custom request. And for store and for update I use the same request. – Dumitru May 15 '18 at 12:52
  • You would need to elaborate regarding what you mean by custom request. Then for the update and store it is ok to use the same request, however if the request contains the rules you will have an issue on the update if you do not ignore the entry into your database with the current primary key. – Leon Vismer May 15 '18 at 13:15
  • 1
    What if value contains comma? – Hlorofos Feb 26 '19 at 13:18
  • 2
    I doubt that `$request->input('id')` is necessary. This works for me `['column_1' => 'required|unique:TableName,column_1,' . $this->id . ',id,colum_2,' . $this->column_2]` for both create and update. – O Connor Oct 01 '19 at 14:44
  • This is exactly what i was looking for, detail explanation and example is missing on the official docs! – Tab Key Aug 18 '20 at 04:31
  • For me this is not working for update. I got this ` "title" => "required|min:2|max:255|unique:terms,title,20,id,type,listing-category"`. I had no entry rather then id 20 with same title. And I am trying to edit the same entry, I mean id with 20. But it is throwing me error instead of updating. Although for create it is working fine – jayant rawat Feb 01 '21 at 13:25
  • I need to check for unique condition where type in terms table is listing-category – jayant rawat Feb 01 '21 at 13:26
  • I feel lucky to come across this solution! Surprised why it is not in official document. – Asif Thebepotra Dec 06 '22 at 18:29
21

Table

server

Field

  • id primary key

  • ip should be unique with hostname

  • hostname should be unique with ip

Here I validate for Ip and the hostname should be unique.

use Illuminate\Validation\Rule;

$ip = '192.168.0.1';
$host = 'localhost';

While Create

Validator::make($data, [
    'ip' => [
        'required',
         Rule::unique('server')->where(function ($query) use($ip,$host) {
           return $query->where('ip', $ip)->where('hostname', $host);
         });
    ],
]);

While Update

Add ignore after RULE

Validator::make($data, [
    'ip' => [
        'required',
         Rule::unique('server')->where(function ($query) use($ip,$host) {
           return $query->where('ip', $ip)->where('hostname', $host);
         })->ignore($serverid);
    ],
]);
Dilip Hirapara
  • 14,810
  • 3
  • 27
  • 49
  • It is ignoring where condition in my case, I guess, while updating. I dd() the condition and got this. `Illuminate\Validation\Rules\Unique {#1871 ▼ #ignore: "10" #idColumn: "id" #table: "term" #column: "NULL" #wheres: array:1 [▼ 0 => array:2 [▼ "column" => "type" "value" => "list-cat" ] ] #using: [] }` But not working at all while updating. – jayant rawat Feb 01 '21 at 13:31
  • There is a category with the same name in table. But the type is different – jayant rawat Feb 01 '21 at 13:32
  • Note: i am using Laravel 5.8 – jayant rawat Feb 01 '21 at 13:33
20

Laravel 5.6 and above

Validation in the controller

The primary key (in my case) is a combination of two columns (name, guard_name)

I validate their uniqueness by using the Rule class both on create and on update method of my controller (PermissionsController)


PermissionsController.php

<?php

namespace App\Http\Controllers;

use App\Permission;

use Illuminate\Http\Request;
use Illuminate\Validation\Rule;
use App\Http\Controllers\Controller;

class PermissionsController extends Controller
{

    /**
     * Store a newly created resource in storage.
     */
    public function store(Request $request)
    {
        request()->validate([

            'name'        => 'required|max:255',

            'guard_name'  => [

                'required', 

                Rule::unique('permissions')->where(function ($query) use ($request) {

                    return $query
                        ->whereName($request->name)
                        ->whereGuardName($request->guard_name);
                }),
            ],
        ],
        [
            'guard_name.unique' => __('messages.permission.error.unique', [

                'name'              => $request->name, 
                'guard_name'        => $request->guard_name
            ]),
        ]);

        Permission::create($request->all());

        flash(__('messages.permission.flash.created'))->success();

        return redirect()->route('permission.index');
    }


    /**
     * Update the specified resource in storage.
     */
    public function update(Request $request, Permission $permission)
    {
        request()->validate([

            'name'        => 'required|max:255',

            'guard_name'  => [

                'required', 

                Rule::unique('permissions')->where(function ($query) use ($request, $permission) {

                    return $query
                        ->whereName($request->name)
                        ->whereGuardName($request->guard_name)
                        ->whereNotIn('id', [$permission->id]);
                }),
            ],
        ],
        [
            'guard_name.unique' => __('messages.permission.error.unique', [

                'name'              => $request->name, 
                'guard_name'        => $request->guard_name
            ]),
        ]);

        $permission->update($request->all());

        flash(__('messages.permission.flash.updated'))->success();

        return redirect()->route('permission.index');
    }
}

Notice in the update method i added an additional query constraint [ whereNotIn('id', [$permission->id]) ] to ignore the current model.


resources/lang/en/messages.php

<?php

return [

    'permission' => [

        'error' => [
            'unique' => 'The combination [":name", ":guard_name"] already exists',
        ],

        'flash' => [
            'updated' => '...',
            'created' => '...',
        ],
    ]
]

The flash() method is from the laracasts/flash package.

chebaby
  • 7,362
  • 50
  • 46
20

This works for me for both create and update.

[
     'column_1' => 'required|unique:TableName,column_1,' . $this->id . ',id,colum_2,' . $this->column_2
]

Note: tested in Laravel 6.

O Connor
  • 4,236
  • 15
  • 50
  • 91
  • I use one method to insert and update, this work for me, and very simple. Thanks O Connor ! – edward Oct 04 '21 at 15:12
8

With Form Requests:

In StoreServerRequest (for Create)

public function rules() {
    'ip' => [
        'required',
         Rule::unique('server')->where(function ($query) {
             $query->where('ip', $this->ip)
                ->where('hostname', $this->host);
         })
    ],
}

public function messages() {
    return [
       'ip.unique' => 'Combination of IP & Hostname is not unique',
    ];
}

In UpdateServerRequest (for Update)

Just Add ignore at the end

public function rules() {
    'ip' => [
        'required',
         Rule::unique('server')->where(function ($query) {
             $query->where('ip', $this->ip)
                ->where('hostname', $this->host);
         })->ignore($this->server->id)
    ],
}
Halfacht
  • 924
  • 1
  • 12
  • 22
6

Try this rule:
'data.ip' => 'required|unique:servers,ip,'.$this->id.'|unique:servers,hostname,'.$this->id

Salim Djerbouh
  • 10,719
  • 6
  • 29
  • 61
vpalade
  • 1,427
  • 1
  • 16
  • 20
  • this makes wrong condition and is not equal to `unique(['ip','hostname'])` but it is for `unique('ip'); unique('hostname');` which is different – Armin.G Feb 20 '22 at 18:38
2

This is the demo code. It would help you much better. I tried covering both insert and update scenarios.

Inside app/Http/Providers/AppServiceProvider.php

Validator::extend('uniqueOfMultiple', function ($attribute, $value, $parameters, $validator)
    {
        $whereData = [
            [$attribute, $value]
        ];

        foreach ($parameters as $key => $parameter) {

            //At 0th index, we have table name
            if(!$key) continue;

            $arr = explode('-', $parameter);

            if($arr[0] == 'except') {
                $column = $arr[1];
                $data = $arr[2];

                $whereData[] = [$column, '<>', $data];
            } else {
                $column = $arr[0];
                $data = $arr[1];

                $whereData[] = [$column, $data];
            }
        }

        $count = DB::table($parameters[0])->where($whereData)->count();
        return $count === 0;
    });

Inside app/Http/Requests/Something/StoreSometing.php

/**
 * Get the validation rules that apply to the request.
 *
 * @return array
 */
public function rules()
{
    return [
        'name' => 'required|max:225|uniqueOfMultiple:menus,location_id-' . $this->get('location_id', 'NULL') . ',language_id-' . $this->get('language_id', 1),
        'location_id' => 'required|exists:menu_location,id',
        'order' => 'digits_between:0,10'
    ];
}

Inside app/Http/Requests/Something/UpdateSomething.php

/**
 * Get the validation rules that apply to the request.
 *
 * @return array
 */
public function rules()
{
    return [
        'name' => 'required|max:225|uniqueOfMultiple:menus,location_id-' . $this->get('location_id', 'NULL') . ',language_id-' . $this->get('language_id', 'NULL') . ',except-id-' . $this->route('id', 'NULL'),
        'location_id' => 'required|exists:menu_location,id',
        'order' => 'digits_between:0,10'
    ];
}

Inside resources/lang/en/validation.php

'unique_of_multiple' => 'The :attribute has already been taken under it\'s parent.',

Here in this code, the custom validation used is uniqueOfMultiple. The first argument passed is the table_name i.e menus and all other arguments are column_name and are comma-separated. The columns are used here, name (primary column), location_id, language_id and one except-for column for the update case, except-id. The value passed for all three is - separated.

Ankit Singh
  • 922
  • 9
  • 16
  • I have published a laravel package for the same. Do check it out https://packagist.org/packages/ankitsinghdalal/laravel-unique-on-multiple – Ankit Singh Sep 18 '21 at 04:06
  • I have published a laravel package for the same. Do check it out https://packagist.org/packages/ankitsinghdalal/laravel-unique-on-multiple – Ankit Singh Sep 18 '21 at 04:06
2

This works for me for both create and update.

in your ServerUpdateRequest or ServerCreateRequest class

public function rules()
{            
    return [
       'column_1' => 'required|unique:TableName,column_1,' . $this->id . ',id,colum_2,' . $this->column_2 . ',colum_3,' . $this->column_3,
    ];
}

This command run background a aggregate Sql like this

select
   count(*) as aggregate 
from 
  `TableName` 
where 
  `column_1` = <postedColumn1Value>  
   and `id` <> idValue 
   and `column_2` = <postedColumn2Value> 
   and `column_3` = <postedColumn3Value> 

tested in Laravel 9. and it works

Note: if you want to see background sql for debugging (For example, to check if the request values are empty[$this->]) , especially you have to write wrong code, For example, you may enter a filed name incorrectly.

blackmamba
  • 99
  • 3
  • 11
1

The following code worked nicely for me at Laravel 8

Create:

'required|unique:TableName,column_1,' . $this->column_1 . ',id,colum_2,' . $this->column_2,

Example:

public function store(Request $request)
{
    $union = auth()->user()->union_id;
    $request->validate([
        'holding_no' => 'required|integer|unique:holding_taxes,holding_no,' . $request->holding_no . ',id,union_id,' . $union,
    ]);   
}

Update:

'required|unique:TableName,column_1,' . $this->id . ',id,colum_2,' . $this->column_2,

Example:

public function update(Request $request, $id)
{
    $union = auth()->user()->union_id;
    $request->validate([
        'holding_no' => 'required|unique:holding_taxes,holding_no,' . $id . ',id,union_id,'.$union,
    ]);   
}
0

for me laravel 8 this works

$req->validate([
    'house_no' => [
        Rule::unique('house')
          ->where('house_no', $req->input('house_no'))
          ->where('ward_no', $req->input('ward_no'))
    ],
]);
peter.babic
  • 3,214
  • 3
  • 18
  • 31
0

Simple solution with call back query

  Rule::unique('users')->where(fn ($query) => $query->where(['project_id'=> request()->project_id, 'code'=> request()->code ])),
khatib
  • 366
  • 5
  • 7
0

This code works in Laravel 9 to make two columns unique together

Create Request

public function rules()
    {
        return [
                'column1' => [
                'string',
                'required',
                Rule::unique('table_name')
                     ->where('column2', $this->column2)
                ],
            ];
    }
user311086
  • 920
  • 2
  • 16
  • 29
-2
public function store(Request $request)
    {
         $this->validate($request, [
            'first_name' => 'required|regex:/^[\pL\s\-]+$/u|max:255|unique:contacts,first_name, NULL,id,first_name,'.$request->input('last_name','id'),
            'last_name'=>'required|regex:/^[\pL\s\-]+$/u|max:255|unique:contacts,last_name',
            'email' => 'required|email|max:255|unique:contacts,email',
            'job_title'=>'required',
            'city'=>'required',
            'country'=>'required'],
            [
             'first_name.regex'=>'Use Alphabets Only',
             'email.unique'=>'Email is Already Taken.Use Another Email',
             'last_name.unique'=>'Contact Already Exist!. Try Again.',
            ]
        );
David Buck
  • 3,752
  • 35
  • 31
  • 35
zhan
  • 1