2014-11-01 59 views
2

我正在使用Laravel 4編寫用戶註冊表單,該表單允許用戶在first_namelast_name字段中輸入其名稱。在我的表單輸入驗證過程中,我希望根據表中保存這個值的值來檢查這兩個字段是否具有唯一性,以確保複合名稱first_name + " " + last_name根據Laravel中的表單輸入驗證複合模型字段的唯一性?

我知道您可以使用unique規則檢查單個字段的唯一性,甚至可以通過指定unique:tableName,fieldName來覆蓋該字段。

理想情況下,我會做類似unique:tableName,first_name + " " + last_name的事情,或者通過在模型本身中指定某些東西,但我一直無法在複合/虛擬字段上找到任何東西。

回答

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); 
    } 
} 
+1

當您設置'$ modelName'時,您將刪除字符串末尾的's'。這並不總是得到單數形式的正確方法(想'box'=>'盒子')。你可以通過使用Laravel的'str_plural()'([檢查文檔](http://laravel.com/docs/4.2/helpers#strings))來改進它。對於那些使用非英文模型/表名的人,你應該關注這種多元化(或者簡單地將表名稱傳遞給驗證)。 – 2015-05-08 09:03:08

+0

感謝您的提示。我將它添加到此項目的修補程序隊列中。 – Derek 2015-05-08 14:21:20

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') 
); 

有了這個規則,你可以使用任意數量的字段,而不是兩個。

+0

我會試試看。我在哪裏放置這個驗證器擴展的代碼呢? – Derek 2014-11-01 22:35:05

+0

只要包含包含規則的文件,就沒有關於此的任何約定。例如,你可以把它放在'app/validators.php'中,並用'app_path()。'/ validators.php';'將它包含在'app/start/global.php'中。 – Bogdan 2014-11-01 22:39:19

+0

您提供的查詢的每種變體都會給我以下SQL錯誤(postgresql):'未定義的列:7錯誤:列「composite_value」不存在# – Derek 2014-11-02 03:47:21

-1

,而無需創建一個自定義的驗證,也可以指定多個條件將被添加爲「在哪裏」的條款來查詢並做到:

'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,只需用該特定ID替換null即可。

參考:http://laravel.com/docs/4.2/validation#rule-unique

+0

如果我在last_name中傳遞,會發生什麼?可以注射...... – 2016-08-03 17:59:13