單一職責(zé)原則
一個(gè)類和方法只負(fù)責(zé)一項(xiàng)職責(zé)。
壞代碼:
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;
}
胖模型援奢、瘦控制器
如果你使用的是查詢構(gòu)建器或原生 SQL 查詢的話將所有 DB 相關(guān)邏輯都放到 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();
}
}
驗(yàn)證
將驗(yàn)證邏輯從控制器轉(zhuǎn)移到請求類肝劲。
壞代碼:
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è)務(wù)邏輯需要放到服務(wù)類
一個(gè)控制器只負(fù)責(zé)一項(xiàng)職責(zé)突雪,所以需要把業(yè)務(wù)邏輯都轉(zhuǎn)移到服務(wù)類中闪唆。
壞代碼:
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
盡可能復(fù)用代碼碧库,單一職責(zé)原則可以幫助你避免重復(fù)柜与,此外巧勤,盡可能復(fù)用 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();
}
優(yōu)先使用 Eloquent 和 集合
通過 Eloquent 可以編寫出可讀性和可維護(hù)性更好的代碼颅悉,此外,Eloquent 還提供了強(qiáng)大的內(nèi)置工具如軟刪除迁匠、事件签舞、作用域等。
壞代碼:
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();
批量賦值
關(guān)于批量賦值細(xì)節(jié)可查看對應(yīng)文檔柒瓣。
壞代碼:
$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í)行查詢 & 使用渴求式加載
壞代碼:
@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) }}`;
好代碼:
<input id="article" type="hidden" value="{{ json_encode($article) }}">
或者
<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'));
使用被社區(qū)接受的標(biāo)準(zhǔn) Laravel 工具
優(yōu)先使用 Laravel 內(nèi)置功能和社區(qū)版擴(kuò)展包,其次才是第三方擴(kuò)展包和工具傍药。這樣做的好處是降低以后的學(xué)習(xí)和維護(hù)成本磺平。
任務(wù) | 標(biāo)準(zhǔn)工具 | 第三方工具 |
---|---|---|
授權(quán) | 策略類 | Entrust、Sentinel等 |
編譯資源 | Laravel Mix | Grunt拐辽、Gulp等 |
開發(fā)環(huán)境 | Homestead | Docker |
部署 | Laravel Forge | Deployer等 |
單元測試 | PHPUnit拣挪、Mockery | Phpspec |
瀏覽器測試 | Laravel Dusk | Codeception |
DB | Eloquent | SQL、Doctrine |
模板 | Blade | Twig |
處理數(shù)據(jù) | Laravel集合 | 數(shù)組 |
表單驗(yàn)證 | 請求類 | 第三方擴(kuò)展包俱诸、控制器中驗(yàn)證 |
認(rèn)證 | 內(nèi)置功能 | 第三方擴(kuò)展包菠劝、你自己的解決方案 |
API認(rèn)證 | Laravel Passport | 第三方 JWT 和 OAuth 擴(kuò)展包 |
創(chuàng)建API | 內(nèi)置功能 | Dingo API和類似擴(kuò)展包 |
處理DB結(jié)構(gòu) | 遷移 | 直接操作DB |
本地化 | 內(nèi)置功能 | 第三方工具 |
實(shí)時(shí)用戶接口 | Laravel Echo、Pusher | 第三方直接處理 WebSocket的擴(kuò)展包 |
生成測試數(shù)據(jù) | 填充類睁搭、模型工廠赶诊、Faker | 手動(dòng)創(chuàng)建測試數(shù)據(jù) |
任務(wù)調(diào)度 | Laravel Task Scheduler | 腳本或第三方擴(kuò)展包 |
DB | MySQL、PostgreSQL园骆、SQLite舔痪、SQL Server | MongoDB |
遵循 Laravel 命名約定
遵循 PSR 標(biāo)準(zhǔn)。此外锌唾,還要遵循 Laravel 社區(qū)版的命名約定:
What | How | Good | Bad |
---|---|---|---|
控制器 | 單數(shù) | ArticleController | |
路由 | 復(fù)數(shù) | articles/1 | |
命名路由 | 下劃線+'.'號分隔 | users.show_active | |
模型 | 單數(shù) | User | |
一對一關(guān)聯(lián) | 單數(shù) | articleComment | |
其他關(guān)聯(lián)關(guān)系 | 復(fù)數(shù) | articleComments | |
數(shù)據(jù)表 | 復(fù)數(shù) | article_comments | |
中間表 | 按字母表排序的單數(shù)格式 | article_user | |
表字段 | 下劃線锄码,不帶模型名 | meta_title | |
外鍵 | 單數(shù)、帶_id后綴 | article_id | |
主鍵 | - | id | |
遷移 | - | 2017_01_01_000000_create_articles_table | |
方法 | 駝峰 | getAll | |
資源類方法 | 文檔 | store | |
測試類方法 | 駝峰 | testGuestCannotSeeArticle | |
變量 | 駝峰 | $articlesWithAuthor | |
集合 | 復(fù)數(shù) | $activeUsers = User::active()->get() | |
對象 | 單數(shù) | $activeUser = User::active()->first() | |
配置和語言文件索引 | 下劃線 | articles_enabled | |
視圖 | 下劃線 | show_filtered.blade.php | |
配置 | 下劃線 | google_calendar.php | |
契約(接口) | 形容詞或名詞 | Authenticatable | |
Trait | 形容詞 | Notifiable |
使用縮寫或可讀性更好的語法
壞代碼:
$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 容器或門面
自己創(chuàng)建新的類會(huì)導(dǎo)致代碼耦合度高晌涕,且難于測試滋捶,取而代之地,我們可以使用 IoC 容器或門面渐排。
壞代碼:
$user = new User;
$user->create($request->all());
好代碼:
public function __construct(User $user)
{
$this->user = $user;
}
....
$this->user->create($request->all());
不要從直接從 .env 獲取數(shù)據(jù)
代碼示例
.env
文件中設(shè)置:
CDN_DOMAIN=cdndomain.com
config/app.php
文件中設(shè)置:
'cdn_domain' => env('CDN_DOMAIN', null),
程序中兩種獲取 相同配置
的方法:
env('CDN_DOMAIN')
config('app.cdn_domain')
在此統(tǒng)一規(guī)定:所有程序配置信息 必須 通過 config()
來讀取炬太,所有的 .env
配置信息 必須 通過 config()
來讀取,絕不 在配置文件以外的范圍使用 env()
驯耻。
傳遞數(shù)據(jù)到配置文件然后使用 config
輔助函數(shù)獲取數(shù)據(jù)亲族。
壞代碼:
$apiKey = env('API_KEY');
好代碼:
// config/api.php
'key' => env('API_KEY'),
// Use the data
$apiKey = config('api.key');
有何優(yōu)勢
這樣做主要有以下幾個(gè)優(yōu)勢:
- 定義分明炒考,
config()
是配置信息,env()
只是用來區(qū)分不同環(huán)境霎迫; - 統(tǒng)一放置于
config
中還可以利用框架的 配置信息緩存功能 來提高運(yùn)行效率斋枢; - 代碼健壯性,
config()
在env()
之上多出來一個(gè)抽象層知给,會(huì)使代碼更加健壯瓤帚,更加靈活。
以標(biāo)準(zhǔn)格式存儲(chǔ)日期
使用訪問器和修改器來編輯日期格式涩赢。
壞代碼:
{{ 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 }}
其他好的實(shí)踐
不要把任何業(yè)務(wù)邏輯寫到路由文件中戈次。
在 Blade 模板中盡量不要編寫原生 PHP。