多國語言列表:
Nederlands (by Protoqol)
한국어 (by cherrypick)
Українська (by Tenevyk)
فارسی (by amirhossein baghaie)
Tiếng Việt (by Chung Nguyễn)
Español (by César Escudero)
Français (by Mikayil S.)
Polski (by Maciej Jeziorski)
Deutsch (by Sujal Patel)
Italiana (by Sujal Patel)
العربية (by ahmedsaoud31)
這並非laravel官方強制要求的規範,而是我們在日常開發過程中遇到的一些容易忽視的優秀實作方式。
註釋你的程式碼,但是更優雅的做法是使用描述性的語言來編寫你的程式碼
不要把 JS 和 CSS 放到 Blade 模板中,也不要把任何 HTML 程式碼放到 PHP 程式碼裡
一個類別和一個方法應該只有一個責任。
例如:
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 isVerifiedClient()
{
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模型或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();
}
}
把驗證規則放到 Request 類別中.
例子:
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');
}
}
}
盡可能重用程式碼,SRP可以幫助您避免重覆造輪子。 此外盡量重覆使用Blade模板,使用Eloquent的 scopes 方法來實作程式碼。
例子:
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還有非常優雅的內建工具,如軟刪除,事件,範圍等。
例子:
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->validated());
例子 (對於100個用戶,將執行101次DB查詢):
@foreach (User::all() as $user)
{{ $user->profile->name }}
@endforeach
更優的寫法 (對於100個用戶,使用以下寫法只需執行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())
例子:
let article = `{{ json_encode($article) }}`;
更好的寫法:
<input id="article" type="hidden" value='@json($article)'>
Or
<button class="js-fav-article" data-article='@json($article)'>{{ $article->name }}<button>
在Javascript文件中加上:
let article = $('#article').val();
當然最好的辦法還是使用專業的PHP的JS包傳輸資料。
例子:
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社群獲得什麽幫助。 不要讓你的客戶為額外的問題付錢。
想要實作的功能 | 標準工具 | 第三方工具 |
---|---|---|
權限 | Policies | Entrust, Sentinel 或者其他擴展包 |
資源編譯工具 | Laravel Mix | Grunt, Gulp, 或者其他第三方包 |
開發環境 | Homestead | Docker |
部署 | Laravel Forge | Deployer 或者其他解決方案 |
自動化測試 | PHPUnit, Mockery | Phpspec |
頁面預覽測試 | Laravel Dusk | Codeception |
DB操縱 | Eloquent | SQL, Doctrine |
模板 | Blade | Twig |
資料操縱 | Laravel集合 | 陣列 |
表單驗證 | Request classes | 他第三方包,甚至在控制器中做驗證 |
權限 | Built-in | 他第三方包或者你自己解決 |
API身份驗證 | Laravel Passport | 第三方的JWT或者 OAuth 擴展包 |
創建 API | Built-in | Dingo API 或者類似的擴展包 |
創建資料庫結構 | Migrations | 直接用 DB 語句創建 |
本土化 | Built-in | 第三方包 |
實時消息隊列 | Laravel Echo, Pusher | 使用第三方包或者直接使用WebSockets |
創建測試資料 | Seeder classes, Model Factories, Faker | 手動創建測試資料 |
任務調度 | Laravel Task Scheduler | 腳本和第三方包 |
資料庫 | MySQL, PostgreSQL, SQLite, SQL Server | MongoDB |
來源 PSR standards.
另外,遵循Laravel社群認可的命名規範:
對象 | 規則 | 更優的寫法 | 應避免的寫法 |
---|---|---|---|
控制器 | 單數 | ArticleController | |
路由 | 覆數 | articles/1 | |
路由命名 | 帶點符號的蛇形命名 | users.show_active | |
模型 | 單數 | User | |
hasOne或belongsTo關系 | 單數 | articleComment | |
所有其他關系 | 覆數 | articleComments | |
表單 | 覆數 | article_comments | |
透視表 | 按字母順序排列模型 | article_user | |
資料表字段 | 使用蛇形並且不要帶表名 | meta_title | |
模型參數 | 蛇形命名 | $model->created_at | |
外鍵 | 帶有_id後綴的單數模型名稱 | article_id | |
主鍵 | - | id | |
遷移 | - | 2017_01_01_000000_create_articles_table | |
方法 | 駝峰命名 | getAll | |
資源控制器 | table | store | |
測試類別 | 駝峰命名 | testGuestCannotSeeArticle | |
變量 | 駝峰命名 | $articlesWithAuthor | |
集合 | 描述性的, 覆數的 | $activeUsers = User::active()->get() | |
對象 | 描述性的, 單數的 | $activeUser = User::active()->first() | |
配置和語言文件索引 | 蛇形命名 | articles_enabled | |
視圖 | 短橫線命名 | show-filtered.blade.php | |
配置 | 蛇形命名 | google_calendar.php | |
內容 (interface) | 形容詞或名詞 | 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) ? null : $object->relation->id |
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容器或注入來實作。
例子:
$user = new User;
$user->create($request->validated());
更優的寫法:
public function __construct(User $user)
{
$this->user = $user;
}
....
$this->user->create($request->validated());
將資料傳遞給配置文件,然後使用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 getSomeDateAttribute($date)
{
return $date->format('m-d');
}
// View
{{ $object->ordered_at->toDateString() }}
{{ $object->ordered_at->some_date }}
永遠不要在路由文件中放任何的邏輯程式碼。
盡量不要在Blade模板中寫原始 PHP 程式碼。