1. 程式人生 > >Laravel 的十八個最佳實踐

Laravel 的十八個最佳實踐

file

這篇文章並不是什麼由 Laravel 改編的 SOLID 原則、模式等。

只是為了讓你注意你在現實生活的 Laravel 專案中最常忽略的內容。

單一責任原則

一個類和一個方法應該只有一個職責。
錯誤的做法:

public function getFullNameAttribute()
{
    if (auth()->user() && auth()->user()->hasRole('client') && auth()->user()->isVerified()) {
        return 'Mr
. ' . $this->
first_name . ' ' . $this->middle_name . ' ' $this->last_name; } else { return $this->first_name[0] . '. ' . $this->last_name; } }

推薦的做法:

public function getFullNameAttribute()
{
    return $this->isVerifiedClient() ? $this->getFullNameLong() : $this
->getFullNameShort(); } public function isVerfiedClient() { return auth()->user() && auth()->user()->hasRole('client') && auth()->user()->isVerified(); } public function getFullNameLong() { return 'Mr. ' . $this->first_name . ' ' . $this->middle_name . ' '
. $this->last_name; } public function getFullNameShort() { return $this->first_name[0] . '. ' . $this->last_name; }

強大的模型 & 簡單控制器

如果你使用查詢構造器或原始 SQL 來查詢,請將所有與資料庫相關的邏輯放入 Eloquent 模型或儲存庫類中。

壞:

““
public function index()
{
clients=Client::verified()>with([orders=>function(q) {
$q->where(‘created_at’, ‘>’, Carbon::today()->subWeek());
}])
->get();

return view('index', ['clients' => $clients]);

}
““

好:

““
public function index()
{
return view(‘index’, [‘clients’ => $this->client->getWithNewOrders()]);
}

Class Client extends Model
{
public function getWithNewOrders()
{
return this>verified()>with([orders=>function(q) {
$q->where(‘created_at’, ‘>’, Carbon::today()->subWeek());
}])
->get();
}
}
““

驗證

將驗證從控制器移動到請求類。

很常見但不推薦的做法:

public function store(Request $request)
{
    $request->validate([
        'title' => 'required|unique:posts|max:255',
        'body' => 'required',
        'publish_at' => 'nullable|date',
    ]);

    ....
}

最好是這樣:

public function store(PostRequest $request)
{    
    ....
}

class PostRequest extends Request
{
    public function rules()
    {
        return [
            'title' => 'required|unique:posts|max:255',
            'body' => 'required',
            'publish_at' => 'nullable|date',
        ];
    }
}

業務邏輯應該在服務類中

一個控制器必須只有一個職責,因此應該將業務邏輯從控制器移到服務類。

壞:

public function store(Request $request)
{
    if ($request->hasFile('image')) {
        $request->file('image')->move(public_path('images') . 'temp');
    }

    ....
}

好:

““
public function store(Request request)  {this->articleService->handleUploadedImage($request->file(‘image’));

....

}

class ArticleService
{
public function handleUploadedImage(image)      {          if (!is_null(image)) {
$image->move(public_path(‘images’) . ‘temp’);
}
}
}
““

不要重複你自己(DRY)

儘可能重用程式碼。 SRP(單一職責原則)正在幫助你避免重複。當然,這也包括了 Blade 模板、Eloquent 的範圍等。

壞:

““
public function getActive()
{
return $this->where(‘verified’, 1)->whereNotNull(‘deleted_at’)->get();
}

public function getArticles()
{
return this>whereHas(user,function(q) {
$q->where(‘verified’, 1)->whereNotNull(‘deleted_at’);
})->get();
}
““

好:

public function scopeActive($q)
{
    return $q->where('verified', 1)->whereNotNull('deleted_at');
}

public function getActive()
{
    return $this->active()->get();
}

public function getArticles()
{
    return $this->whereHas('user', function ($q) {
            $q->active();
        })->get();
}

最好傾向於使用 Eloquent 而不是 Query Builder 和原生的 SQL 查詢。要優先於陣列的集合

Eloquent 可以編寫可讀和可維護的程式碼。此外,Eloquent 也擁有很棒的內建工具,比如軟刪除、事件、範圍等。

比如你這樣寫:

SELECT *
FROM `articles`
WHERE EXISTS (SELECT *
              FROM `users`
              WHERE `articles`.`user_id` = `users`.`id`
              AND EXISTS (SELECT *
                          FROM `profiles`
                          WHERE `profiles`.`user_id` = `users`.`id`) 
              AND `users`.`deleted_at` IS NULL)
AND `verified` = '1'
AND `active` = '1'
ORDER BY `created_at` DESC

還不如這樣寫:

Article::has('user.profile')->verified()->latest()->get();

