Laravel 代碼開發最佳實踐
我們這里要討論的并不是 Laravel 版的 SOLID 原則(想要了解更多 SOLID 原則細節查看這篇文章)亦或是設計模式,而是 Laravel 實際開發中容易被忽略的最佳實踐。

內容概覽
單一職責原則
胖模型,瘦控制器
驗證
業務邏輯應該放到服務類
DRY(Don't Repeat Yourself,不要重復造輪子)
優先使用 Eloquent 而不是查詢構建器和原生 SQL 查詢,優先使用集合而不是數組
批量賦值
不要在 Blade 模板中執行查詢 & 使用渴求式加載(避免 N+1 問題)
注釋代碼
不要把 JS 和 CSS 代碼放到 Blade 模板里面,不要在 PHP 類中寫 HTML 代碼
使用配置、語言文件、常量而不是在代碼中寫死
使用社區接受的標準 Laravel 工具
遵循 Laravel 命名約定
使用更短的、可讀性更好的語法
使用 IoC 容器或門面而不是創建新類
不要直接從 .env 文件獲取數據
以標準格式存儲日期,使用訪問器和修改器來編輯日期格式
其他好的實踐
單一職責原則
一個類和方法只負責一項職責。
壞代碼:
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 查詢的話將所有 DB 相關邏輯都放到 Eloquent 模型或 Repository 類。
壞代碼:
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
盡可能復用代碼,單一職責原則可以幫助你避免重復,此外,盡可能復用 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 和 集合
通過 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 執行查詢 & 使用渴求式加載
壞代碼:
@foreach (User::all() as $user)
{{ $user->profile->name }}
@endforeach
好代碼:
$users = User::with('profile')->get();
...
@foreach ($users as $user)
{{ $user->profile->name }}
@endforeach
注釋你的代碼
壞代碼:
if (count((array) $builder->getQuery()->joins) > 0)
好代碼:
// Determine if there are any joins.
if (count((array) $builder->getQuery()->joins) > 0)
最佳:
if ($this->hasJoins())
將前端代碼和 PHP 代碼分離:
不要把 JS 和 CSS 代碼寫到 Blade 模板里,也不要在 PHP 類中編寫 HTML 代碼。
壞代碼:
let article = `{{ json_encode($article) }}`;
好代碼:
或者
在 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 命名約定
遵循 PSR 標準。此外,還要遵循 Laravel 社區版的命名約定:
使用縮寫或可讀性更好的語法
壞代碼:
$request->session()->get('cart');
$request->input('name');
好代碼:
session('cart');
$request->name;
更多示例:
使用 IoC 容器或門面
自己創建新的類會導致代碼耦合度高,且難于測試,取而代之地,我們可以使用 IoC 容器或門面。
壞代碼:
$user = new User;
$user->create($request->all());
好代碼:
public function __construct(User $user)
{
$this->user = $user;
}
....
$this->user->create($request->all());
不要從直接從 .env 獲取數據
傳遞數據到配置文件然后使用 config 輔助函數獲取數據。
壞代碼:
$apiKey = env('API_KEY');
好代碼:
// config/api.php
'key' => env('API_KEY'),
// Use the data
$apiKey = config('api.key');
以標準格式存儲日期
使用訪問器和修改器來編輯日期格式。
壞代碼:
{{ Carbon::createFromFormat('Y-d-m H-i', $object->ordered_at)->toDateString() }}
{{ Carbon::createFromFormat('Y-d-m H-i', $object->ordered_at)->format('m-d') }}
好代碼:
// Model
protected $dates = ['ordered_at', 'created_at', 'updated_at']
public function getMonthDayAttribute($date)
{
return $date->format('m-d');
}
// View
{{ $object->ordered_at->toDateString() }}
{{ $object->ordered_at->monthDay }}
其他好的實踐
不要把任何業務邏輯寫到路由文件中。
在 Blade 模板中盡量不要編寫原生 PHP。
https://laravelacademy.org/post/8464.html
Laravel PHP
版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。
版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。