我正在使用Laravel 4编写用户注册表单,该表单允许用户在first_name
和last_name
字段中输入其姓名。在我的表单输入验证期间,我想从表中要保存的值中检查两个字段是否与复合名称first_name + " " + last_name
的唯一性。
我知道您可以使用unique
规则检查单个字段的唯一性,甚至可以通过指定unique:tableName,fieldName
覆盖该字段。
理想情况下,我会做unique:tableName,first_name + " " + last_name
之类的事情,或者在模型中指定一些内容,但我还没有能够在复合/虚拟字段中找到任何内容。
答案 0 :(得分:2)
写下你自己的规则。它看起来像这样:
'unique_composite:table,field1,field2,field3,...,fieldN'
将连接表名后枚举的字段并根据复合值进行检查。验证规则如下所示:
Validator::extend('unique_composite', function ($attribute, $value, $parameters)
{
// Get table name from first parameter
$table = array_shift($parameters);
$fields = implode(',', $parameters);
// Build the query that searches the database for matches
$matches = DB::table($table)
->where(DB::raw('CONCAT_WS(" ", ' . $fields . ')'), $value)
->count();
// Validation result will be false if any rows match the combination
return ($matches == 0);
});
在您的验证器中,您可以使用以下内容:
$validator = Validator::make(
array('full_name' => $firstName + ' ' + $lastName),
array('full_name' => 'unique_composite:users,first_name,last_name')
);
使用此规则,您可以使用任意数量的字段,而不仅仅是两个字段。
答案 1 :(得分:1)
我最终扩展了Validator类来定义自定义验证器规则。我在我的应用程序的first_name
字段上检查它主要是因为我不想在生成全名时做额外的工作。除了将其作为复合值,而不是在考虑问题后这是不必要的,我只是将其设置为检查指定字段的所有值的AND
。您可以指定任意数量的字段,如果验证程序数据中不存在其中一个字段,则会引发异常。我甚至不确定这是否只能通过现有的unique
规则来完成,但无论如何这都是一个很好的练习。
'first_name' => 'unique_multiple_fields:members,first_name,last_name'
我的验证器子类代码:
use Illuminate\Validation\Validator as IlluminateValidator;
class CustomValidatorRules extends IlluminateValidator
{
/**
* Validate that there are no records in the specified table which match all of the
* data values in the specified fields. Returns true iff the number of matching
* records is zero.
*/
protected function validateUniqueMultipleFields( $attribute, $value, $parameters )
{
if (is_null($parameters) || empty($parameters)) {
throw new \InvalidArgumentException('Expected $parameters to be a non-empty array.');
}
if (count($parameters) < 3) {
throw new \InvalidArgumentException('The $parameters option should have at least 3 items: table, field1, field2, [...], fieldN.');
}
// Get table name from first parameter, now left solely with field names.
$table = array_shift($parameters);
// Uppercase the table name, remove the 's' at the end if it exists
// to get the class name of the model (by Laravel convention).
$modelName = preg_replace("/^(.*)([s])$/", "$1", ucfirst($table));
// Create the SQL, start by getting only the fields specified in parameters
$select = $modelName::select($parameters);
// Generate the WHERE clauses of the SQL query.
foreach ($parameters as $fieldName) {
$curFieldVal = ($fieldName === $attribute) ? $value : $this->data[$fieldName];
if (is_null($curFieldVal)) {
// There is no data for the field specified, so fail.
throw new \Exception("Expected `{$fieldName}` data to be set in the validator.");
}
// Add the current field name and value
$select->where($fieldName, '=', $curFieldVal);
}
// Get the number of fields found
$numFound = $select->count();
return ($numFound === 0);
}
}
如果你很好奇,我确实使用我最初看到的复合方法让它工作。代码如下。事实证明'分隔符'是完全没有意义的,因此我最终重构它以使用上面指定的多字段方法。
use Illuminate\Validation\Validator as IlluminateValidator;
class CustomValidatorRules extends IlluminateValidator
{
/**
* Validate that the final value of a set of fields - joined by an optional separator -
* doesn't match any records in the specified table. Returns true iff the number of
* matching records is zero.
*/
protected function validateUniqueComposite( $attribute, $value, $parameters )
{
if (is_null($parameters) || empty($parameters)) {
throw new \InvalidArgumentException('Expected $parameters to be a non-empty array.');
}
if (count($parameters) < 3) {
throw new \InvalidArgumentException('The $parameters option should have at least 3 items: table, field1, field2, [...], fieldN.');//, [separator].');
}
// Get table name from first parameter
$table = array_shift($parameters);
// Determine the separator
$separator = '';
$lastParam = array_pop($parameters);
if (! isset($this->data[$lastParam])) {
$separator = $lastParam;
}
// Get the names of the rest of the fields.
$fields = array();
foreach ($parameters as $fieldName) {
array_push($fields, $table . "." . $fieldName);
}
$fields = implode(', ', $fields);
$dataFieldValues = array();
foreach ($parameters as $fieldName) {
$curFieldVal = ($fieldName === $attribute) ? $value : $this->data[$fieldName];
if (is_null($curFieldVal)) {
throw new \Exception("Expected `{$fieldName}` data.");
}
array_push($dataFieldValues, $curFieldVal);
}
$compositeValue = implode($separator, $dataFieldValues);
// Uppercase the table name, remove the 's' at the end if it exists
// to get the class name of the model (by Laravel convention).
$modelName = preg_replace("/^(.*)([s])$/", "$1", ucfirst($table));
$raw = \DB::raw("concat_ws('" . $separator . "', " . $fields . ")");
$model = new $modelName;
// Generate the SQL query
$select = $modelName::where($raw, '=', $compositeValue);
$numFound = $select->count();
return ($numFound === 0);
}
}
答案 2 :(得分:0)
如果不创建自定义验证器,您还可以指定更多条件,将其添加为&#34;其中&#34;查询的子句并执行:
'first_name' => 'required|unique:table_name,first_name,null,id,last_name,'.$data['last_name'],
'last_name' => 'required|unique:table_name,last_name,null,id,first_name,'.$data['first_name'],
这样,first_name
的唯一性只会在last_name
等于输入的last_name
(我们示例中为$data['last_name']
)的行上强制执行。
另一种方法是last_name
的唯一性。
如果您想强制唯一规则忽略给定ID,只需将null
替换为该特定ID即可。