批量賦值

比如你這樣寫:

$article = new Article;
$article->title = $request->title;
$article->content = $request->content;
$article->verified = $request->verified;
// Add category to article
$article->category_id = $category->id;
$article->save();

是不是還不如這樣寫:


$category->article()->create($request->all());

不要在 Blade 模板中執行查詢並使用關聯載入(N + 1 問題)

不好的地方在於,這對於100 個使用者來說,等於執行 101 個 DB 查詢:


[@foreach](https://laravel-china.org/users/5651) (User::all() as $user)
{{ $user->profile->name }}
@endforeach

下面的做法,對於 100 個使用者來說,僅僅只執行 2 個 DB 查詢:

““
$users = User::with(‘profile’)->get();

與其花盡心思給你的程式碼寫註釋,還不如對方法或變數寫一個描述性的名稱

壞:

if (count((array) $builder->getQuery()->joins) > 0)

好:

// 確定是否有任何連線。
if (count((array) $builder->getQuery()->joins) > 0)

最好:

if ($this->hasJoins())

不要把 JS 和 CSS 放在 Blade 模板中,也不要將任何 HTML 放在 PHP 類中

壞:

let article = `{{ json_encode($article) }}`;

好:

<input id="article" type="hidden" value="{{ json_encode($article) }}">

Or

<button class="js-fav-article" data-article="{{ json_encode($article) }}">{{ $article->name }}<button>

最好的方法是使用在 Javascript 中這樣來傳輸資料:

let article = $('#article').val();

在程式碼中使用配置和語言檔案、常量,而不是寫死它

壞:

public function isNormal()
{
    return $article->type === 'normal';
}

return back()->with('message', 'Your article has been added!');

好:

public function isNormal()
{
    return $article->type === Article::TYPE_NORMAL;
}

return back()->with('message', __('app.article_added'));

使用社群接受的標準的 Laravel 工具

最好使用內建的 Laravel 功能和社群軟體包,而不是其他第三方軟體包和工具。因為將來與你的應用程式一起工作的開發人員都需要學習新的工具。另外,使用第三方軟體包或工具的話,如果遇到困難,從 Laravel 社群獲得幫助的機會會大大降低。不要讓你的客戶為此付出代價!

任務 標準工具 第三方工具
授權 Policies Entrust, Sentinel and other packages
前端編譯 Laravel Mix Grunt, Gulp, 3rd party packages
開發環境 Homestead Docker
部署 Laravel Forge Deployer and other solutions
單元測試 PHPUnit, Mockery Phpspec
瀏覽器測試 Laravel Dusk Codeception
資料庫操作 Eloquent SQL, Doctrine
模板 Blade Twig
資料操作 Laravel collections Arrays
表單驗證 Request classes 3rd party packages, validation in controller
認證 Built-in 3rd party packages, your own solution
API 認證 Laravel Passport 3rd party JWT and OAuth packages
建立 API Built-in Dingo API and similar packages
資料庫結構操作 Migrations Working with DB structure directly
區域性化 Built-in 3rd party packages
實時使用者介面 Laravel Echo, Pusher 3rd party packages and working with WebSockets directly
Generating testing data Seeder classes, Model Factories, Faker Creating testing data manually
生成測試資料 Laravel Task Scheduler Scripts and 3rd party packages
資料庫 MySQL, PostgreSQL, SQLite, SQL Server MongoDB

遵循Laravel命名約定

遵循 PSR 標準。 另外,請遵循 Laravel 社群接受的命名約定:

型別 規則 正確示例 錯誤示例
Controller 單數 ArticleController ArticlesController
Route 複數 articles/1 article/1
Named route 帶點符號的蛇形命名 users.show_active users.show-active, show-active-users
Model 單數 User Users
hasOne or belongsTo relationship 單數 articleComment articleComments, article_comment
All other relationships 複數 articleComments articleComment, article_comments
Table 複數 article_comments article_comment, articleComments
Pivot table 按字母順序排列的單數模型名稱 article_user user_article, articles_users
Table column 帶著模型名稱的蛇形命名 meta_title MetaTitle; article_meta_title
Foreign key 帶_id字尾的單數型號名稱 article_id ArticleId, id_article, articles_id
Primary key - id custom_id
Migration - 2017_01_01_000000_create_articles_table 2017_01_01_000000_articles
Method 小駝峰命名 getAll get_all
Method in resource controller 具體看錶格 store saveArticle
Method in test class 小駝峰命名 testGuestCannotSeeArticle test_guest_cannot_see_article
Variable 小駝峰命名 articlesWithAuthor|articles_with_author~~
Collection 具描述性的複數形式 activeUsers=User::active()>get()|active, $data~~
Object 具描述性的單數形式 activeUser=User::active()>first()|users, $obj~~
Config and language files index 蛇形命名 articles_enabled ArticlesEnabled; articles-enabled
View 蛇形命名 show_filtered.blade.php showFiltered.blade.php, show-filtered.blade.php
Config 蛇形命名 google_calendar.php googleCalendar.php, google-calendar.php
Contract (interface) 形容詞或名詞 Authenticatable AuthenticationInterface, IAuthentication
Trait 形容詞 Notifiable NotificationTrait

儘可能使用更短、更易讀的語法

壞:

$request->session()->get('cart');
$request->input('name');

好:

session('cart');
$request->name;

更多示例:

通用語法 更短、更可讀的語法
Session::get('cart') session('cart')
$request->session()->get('cart') session('cart')
Session::put('cart', $data) session(['cart' => $data])
$request->input('name'), Request::get('name') $request->name, request('name')
return Redirect::back() return back()
is_null($object->relation) ? $object->relation->id : null } optional($object->relation)->id
return view('index')->with('title', $title)->with('client', $client) return view('index', compact('title', 'client'))
$request->has('value') ? $request->value : 'default'; $request->get('value', 'default')
Carbon::now(), Carbon::today() now(), today()
App::make('Class') app('Class')
->where('column', '=', 1) ->where('column', 1)
->orderBy('created_at', 'desc') ->latest()
->orderBy('age', 'desc') ->latest('age')
->orderBy('created_at', 'asc') ->oldest()
->select('id', 'name')->get() ->get(['id', 'name'])
->first()->name ->value('name')

