摘要:最近,我需要在開發(fā)的事件管理系統(tǒng)中實(shí)現(xiàn)搜索功能。今天,我會(huì)介紹整個(gè)過程以及如何構(gòu)建靈活且可擴(kuò)展的搜索系統(tǒng)。這將是個(gè)挑戰(zhàn)前端的條件過濾的截圖。像剛剛的情況下搜索用戶時(shí)加上一個(gè)過濾器再返回搜索結(jié)果。
最近,我需要在開發(fā)的事件管理系統(tǒng)中實(shí)現(xiàn)搜索功能。 一開始只是簡(jiǎn)單的幾個(gè)選項(xiàng) (通過名稱,郵箱等搜索),到后面參數(shù)變得越來越多。
今天,我會(huì)介紹整個(gè)過程以及如何構(gòu)建靈活且可擴(kuò)展的搜索系統(tǒng)。如果你想查看代碼,請(qǐng)?jiān)L問?Git 倉(cāng)庫(kù)?。
我們將創(chuàng)造什么我們公司需要一種跟蹤我們與世界各地客戶舉辦的各種活動(dòng)和會(huì)議的方式。我們目前的唯一方法是讓每位員工在 Outlook 日程表上存儲(chǔ)會(huì)議的詳細(xì)信息。可拓展性較差!
我們需要公司的每個(gè)人都可以訪問,來查看我們客戶的被邀請(qǐng)的詳細(xì)信息以及他們的RSVP(國(guó)際縮用語(yǔ):請(qǐng)回復(fù))狀態(tài)。
這樣,我們可以通過上次與他們互動(dòng)的數(shù)據(jù)來確定哪些用戶可以邀請(qǐng)來參加未來的活動(dòng)。
使用高級(jí)搜索過濾器查找的截圖
查找用戶常用過濾用戶的方法:
通過姓名,電子郵件,位置
通過用戶工作的公司
被邀請(qǐng)參加特定活動(dòng)的用戶
參加過特定活動(dòng)的用戶
邀請(qǐng)及已參加活動(dòng)的用戶
邀請(qǐng)但尚未回復(fù)的用戶
答應(yīng)參加但未出席的用戶
分配給銷售經(jīng)理的用戶
雖然這個(gè)列表不算完整,但可以讓我們知道需要多少個(gè)過濾器。這將是個(gè)挑戰(zhàn)!
前端的條件過濾的截圖。
模型及模型關(guān)聯(lián)在這個(gè)例子中我們回用到很多模型:
User?---? 代表被邀請(qǐng)參加活動(dòng)的用戶。一個(gè)用戶可以參加很多活動(dòng)。
Event?--- 代表我公司舉辦的活動(dòng)。活動(dòng)可以有多個(gè)。
Rsvp?---? 代表用戶對(duì)活動(dòng)邀請(qǐng)的回復(fù)。一個(gè)用戶對(duì)一個(gè)活動(dòng)的回復(fù)是一對(duì)一的。
Manager?---? 一個(gè)用戶可以對(duì)應(yīng)多個(gè)我公司的銷售經(jīng)理.
搜索的需求在開始代碼之前,我想先把搜索的需求明確一下。也就是說我要很清楚地知道我要對(duì)哪些數(shù)據(jù)做搜索功能。
下面就是一個(gè)例子:
{ "name": "Billy", "company": "Google", "city": "London", "event": "key-note-presentation-new-york-05-2016", "responded": true, "response": "I will be attending", "managers": [ "Tom Jones", "Joe Bloggs" ], }
總結(jié)一下上面數(shù)據(jù)想表達(dá)的搜索的條件:
客人的名字是 "Billy",來自 "Google" 公司,目前居住在 "London",已經(jīng)對(duì) "key-note-presentation-new-york-05--2016" 的活動(dòng)邀請(qǐng)做出了回復(fù),并且回復(fù)的內(nèi)容是 "I will be attending",負(fù)責(zé)跟進(jìn)這位客人的銷售經(jīng)理是 "Tom Jones" 或者 "Joe Bloggs"。開始 --- 最佳實(shí)踐
我是一個(gè)堅(jiān)定不移的極簡(jiǎn)主義者,我堅(jiān)信少即是多。下面就讓我們以最簡(jiǎn)單的方式探索出解決這個(gè)需求的最佳實(shí)踐。
首先,在 ?routes.php 文件中添加如下代碼:
Route::post("/search", "SearchController@filter");
接下來,創(chuàng)建 ?SearchController.
php artisan make:controller SearchController
添加前面路由中明確的?filter()? 方法:
由于我們需要在 filter 方法中處理請(qǐng)求提交的數(shù)據(jù),所以我把 Request 類做了依賴注入。Laravel 的服務(wù)容器 會(huì)解析這個(gè)依賴,我們可以在方法中直接使用 Request 的實(shí)例,也就是 $request。User 類也是同樣道理,我們需要從中檢索一些數(shù)據(jù)。
這個(gè)搜索需求有一點(diǎn)比較麻煩的是,每個(gè)參數(shù)都是可選的。所以我們要先寫一系列的條件語(yǔ)句來判斷每個(gè)參數(shù)是否存在:
這是我初步寫出來的代碼:
public function filter(Request $request, User $user) { // 根據(jù)姓名查找用戶 if ($request->has("name")) { return $user->where("name", $request->input("name"))->get(); } // 根據(jù)公司名查找用戶 if ($request->has("company")) { return $user->where("company", $request->input("company")) ->get(); } // 根據(jù)城市查找用戶 if ($request->has("city")) { return $user->where("city", $request->input("city"))->get(); } // 繼續(xù)根據(jù)其他條件查找 // 再無(wú)其他條件, // 返回所有符合條件的用戶。 // 在實(shí)際項(xiàng)目中需要做分頁(yè)處理。 return User::all(); }很明顯,上面的代碼邏輯是錯(cuò)誤的。
首先,它只會(huì)根據(jù)一個(gè)條件去檢索用戶表,然后就返回了。所以,通過上面的代碼邏輯,我們根本無(wú)法獲得姓名為 "Billy", 而且住在 "London" 的用戶。
實(shí)現(xiàn)這種目的的一種方式是嵌套條件:
// 根據(jù)用戶名搜索用戶 if ($request->has("name")) { // 是否還提供了 "city" 搜索參數(shù) if ($request->has("city")) { // 基于用戶名及城市搜索用戶 return $user->where("name", $request->input("name")) ->where("city", $request->input("city")) ->get(); } return $user->where("name", $request->input("name"))->get(); }我確信你可以看到這在兩個(gè)或者三個(gè)參數(shù)的時(shí)候起作用,但是一旦我們添加更多選項(xiàng),這將會(huì)難以管理。
改進(jìn)我們的搜索 api所以我們?nèi)绾巫屵@個(gè)生效,而同時(shí)不會(huì)因?yàn)榍短讞l件而變得瘋狂?
我們可以使用 User 模型繼續(xù)重構(gòu),來使用?builder?而不是直接返回模型。
public function filter(Request $request, User $user) { $user = $user->newQuery(); // 根據(jù)用戶名搜索用戶 if ($request->has("name")) { $user->where("name", $request->input("name")); } // 根據(jù)用戶公司信息搜索用戶 if ($request->has("company")) { $user->where("company", $request->input("company")); } // 根據(jù)用戶城市信息搜索用戶 if ($request->has("city")) { $user->where("city", $request->input("city")); } // 繼續(xù)執(zhí)行其他過濾 // 獲得并返回結(jié)果 return $user->get(); }好多了!我們現(xiàn)在可以將每個(gè)搜索參數(shù)做為修飾符添加到從 ?$user->newQuery() 返回的查詢實(shí)例中。
我們現(xiàn)在可以根據(jù)所有的參數(shù)來做搜索了, 再多參數(shù)都不怕.
一起來實(shí)踐吧:
$user = $user->newQuery(); // 根據(jù)姓名查找用戶 if ($request->has("name")) { $user->where("name", $request->input("name")); } // 根據(jù)公司名查找用戶 if ($request->has("company")) { $user->where("company", $request->input("company")); } // 根據(jù)城市查找用戶 if ($request->has("city")) { $user->where("city", $request->input("city")); } // 只查找有對(duì)接我公司銷售經(jīng)理的用戶 if ($request->has("managers")) { $user->whereHas("managers", function ($query) use ($request) { $query->whereIn("managers.name", $request->input("managers")); }); } // 如果有 "event" 參數(shù) if ($request->has("event")) { // 只查找被邀請(qǐng)的用戶 $user->whereHas("rsvp.event", function ($query) use ($request) { $query->where("event.slug", $request->input("event")); }); // 只查找回復(fù)邀請(qǐng)的用戶( 以任何形式回復(fù)都可以 ) if ($request->has("responded")) { $user->whereHas("rsvp", function ($query) use ($request) { $query->whereNotNull("responded_at"); }); } // 只查找回復(fù)邀請(qǐng)的用戶( 限制回復(fù)的具體內(nèi)容 ) if ($request->has("response")) { $user->whereHas("rsvp", function ($query) use ($request) { $query->where("response", "I will be attending"); }); } } // 最終獲取對(duì)象并返回 return $user->get();搞定,棒極了!
是否還需要重構(gòu)?通過上面的代碼我們實(shí)現(xiàn)了業(yè)務(wù)需求,可以根據(jù)搜索條件返回正確的用戶信息。但是我們能說這是最佳實(shí)踐嗎?顯然是不能。
現(xiàn)在是通過一系列條件判斷的嵌套來實(shí)現(xiàn)業(yè)務(wù)邏輯,而且所有的邏輯都在控制器里,這樣做真的合適嗎?
這可能是一個(gè)見仁見智的問題,最好還是結(jié)合自己的項(xiàng)目,具體問題具體分析。如果你的項(xiàng)目比較小,邏輯相對(duì)簡(jiǎn)單,而且只是一個(gè)短期需求的項(xiàng)目,那么就不必糾結(jié)這個(gè)問題了,直接照上面的邏輯寫就好了。
然而,如果你是在構(gòu)建一個(gè)比較復(fù)雜的項(xiàng)目,那么我們還是需要更加優(yōu)雅且擴(kuò)展性好的解決方案。
編寫新的搜索?api當(dāng)我要寫一個(gè)功能接口的時(shí)候,我不會(huì)立刻去寫核心代碼,我通常會(huì)先想想我要怎么用這個(gè)接口。這可能就是俗稱的「面向結(jié)果編程」(或者說是「結(jié)果導(dǎo)向思維」)。
「在你寫一個(gè)組件之前,建議你先寫一些要用這個(gè)組件的測(cè)試代碼。通過這種方式,你會(huì)更加清晰地知道你究竟要寫哪些函數(shù),以及傳哪些必要的參數(shù),這樣你才能寫出真正好用的接口。因?yàn)閷懡涌诘哪康氖呛?jiǎn)化使用組件的代碼,而不是簡(jiǎn)化接口自身的代碼。」 ( 摘自:?c2.com)
根據(jù)我的經(jīng)驗(yàn),這個(gè)方法能幫助我寫出可讀性更強(qiáng),更加優(yōu)雅的程序。還有一個(gè)很大的額外收獲就是,通過這種階段性的驗(yàn)收測(cè)試,我能更好地抓住商業(yè)需求。因此,我可以很自信地說我寫的程序可以很好地滿足市場(chǎng)的需求,具有很高商業(yè)價(jià)值。
以下添加到搜索功能的代碼中,我希望我的搜索 api 是這樣寫的:
return UserSearch::apply($filters);這樣有著很好的可讀性。 根據(jù)經(jīng)驗(yàn), 如果我查閱代碼能想看文章的句子一樣,那會(huì)非常美妙。像剛剛的情況下:
搜索用戶時(shí)加上一個(gè)過濾器再返回搜索結(jié)果。這對(duì)技術(shù)人員和非技術(shù)人員都有意義。
我想我需要新建一個(gè) UserSearch 類,還需要一個(gè)靜態(tài)的 apply 函數(shù)來接收過濾條件。讓我開始吧:
最簡(jiǎn)單的方式,讓我們把控制器中的代碼復(fù)制到 apply 函數(shù)中:
newQuery(); // 基于用戶名搜索 if ($filters->has("name")) { $user->where("name", $filters->input("name")); } // 基于用戶的公司名搜索 if ($filters->has("company")) { $user->where("company", $filters->input("company")); } // 基于用戶的城市名搜索 if ($filters->has("city")) { $user->where("city", $filters->input("city")); } // 只返回分配了銷售經(jīng)理的用戶 if ($filters->has("managers")) { $user->whereHas("managers", function ($query) use ($filters) { $query->whereIn("managers.name", $filters->input("managers")); }); } // 搜索條件中是否包含 "event’ ? if ($filters->has("event")) { // 只返回被邀請(qǐng)參加了活動(dòng)的用戶 $user->whereHas("rsvp.event", function ($query) use ($filters) { $query->where("event.slug", $filters->input("event")); }); // 只返回以任何形式答復(fù)了邀請(qǐng)的用戶 if ($filters->has("responded")) { $user->whereHas("rsvp", function ($query) use ($filters) { $query->whereNotNull("responded_at"); }); } // 只返回以某種方式答復(fù)了邀請(qǐng)的用戶 if ($filters->has("response")) { $user->whereHas("rsvp", function ($query) use ($filters) { $query->where("response", "I will be attending"); }); } } // 返回搜索結(jié)果 return $user->get(); } }我們做了一系列的改變。 首先, 我們將在控制器中的 $request 變量更名為 filters 來提高可讀性。
其次,由于 newQuery() 方法不是靜態(tài)方法,無(wú)法通過 User 類靜態(tài)調(diào)用,所以我們需要先創(chuàng)建一個(gè) User 對(duì)象,再調(diào)用這個(gè)方法:
$user = (new User)->newQuery();調(diào)用上面的 UserSearch 接口,對(duì)控制器的代碼進(jìn)行重構(gòu):
好多了,是不是?把一系列的條件判斷交給專門的類處理,使控制器的代碼簡(jiǎn)介清新。
下面進(jìn)入見證奇跡的時(shí)刻在這篇文章的例子中,一共有 7 個(gè)過濾條件,但是現(xiàn)實(shí)的情況是更多更多。所以在這種情況下,只用一個(gè)文件來處理所有的過濾邏輯,就顯得差強(qiáng)人意了。擴(kuò)展性不好,而且也不符合 S.O.L.I.D. principles 原則。目前,apply()? 方法需要處理這些邏輯:
檢查參數(shù)是否存在
把參數(shù)轉(zhuǎn)成查詢條件
執(zhí)行查詢
如果我想增加一個(gè)新的過濾條件,或者修改一下現(xiàn)有的某個(gè)過濾條件的邏輯,我都要不停地修改 UserSearch 類,因?yàn)樗羞^濾條件的處理都在這一個(gè)類里,隨著業(yè)務(wù)邏輯的增加,會(huì)有點(diǎn)尾大不掉的感覺。所以對(duì)每個(gè)過濾條件多帶帶建個(gè)類文件是非常有必要的。
先從 Name 條件開始吧。但是,就像我們前面講的,還是想一下我們需要怎樣使用這種單一條件過濾的接口。
我希望可以這樣調(diào)用這個(gè)接口:
$user = (new User)->newQuery(); $user = static::applyFiltersToQuery($filters, $user); return $user->get();不過這里再使用 $user 這個(gè)變量名就不合適了,應(yīng)該用 $query 更有意義。
public static function apply(Request $filters) { $query = (new User)->newQuery(); $query = static::applyFiltersToQuery($filters, $query); return $query->get(); }然后把所有條件過濾的邏輯都放到 applyFiltersToQuery() 這個(gè)新接口里。
下面開始創(chuàng)建第一個(gè)條件過濾類:Name.
where("name", $value); } }在這個(gè)類里定義一個(gè)靜態(tài)方法 apply(),這個(gè)方法接收兩個(gè)參數(shù),一個(gè)是 Builder 實(shí)例,另一個(gè)是過濾條件的值( 在這個(gè)例子中,這個(gè)值是 "Billy" )。然后帶著這個(gè)過濾條件返回一個(gè)新的 Builder 實(shí)例。
接下來是 City 類:
where("city", $value); } }如你所見,City 類的代碼邏輯跟 Name 類相同,只是過濾條件變成了 "city"。讓每個(gè)條件過濾類都只有一個(gè)簡(jiǎn)單的 apply() 方法,而且方法接收的參數(shù)和處理的邏輯都相同,我們可以把這看成一個(gè)協(xié)議,這一點(diǎn)很重要,下面我會(huì)具體說明。
為了確保每個(gè)條件過濾類都能遵循這個(gè)協(xié)議,我決定寫一個(gè)接口,讓每個(gè)類都實(shí)現(xiàn)這個(gè)接口。
我為這個(gè)接口的方法寫了詳細(xì)的注釋,這樣做的好處是,對(duì)于每一個(gè)實(shí)現(xiàn)這個(gè)接口的類,我都可以利用我的 IDE ( PHPStorm ) 自動(dòng)生成同樣的注釋。
下面,分別在 Name 和 City 類中實(shí)現(xiàn)這個(gè) Filter 接口:
where("name", $value); } }以及
where("city", $value); } }完美。現(xiàn)在已經(jīng)有兩個(gè)條件過濾類完美地遵循了這個(gè)協(xié)議。把我的目錄結(jié)構(gòu)附在下面給大家參考一下:
這是到目前為止關(guān)于搜索的文件結(jié)構(gòu)。
我把所有的條件過濾類的文件放在一個(gè)多帶帶的文件夾里,這讓我對(duì)已有的過濾條件一目了然。
使用新的過濾器現(xiàn)在回過頭來看 UserSearch 類的 applyFiltersToQuery() 方法,發(fā)現(xiàn)我們可以再做一些優(yōu)化了。
首先,把每個(gè)條件判斷里構(gòu)建查詢語(yǔ)句的工作,交給對(duì)應(yīng)的過濾類去做。
// 根據(jù)姓名搜索用戶 if ($filters->has("name")) { $query = Name::apply($query, $filters->input("name")); } // 根據(jù)城市搜索用戶 if ($filters->has("city")) { $query = City::apply($query, $filters->input("city")); }現(xiàn)在根據(jù)過濾條件構(gòu)建查詢語(yǔ)句的工作已經(jīng)轉(zhuǎn)給各個(gè)相應(yīng)的過濾類了,但是判斷每個(gè)過濾條件是否存在的工作,還是通過一系列的條件判斷語(yǔ)句完成的。而且條件判斷的參數(shù)都是寫死的,一個(gè)參數(shù)對(duì)應(yīng)一個(gè)過濾類。這樣我每增加一個(gè)新的過濾條件,我都要重新修改 UserSearch 類的代碼。這顯然是一個(gè)需要解決的問題。
其實(shí),根據(jù)我們前面介紹的命名規(guī)則, 我們很容易把這段條件判斷的代碼改成動(dòng)態(tài)的:
AppUserSearchFiltersNameAppUserSearchFiltersCity
就是結(jié)合命名空間和過濾條件的名稱,動(dòng)態(tài)地創(chuàng)建過濾類(當(dāng)然,要對(duì)接收到的過濾條件參數(shù)做適當(dāng)?shù)奶幚恚?/p>
大概就是這個(gè)思路,下面是具體實(shí)現(xiàn):
private static function applyFiltersToQuery( Request $filters, Builder $query) { foreach ($filters->all() as $filterName => $value) { $decorator = __NAMESPACE__ . "Filters" . str_replace(" ", "", ucwords( str_replace("_", " ", $filterName))); if (class_exists($decorator)) { $query = $decorator::apply($query, $value); } } return $query; }下面逐行分析這段代碼:
foreach ($filters->all() as $filterName => $value) {遍歷所有的過濾參數(shù),把參數(shù)名(比如 city)賦值給變量 $filterName,參數(shù)值(比如 London)復(fù)制給變量 $value。
$decorator = __NAMESPACE__ . "Filters" . str_replace(" ", "", ucwords( str_replace("_", " ", $filterName)));這里是對(duì)參數(shù)名進(jìn)行處理,將下劃線改成空格,讓每個(gè)單詞都首字母大寫,然后去掉空格,如下例子:
"name" => AppUserSearchFiltersName, "company" => AppUserSearchFiltersCompany, "city" => AppUserSearchFiltersCity, "event" => AppUserSearchFiltersEvent, "responded" => AppUserSearchFiltersResponded, "response" => AppUserSearchFiltersResponse, "managers" => AppUserSearchFiltersManagers如果有參數(shù)名是帶下劃線的,比如 has_responded,根據(jù)上面的規(guī)則,它將被處理成 HasResponded,因此,其相應(yīng)的過濾類的名字也要是這個(gè)。
if (class_exists($decorator)) {這里就是要先確定這個(gè)過濾類是存在的,再執(zhí)行下面的操作,否則在客戶端報(bào)錯(cuò)就尷尬了。
$query = $decorator::apply($query, $value);這里就是神器的地方了,PHP 允許把變量 $decorator 作為類,并調(diào)用其方法(在這里就是 apply() 方法了)。現(xiàn)在再看這個(gè)接口的代碼,發(fā)現(xiàn)我們?cè)俅螌?shí)力證明了磨刀不誤砍柴工。現(xiàn)在我們可以確保每個(gè)過濾類對(duì)外響應(yīng)一致,內(nèi)部又可以分別處理各自的邏輯。
最后的優(yōu)化現(xiàn)在 UserSearch 類的代碼應(yīng)該已經(jīng)比之前好多了,但是,我覺得還可以更好,所以我又做了些改動(dòng),這是最終版本:
newQuery() ); return static::getResults($query); } private static function applyDecoratorsFromRequest(Request $request, Builder $query) { foreach ($request->all() as $filterName => $value) { $decorator = static::createFilterDecorator($filterName); if (static::isValidDecorator($decorator)) { $query = $decorator::apply($query, $value); } } return $query; } private static function createFilterDecorator($name) { return return __NAMESPACE__ . "Filters" . str_replace(" ", "", ucwords(str_replace("_", " ", $name))); } private static function isValidDecorator($decorator) { return class_exists($decorator); } private static function getResults(Builder $query) { return $query->get(); } }我最后決定去掉 applyFiltersToQuery() 方法,是因?yàn)楦杏X跟接口的主要方法名 apply() 有點(diǎn)沖突了。
而且,為了貫徹執(zhí)行單一職責(zé)原則,我把原來 applyFiltersToQuery() 方法里比較復(fù)雜的邏輯又做了拆分,為動(dòng)態(tài)創(chuàng)建過濾類名稱,和確認(rèn)過濾類是否存在的判斷,都寫了多帶帶的方法。
這樣,即便要擴(kuò)展搜索接口,我也不需要再去反復(fù)修改 UserSearch 類里的代碼了。需要增加新的過濾條件嗎?簡(jiǎn)單,只要在 AppUserSearchFilters 目錄下創(chuàng)建一個(gè)過濾類,并使之實(shí)現(xiàn) Filter 接口就 OK 了。
結(jié)論我們已經(jīng)把一個(gè)擁有所有搜索邏輯的巨大控制器方法保存成一個(gè)允許打開過濾器的模塊化過濾系統(tǒng),而不需要添加修改核心代碼。 像評(píng)論里?@rockroxx所建議的,另一個(gè)重構(gòu)的方案是把所有方法提取到?trait 并將?User? 設(shè)置成 ?const? 然后由?Interface 實(shí)現(xiàn)。
class UserSearch implements Searchable { const MODEL = AppUser; use SearchableTrait; }如果你很好的理解了這個(gè)設(shè)計(jì)模式,你可以?利用多態(tài)代替多條件。
代碼會(huì)提交到?GitHub?你可以 fork,測(cè)試和實(shí)驗(yàn)。
如何解決多條件高級(jí)搜索,我希望你能留下你的想法、建議和評(píng)論。
文章轉(zhuǎn)自:https://learnku.com/laravel/t...
更多文章:https://learnku.com/laravel/c...
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/31090.html
摘要:安全生成安全的隨機(jī)數(shù),加密數(shù)據(jù),掃描漏洞的庫(kù)一個(gè)兼容標(biāo)準(zhǔn)的過濾器一個(gè)生成隨機(jī)數(shù)和字符串的庫(kù)使用生成隨機(jī)數(shù)的庫(kù)一個(gè)安全庫(kù)一個(gè)純安全通信庫(kù)一個(gè)簡(jiǎn)單的鍵值加密存儲(chǔ)庫(kù)一個(gè)結(jié)構(gòu)化的安全層一個(gè)試驗(yàn)的面向?qū)ο蟮陌b庫(kù)一個(gè)掃描文件安全的庫(kù) Security 安全 生成安全的隨機(jī)數(shù),加密數(shù)據(jù),掃描漏洞的庫(kù) HTML Purifier-一個(gè)兼容標(biāo)準(zhǔn)的HTML過濾器 RandomLib-一個(gè)生成隨機(jī)數(shù)和字...
摘要:安全生成安全的隨機(jī)數(shù),加密數(shù)據(jù),掃描漏洞的庫(kù)一個(gè)兼容標(biāo)準(zhǔn)的過濾器一個(gè)生成隨機(jī)數(shù)和字符串的庫(kù)使用生成隨機(jī)數(shù)的庫(kù)一個(gè)安全庫(kù)一個(gè)純安全通信庫(kù)一個(gè)簡(jiǎn)單的鍵值加密存儲(chǔ)庫(kù)一個(gè)結(jié)構(gòu)化的安全層一個(gè)試驗(yàn)的面向?qū)ο蟮陌b庫(kù)一個(gè)掃描文件安全的庫(kù) Security 安全 生成安全的隨機(jī)數(shù),加密數(shù)據(jù),掃描漏洞的庫(kù) HTML Purifier-一個(gè)兼容標(biāo)準(zhǔn)的HTML過濾器 RandomLib-一個(gè)生成隨機(jī)數(shù)和字...
摘要:是文檔的一種表示結(jié)構(gòu)。這些任務(wù)大部分都是基于它。這個(gè)實(shí)踐的重點(diǎn)是把你在前端練級(jí)攻略第部分中學(xué)到的一些東西和結(jié)合起來。一旦你進(jìn)入框架部分,你將更好地理解并使用它們。到目前為止,你一直在使用進(jìn)行操作。它是在前端系統(tǒng)像今天這樣復(fù)雜之前編寫的。 本文是 前端練級(jí)攻略 第二部分,第一部分請(qǐng)看下面: 前端練級(jí)攻略(第一部分) 在第二部分,我們將重點(diǎn)學(xué)習(xí) JavaScript 作為一種獨(dú)立的語(yǔ)言,如...
摘要:別堵塞了傳輸層大多數(shù)事件處理器被當(dāng)作傳輸層組件。解耦事件處理器開始本命題前,我們來使用一個(gè)示例。假想下把隊(duì)列處理器用來發(fā)送消息給用戶。盡量避免在事件處理器中摻雜太多的業(yè)務(wù)邏輯。 聲明:本文并非博主原創(chuàng),而是來自對(duì)《Laravel 4 From Apprentice to Artisan》閱讀的翻譯和理解,當(dāng)然也不是原汁原味的翻譯,能保證90%的原汁性,另外因?yàn)槭抢斫夥g,肯定會(huì)有錯(cuò)誤的...
閱讀 832·2023-04-26 00:13
閱讀 2823·2021-11-23 10:08
閱讀 2450·2021-09-01 10:41
閱讀 2118·2021-08-27 16:25
閱讀 4198·2021-07-30 15:14
閱讀 2365·2019-08-30 15:54
閱讀 864·2019-08-29 16:22
閱讀 2741·2019-08-26 12:13