這篇文章并不是什么由 Laravel 改編的 SOLID 原則、模式等嗤谚。
只是為了讓你注意你在現(xiàn)實生活的 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 來查詢毅整,請將所有與數(shù)據(jù)庫相關的邏輯放入 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',
];
}
}
業(yè)務邏輯應該在服務類中
一個控制器必須只有一個職責翻具,因此應該將業(yè)務邏輯從控制器移到服務類。
壞:
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 模板中執(zhí)行查詢并使用關聯(lián)加載(N + 1 問題)
不好的地方在于坎缭,這對于100 個用戶來說竟痰,等于執(zhí)行 101 個 DB 查詢:
@foreach(User::all() as $user)
{{ $user->profile->name }}
@endforeach
下面的做法,對于 100 個用戶來說掏呼,僅僅只執(zhí)行 2 個 DB 查詢:
$users = User::with('profile')->get();
...
@foreach($users as $user)
{{ $user->profile->name }}
@endforeach
與其花盡心思給你的代碼寫注釋坏快,還不如對方法或變量寫一個描述性的名稱
壞:
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 中這樣來傳輸數(shù)據(jù):
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命名約定
盡可能使用更短、更易讀的語法
壞:
$request->session()->get('cart');
$request->input('name');
好:
session('cart');
$request->name;
使用 IoC 容器或 facades 代替 new Class
新的 Class 語法創(chuàng)建類時祥得,不僅使得類與類之間緊密耦合兔沃,還加重了測試的復雜度。推薦改用 IoC 容器或 facades级及。
壞:
$user = new User;
$user->create($request->all());
好:
public function __construct(User $user)
{
$this->user = $user;
}
....
$this->user->create($request->all());
不要直接從 .env 文件獲取數(shù)據(jù)
將數(shù)據(jù)傳遞給配置文件乒疏,然后使用輔助函數(shù) config() 在應用程序中使用數(shù)據(jù)。
壞:
$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 }}