使用 IoC 容器或 facades 代替新的 Class

新的 Class 語法建立類時,不僅使得類與類之間緊密耦合,還加重了測試的複雜度。推薦改用 IoC 容器或 facades。

壞:

$user = new User;
$user->create($request->all());

好:

public function __construct(User $user)
{
    $this->user = $user;
}

....

$this->user->create($request->all());

相關推薦

Laravel最佳實踐

這篇文章並不是什麼由 Laravel 改編的 SOLID 原則、模式等。 只是為了讓你注意你在現實生活的 Laravel 專案中最常忽略的內容。 單一責任原則 一個類和一個方法應該只有一個職責。 錯誤的做法: public functi

Hadoop管理員的最佳實踐

宣告 以下內容轉自 http://www.infoq.com/cn/articles/hadoop-ten-best-practice 前言 接觸Hadoop有兩年的時間了,期間遇到很多的問題,既有經典的NameNode和JobTracker記憶體溢位故障,也有HDFS儲存

抵禦GandCrab勒索軟件的最佳實踐

策略 解密 都在 官網 員工 top 下載文件 方案 完全訪問 勒索軟件軟件越來越盛行,特別是在2017年的WannaCry之後,勒索軟件已經成為讓企業憂心的主要網絡威脅。隨著勒索軟件的演進,企業需要不斷關註他們的網絡生態,特別是近來勒索軟件開始出現了不同的變體。GandC

MySQL性能優化的21最佳實踐 和 mysql使用索引

