咱們這裏要討論的並非 Laravel 版的 SOLID 原則(想要了解更多 SOLID 原則細節查看這篇文章)亦或是設計模式,而是 Laravel 實際開發中容易被忽略的最佳實踐。php
.env
文件獲取數據一個類和方法只負責一項職責。html
壞代碼:前端
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; } }
好代碼:laravel
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 類。json
壞代碼:設計模式
public function index() { $clients = Client::verified() ->with(['orders' => function ($q) { $q->where('created_at', '>', Carbon::today()->subWeek()); }]) ->get(); return view('index', ['clients' => $clients]); }
好代碼:api
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', ]); .... }
好代碼:session
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'); } } }
儘量複用代碼,單一職責原則能夠幫助你避免重複,此外,儘量複用 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 還提供了強大的內置工具如軟刪除、事件、做用域等。
壞代碼:
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());
壞代碼:
@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())
不要把 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'));
優先使用 Laravel 內置功能和社區版擴展包,其次纔是第三方擴展包和工具。這樣作的好處是下降之後的學習和維護成本。
任務 | 標準工具 | 第三方工具 |
---|---|---|
受權 | 策略類 | Entrust、Sentinel等 |
編譯資源 | Laravel Mix | Grunt、Gulp等 |
開發環境 | Homestead | Docker |
部署 | Laravel Forge | Deployer等 |
單元測試 | PHPUnit、Mockery | Phpspec |
瀏覽器測試 | Laravel Dusk | Codeception |
DB | Eloquent | SQL、Doctrine |
模板 | Blade | Twig |
處理數據 | Laravel集合 | 數組 |
表單驗證 | 請求類 | 第三方擴展包、控制器中驗證 |
認證 | 內置功能 | 第三方擴展包、你本身的解決方案 |
API認證 | Laravel Passport | 第三方 JWT 和 OAuth 擴展包 |
建立API | 內置功能 | Dingo API和相似擴展包 |
處理DB結構 | 遷移 | 直接操做DB |
本地化 | 內置功能 | 第三方工具 |
實時用戶接口 | Laravel Echo、Pusher | 第三方直接處理 WebSocket的擴展包 |
生成測試數據 | 填充類、模型工廠、Faker | 手動建立測試數據 |
任務調度 | Laravel Task Scheduler | 腳本或第三方擴展包 |
DB | MySQL、PostgreSQL、SQLite、SQL Server | MongoDB |
遵循 PSR 標準。此外,還要遵循 Laravel 社區版的命名約定:
What | How | Good | Bad |
---|---|---|---|
控制器 | 單數 | ArticleController | |
路由 | 複數 | articles/1 | |
命名路由 | 下劃線+'.'號分隔 | users.show_active | |
模型 | 單數 | User | |
一對一關聯 | 單數 | articleComment | |
其餘關聯關係 | 複數 | articleComments | |
數據表 | 複數 | article_comments | |
中間表 | 按字母表排序的單數格式 | article_user | |
表字段 | 下劃線,不帶模型名 | meta_title | |
外鍵 | 單數、帶_id後綴 | article_id | |
主鍵 | - | id | |
遷移 | - | 2017_01_01_000000_create_articles_table | |
方法 | 駝峯 | getAll | |
資源類方法 | 文檔 | store | |
測試類方法 | 駝峯 | testGuestCannotSeeArticle | |
變量 | 駝峯 | $articlesWithAuthor | |
集合 | 複數 | $activeUsers = User::active()->get() | |
對象 | 單數 | $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 容器或門面。
壞代碼:
$user = new User; $user->create($request->all());
好代碼:
public function __construct(User $user) { $this->user = $user; } .... $this->user->create($request->all());
傳遞數據到配置文件而後使用 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