Skip to content

Instantly share code, notes, and snippets.

@craigh411
Last active October 20, 2015 12:46
Show Gist options
  • Save craigh411/2d315718c9a6fb097f6f to your computer and use it in GitHub Desktop.
Save craigh411/2d315718c9a6fb097f6f to your computer and use it in GitHub Desktop.
Trait to help with Laravels unique validator

Laravel 5.1 Helper Trait for using unique

In Laravel the unique validation rule will trigger even when you are updating the original record, unless you provide some logic in your FormRequest to exclude the record when updating. This trait can be used to avoid that logic by detecting when a record is being created or updated.

Usage

Place the trait in app\Http\Traits (if using a different directory simply change the namespace) and then use it inside your form request or add it to the app\Requests\Request.php:

use App\Http\Traits\ValidationHelper

You can now do as follows in your FormRequest:

public function rules()
{
  return [
    'email' => [$this->unique('users','email')]
  ];
}

This will now automatically exclude the original record from the unique check when it is being updated.

Parameters

The unique function takes the following parameters:

$table The name of the table we are using the unique rule on.
$column - The column that should be unique.
$id - The name of the primary key (defaults to id).
$where - The where parameters passed as an associative array e.g. ['account_id' => 1]
$routeParameterName - The route parameter name for the update. Only needs to be specified if you have multiple route parameters
$ignoreExclusionRule - Set to true to avoid excluding any records when updating. When set to true it's same as using 'unique:table,column' and is only included for consistency purposes.

Note: This trait pulls the excluded id directly from the route. If you only have one route parameter e.g. /admin/users/{user_id}/edit then the value of {user_id} will automatically be used in the exclude rule. You only need to specifiy the $routeParameterName if you are using multiple paramaters in your route e.g. /my/route/{param1}/{param2}, In this case to exclude param2 you would pass 'param2' as the $routeParameterName.

Route-Model Binding

The trait supports route model binding, there is no need to make any adjustments if you are binding your models to your routes.

Example

 public function rules()
{

  return [
    'email' => [$this->unique('users','email', 'id', ['account_id' => 1], 'user_id')]
  ];
}
<?php
namespace App\Http\Traits;
use Exception;
use Illuminate\Support\Facades\Request;
trait ValidationHelper {
/**
* Builds the relevant 'unique' validation queries for both store and update
*
* @param $table - The name of the table we are looking at
* @param null $column - The column that should be unique
* @param string $id - The name of the primary key
* @param array $where - The where parameters passed as an associative array
* @param null $routeParameterName - The route parameter name. Only needs to be specified if you have multiple route parameters
* @param $ignoreExclusionRule - Set to true to avoid excluding any records when updating
* @return string
*/
public function unique($table, $column = null, $id = 'id', array $where = [], $routeParameterName = null, $ignoreExclusionRule = false)
{
$unique = "unique:{$table}";
$unique .= ($column) ? ",{$column}" : "";
// Check that we are updating the record and that we have set a column for the query and we are excluding everything.
if($this->isUpdating() && $column && ! $ignoreExclusionRule)
{
$routeParameterName = $routeParameterName ?: $this->getRouteParameterName();
$unique .= "," . $this->getIdFromRoute($id, $routeParameterName);
}
elseif(! empty($where))
{
// If we are using where clauses on 'POST' add NULL as the ignore rule.
$unique .= ",NULL";
}
$unique .= $this->buildWhereClauses($id, $where);
return $unique;
}
/**
* @return bool
*/
protected function isUpdating()
{
return Request::getMethod() == "PUT" || Request::getMethod() == "PATCH";
}
/**
* Builds up uniques' 'where' section
* @param $id
* @param array $where
* @return string
*/
protected function buildWhereClauses($id, array $where)
{
if(empty($where))
{
return "";
}
$where = array_map(function ($key, $value)
{
return $key . ',' . $value;
}, array_keys($where), $where);
$where = implode(',', $where);
return ",{$id},{$where}";
}
/**
* Gets the id of the record from the route
*
* @param $id
* @param $routeParameterName
* @return string
*/
protected function getIdFromRoute($id, $routeParameterName)
{
// Check if we have an object (route-model binding) or the actual id, so we can return the correct value.
return (is_object(Request::route($routeParameterName))) ? Request::route($routeParameterName)->$id : Request::route($routeParameterName);
}
/**
* Attempts to get the route parameter name from the route.
* @return mixed
* @throws Exception
*/
protected function getRouteParameterName()
{
$parameterNames = Request::route()->parameterNames();
if(count($parameterNames) > 1)
{
throw new Exception('Too many route parameters, please specify which route parameter name you want to use.');
}
return Request::route()->parameterNames()[0];
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment