自定義驗證規則的處理不當是爲什麼我放棄了拉拉維爾(嗯,這是很多原因之一,但它是打破駱駝背的稻草,可以這麼說)。但無論如何,我有三個部分的答案給你:你不想在這個特定情況下這樣做的原因,快速概述你必須處理的混亂,然後在你的問題的答案你仍然想要這樣做。
重要的安全問題管理登錄
最佳安全實踐表明你應該總是返回一個一般的錯誤信息進行登錄的問題。典型的反例是,如果您返回「該電子郵件未在我們的系統中註冊」,而未找到電子郵件,並且錯誤密碼爲正確的電子郵件,則返回「錯誤密碼」。如果您提供單獨的驗證消息,則可以向潛在攻擊者提供有關如何更有效地指揮其攻擊的更多信息。因此,所有與登錄相關的問題都應該返回一個通用的驗證消息,而不管其根本原因如何,以及「無效的電子郵件/密碼組合」的效果。對於密碼恢復表單也是如此,這些表單通常會說:「密碼恢復說明已發送到該電子郵件,如果它存在於我們的系統中」。否則,你會給攻擊者(和其他人)一種方法來知道你的系統註冊了哪些電子郵件地址,並且可以暴露額外的攻擊媒介。所以在這個特定的情況下,一個驗證信息就是你想要的。
的麻煩laravel
你碰到的問題是,laravel驗證簡單地返回true或false表示該規則是否被滿足。錯誤消息分開處理。您無法從驗證器邏輯中指定驗證器錯誤消息。我知道。這很荒謬,而且計劃不周。你所能做的只是返回真或假。您無法訪問其他任何內容來幫助您,所以您的僞代碼不會執行此操作。
的(醜陋的)答案
創建自己的驗證消息最簡單的方法就是create your own validator。這看起來是這樣的(你的控制器內):
$validator = Validator::make($input, $rules, $messages);
你仍然要創建開機驗證(您Valiator::Extend
電話然後就可以正常通過使它們到您的自定義驗證指定$rules
最後,你可以指定你的郵件這樣的事情,總(控制器內):
public function login(Request $request)
{
$rules = [
'email' => 'bail|required|checkEmailPresenceAndValidity'
]
$messages = [
'checkEmailPresenceAndValidity' => 'Invalid email.',
];
$validator = Validator::make($request->all(), $rules, $messages);
}
(我不記得,如果你要指定您$messages
陣列中的每個規則,我不這麼認爲)當然,即使這不是很棒,因爲你爲$ message傳遞的只是一個數組字符串(這就是允許的)。因此,您仍然不能根據用戶輸入輕易更改此錯誤消息。這一切都發生在驗證器運行之前。您的目標是根據驗證結果讓驗證消息發生變化,但laravel會強制您首先生成消息。因此,要真正做你想做的事情,你必須調整系統的實際流程,這並不是很棒。
解決方案是在您的控制器中有一個方法來計算您的自定義驗證規則是否被滿足。在你做驗證器之前,它會這樣做,以便你可以發送一個合適的消息給你構建的驗證器。然後,當您創建驗證規則時,只要將您的規則定義移動到控制器中,您也可以將其與綁定到驗證計算器的結果綁定。你只需要確定,而不是意外地把事情搞亂。您還必須記住,這需要將您的驗證邏輯移到驗證器之外,這是相當黑客。不幸的是,我95%確定沒有其他辦法可以做到這一點。
我在下面有一些示例代碼。它肯定有一些缺點:你的規則不再是全局的(它在控制器中定義),驗證邏輯移出驗證器(違反了最小驚訝原則),你將不得不提出一個in - 對象緩存方案(這不難),以確保您不會執行兩次查詢,因爲驗證邏輯被調用兩次。重申一下,這絕對是不吉利的,但我相當肯定這是用laravel做你想做的唯一方法。可能有更好的方法來組織這個,但這至少應該讓你知道你需要做什麼。
<?php
namespace App\Http\Controllers;
use User;
use Validator;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
class LoginController extends Controller
{
public function __construct() {
Validator::extend('checkEmailPresenceAndValidity', function ($attribute, $value, $parameters, $validator) {
return $this->checkLogin($value) === true ? true : false;
});
}
public function checkLogin($email) {
$user = User::where('email', $email)->first();
// Email has not been found
if (! $user) {
return 'not found';
}
// Email has not been validated
if (! $user->valid_email) {
return 'invalid';
}
return true;
}
public function login(Request $request) {
$rules = [
'email' => 'bail|required|checkEmailPresenceAndValidity'
]
$hasError = $this->checkLogin($request->email);
if ($hasError === 'not found')
$message = "That email wasn't found";
elseif ($hasError === 'invalid')
$message = "That is an invalid email";
else
$message = "Something was wrong with your request";
$messages = [
'checkEmailPresenceAndValidity' => $message,
];
$validator = Validator::make($request->all(), $rules, $messages);
if ($validator->fails()) {
// do something and redirect/exit
}
// process successful form here
}
}
此外,值得一快速的注意,此實現依賴於閉包,這(我相信)$this
支持PHP 5.4中添加。如果您使用的是舊版本的PHP,則必須提供$this
來關閉use
。
編輯咆哮
它真正歸結爲是,laravel驗證系統的設計是非常精細的。每個驗證規則都是專門用來驗證一件事情的。因此,給定驗證器的驗證消息不應該被更改,因此$messages
(當您構建自己的驗證器時)只接受普通字符串。
一般來說,粒度在應用程序設計中是一件好事,而SOLID原則的正確實施則力求實現。但是,這個特殊的實現讓我發瘋。我的一般編程哲學是,一個好的實現應該使最常見的用例非常簡單,然後爲不太常見的用例開闢道路。在這種情況下,laravel的體系結構使得最常見的用例變得簡單,但不太常見的用例幾乎是不可能的。我對這種交易不滿意。我對Laravel的總體印象是,只要你需要以迂迴的方式來做事,它就會很好,但如果你出於任何原因不得不跳出這個框,它會積極地讓你的生活變得更加困難。在你的情況下,最好的答案是可能只是立即回到那個框中,即,即使它意味着進行冗餘查詢也要創建兩個驗證器。對應用程序性能的實際影響可能根本無關緊要,但是您將獲得長期可維護性,以使您的應用程序表現得相當大。
有趣。我認爲這個答案只是支持我在我的總體總結:做這件相當簡單的事情(根據輸入定製錯誤信息)在laravel中幾乎是不可能的!你的答案和我的都不是特別簡單。 –
作爲一個簡要說明,您的特質依賴於它在用於工作的類中的實現細節的行爲,這通常會導致非常脆弱的應用程序。詳細地說,你的特質依賴於'$ this-> v'中的驗證器,但它只是在那裏,因爲'MyCustomFormRequest'填充它。這意味着這個特質根本不可重用,這就破壞了特質的目的。我認爲你最好放棄這個特性並將'validateUserEmailAndValidity'移動到'CreateUserRequest'中,或者讓驗證器成爲你的特性中的一個必需參數,而不是從'$ this-> v'中獲取。 –