oct 靜態 state zid 希望 lte 適合 實踐 打開 今天,數據庫的操作越來越成為整個應用的性能瓶頸了,這點對於Web應用尤其明顯。關於數據庫的性能,這並不只是DBA才需要擔心的事,而這更是我 們程序員需要去關註的事情。當我們去設計數據庫表結構,對操作數據庫時(

MySQL性能優化的21最佳實踐

明顯 架構 crash 其它 string 簡單的 nts arch 網絡 當我們去設計數據庫表結構,對操作數據庫時(尤其是查表時的SQL語句),我們都需要註意數據操作的性能。這裏,我們不會講過多的SQL語句的優化,而只是針對MySQL這一Web應用最多的數據庫。 1. 為

完美CSS文檔的8最佳實踐

風格 產品經理 sta war support display 主題 ucd 發生 在css的世界,文檔沒有被得到充分的利用。由於文檔對終端用戶不可見,因此它的價值常常被忽視。另外,如果你第一次為css編寫文檔,可能很難確定哪些內容值得記錄,以及如何能夠高效完

數據湖是一種方法 數據湖的四最佳實踐

發出 就是 arc 而不是 程序集 image new 裏的 子郵件 轉載自:http://bigdata.chinabyte.com/311/13871811.shtml數據湖聽起來很簡單:把數據或信息匯集到一個結合處理速度和存儲空間的大數據系統――Hadoop集群或內存

Java 中處理異常的 9 最佳實踐

lan method 永遠 是否 res ati 是你 dex mes 在本文中,作者介紹了9個處理異常的最佳方法與實踐,以舉例與代碼展示結合的方式,讓開發者更好的理解這9種方式,並指導讀者在不同情況下選擇不同的異常處理方式。 以下為譯文: Java中的異常處理不是一個簡單

輕量ORM-SqlRepoEx (五)最佳實踐之資料對映(Map)

簡介:SqlRepoEx是 .Net平臺下相容.NET Standard 2.0人一個輕型的ORM。解決了Lambda轉Sql語句這一難題,SqlRepoEx使用的是Lambda表示式,所以,對c#程式設計師來說,是非常簡單的,其語法特點與Linq to Sql極為相似。不僅實現了完整的Select

編寫 Node.js Rest API 的 10 最佳實踐

Node.js 除了用來編寫 WEB 應用之外,還可以用來編寫 API 服務,我們在本文中會介紹編寫 Node.js Rest API 的最佳實踐,包括如何命名路由、進行認證和測試等話題,內容摘要如下: 正確使用 HTTP Method 和路由 正確的使用 HTTP 狀態碼 使用 HTTP H

Java 程式設計中關於異常處理的10最佳實踐

        異常處理是Java 開發中的一個重要部分。它是關乎每個應用的一個非功能性需求,是為了處理任何錯誤狀況,比如資源不可訪問,非法輸入,空輸入等等。Java提供了幾個異常處理特性,以try,catch 和 finally 關鍵字的形式內建於語言自

有效資料湖攝取的5最佳實踐

在不斷波動的技術環境和客戶市場政策的世界中,資料已成為最大的商業資產之一。它加強並提高了組織在競爭中取得領先地位的能力。因此,它是一個關鍵的價值創造者,其管理,定期維護和儲存對於規劃未來持續成功的企業來說非常重要。多年來技術的進步在資料建立和儲存方面都是有利的,但它們永遠不足以進行有效的資料管理。有時,企

Java異常處理的9最佳實踐

無論你是新手還是資深程式設計師,複習下異常處理的實踐總是一件好事,因為這能確保你與你的團隊在遇到問題時能夠處理得了它。 在 Java 中處理異常並不是一件易事。新手覺得處理異常難以理解,甚至是資深開發者也會花上好幾個小時來討論是應該丟擲拋異常還是處理異常。 這就是為何大多

輕量ORM-SqlRepoEx (四)最佳實踐之Dapper(1)

簡介:SqlRepoEx是 .Net平臺下相容.NET Standard 2.0人一個輕型的ORM。解決了Lambda轉Sql語句這一難題,SqlRepoEx使用的是Lambda表示式,所以,對c#程式設計師來說,是非常簡單的,其語法特點與Linq to Sql極為相似。不僅

木門企業最典型的問題

問題一:資訊傳遞經常失真,給企業造成浪費和損失,例如以傳真訂單出錯率高為代表,客戶明明下單2070*900*300,最後卻變成了2010*900*300, 問題二:各工序銜接混亂、半成品交接混亂 問題三:價格計算太複雜了,每個詳單都需要翻價格表才能確定,價格表並不能實時更新,玻璃、雕花等加價計算不標準,經

規模化敏捷開發的10最佳實踐(上)

【編者按】軟體開發和採購人員經常會對現有軟體開發方法、技巧和工具產生一些疑問。針對這些疑問,Kevin Fall 整理了五個軟體方面的話題:Agile at Scale,Safety-Critical Systems,Monitoring Software-In

編寫 Dockerfile 的五最佳實踐

此文適合Docker初學入門讀者,大師請繞行!,遵守最佳實踐可少踩坑、提升效能體驗及可移植性,期望對讀者有所幫助! 什麼是Dockerfile Dockerfile 是一個文字檔案,裡面包含了打包Docker映象所需要用到的命令。Docker 可以通

Java中處理異常的9最佳實踐

【譯者注】在本文中,作者介紹了9個處理異常的最佳方法與實踐,以舉例與程式碼展示結合的方式,讓開發者更好的理解這9種方式,並指導讀者在不同情況下選擇不同的異常處理方式。 以下為譯文:Java中的異常處理不是一個簡單的話題。初學者很難理解,甚至有經驗的開發人員也會

中科大再創世界紀錄,首次實現量子位元糾纏

驕傲!中科大再次重新整理了量子糾纏的世界紀錄!日前,該校潘建偉教授及其同事陸朝陽、劉乃樂、汪喜林

超級經典小故事

  一、 用人之道  去過廟的人都知道,一進廟門,首先是彌陀佛,笑臉迎客,而在他的北面,則是黑口黑臉的韋陀。但相傳在很久以前,他們並不在同一個廟裡,而是分別掌管不同的廟。彌樂佛熱情快樂,所以來的人非常多,但他什麼都不在乎,丟三拉四,沒有好好的管理賬務,所以依然入不敷出。而韋陀