Single Responsibility Principle – Một Nhiệm Vụ Duy Nhất
Một lớp hay phương thức chỉ nên thực hiện một nhiệm vụ duy nhất
Không Nên:
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; } }
Nên Code như vậy:
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; }
Fat Models, Skinny Controllers – Code vào Models, Giảm Thiểu Controller
Đặt tất cả các logic liên quan đến Database vào các Eloquent Models hoặc Respository Class.
Không Nên:
public function index() { $clients = Client::verified() ->with(['orders' => function ($q) { $q->where('created_at', '>', Carbon::today()->subWeek()); }]) ->get(); return view('index', ['clients' => $clients]); }
Nên viết như vậy:
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(); } }
Validation – Kiểm Tra Lỗi
Nên kiểm tra lỗi bằng cách tạo class Request, thay vì kiểm tra lỗi trong Controller.
Không Nên:
public function store(Request $request) { $request->validate([ 'title' => 'required|unique:posts|max:255', 'body' => 'required', 'publish_at' => 'nullable|date', ]); .... }
Nên viết như vậy:
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', ]; } }
Business Logic Should Be in Service Class – Business Login nên viết trong Service Class
Một Controller chỉ chịu trách nhiệm làm một việc gì đó, vì vậy hãy viết những code liên quan đến logic business vào Service Class.
Không Nên:
public function store(Request $request) { if ($request->hasFile('image')) { $request->file('image')->move(public_path('images') . 'temp'); } .... }
Nên viết như vậy:
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'); } } }
Don’t Repeat Yourself (DRY) – Tránh Lặp Code
Tái sử dụng code nếu có thể. SRP giúp bạn tránh lặp code. Thêm vào đó, tái sử dụng blade templates, sử dụng Eloquent scopes,…
Không Nên:
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(); }
Nên viết như vậy:
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(); }
Prefer to Use Eloquent Over Using Query Builder and Raw SQL Queries. Prefer Collections Over Arrays – Sử Dụng Eloquent Thay Vì Query Builder Hay RAW SQL Query
Eloquent cho phép bạn viết code dễ đọc và dễ bảo trì. Ngoài ra, Eloquent có các công cụ tích hợp tuyệt vời như softdelete, events, scopes,…
Không Nên:
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
Nên viết như vậy cho ngắn gọn:
Article::has('user.profile')->verified()->latest()->get();
Mass assignment
Không Nên:
$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();
Nên làm như vậy:
$category->article()->create($request->validated());
Do Not Execute Queries in Blade Templates and Use Eager Loading (N + 1 Problem)
Không Nên (Nếu có 100 users, 101 DB queries sẽ thực hiện)
@foreach (User::all() as $user) {{ $user->profile->name }} @endforeach
Nên viết như vậy (Nếu có 100 users. 2 DB queries sẽ thực hiện)
$users = User::with('profile')->get(); ... @foreach ($users as $user) {{ $user->profile->name }} @endforeach
Comment Your Code, but Prefer Descriptive Method and Variable Names Over Comments – Comment Code Đúng Cách
Không Nên:
if (count((array) $builder->getQuery()->joins) > 0)
Tốt hơn một chút:
// Determine if there are any joins. if (count((array) $builder->getQuery()->joins) > 0)
Hoặc là như vậy cho ngắn gọn:
if ($this->hasJoins())
Do Not Put Js and CSS in Blade Templates and Do Not Put Any HTML in PHP Classes – Không Nên Viết JS và CSS Vào Blade Templates Và Không Nên Viết HTML vào PHP Class
Không Nên:
let article = `{{ json_encode($article) }}`;
Nên viết như vậy:
<input id="article" type="hidden" value='@json($article)'> Or <button class="js-fav-article" data-article='@json($article)'>{{ $article->name }}<button>
Trong Javascript file:
let article = $('#article').val();
Use Config and Language Files, Constants Instead of Text in the Code – Sử Dụng Cấu Hình Và Tệp Ngôn Ngữ, Hằng Số Thay Vì Nhập Text Vào Code
Không Nên:
public function isNormal() { return $article->type === 'normal'; } return back()->with('message', 'Your article has been added!');
Nên viết như vậy:
public function isNormal() { return $article->type === Article::TYPE_NORMAL; } return back()->with('message', __('app.article_added'));
Use Standard Laravel Tools Accepted by Community – Sử Dụng Các Công Cụ Chuẩn Laravel Được Cộng Đồng Chấp Nhận
Ưu tiên dụng các gói cộng đồng và chức năng Laravel tích hợp sẵn thay vì sử dụng các gói và công cụ của bên thứ ba. Bất kỳ nhà phát triển nào sẽ làm việc với ứng dụng của bạn trong tương lai sẽ cần tìm hiểu các công cụ mới. Ngoài ra, cơ hội nhận được trợ giúp từ cộng đồng Laravel thấp hơn đáng kể khi bạn đang sử dụng gói hoặc công cụ của bên thứ ba.
Task | Standard tools | 3rd party tools |
---|---|---|
Authorization | Policies | Entrust, Sentinel and other packages |
Compiling assets | Laravel Mix | Grunt, Gulp, 3rd party packages |
Development Environment | Laravel Sail, Homestead | Docker |
Deployment | Laravel Forge | Deployer and other solutions |
Unit testing | PHPUnit, Mockery | Phpspec |
Browser testing | Laravel Dusk | Codeception |
DB | Eloquent | SQL, Doctrine |
Templates | Blade | Twig |
Working with data | Laravel collections | Arrays |
Form validation | Request classes | 3rd party packages, validation in controller |
Authentication | Built-in | 3rd party packages, your own solution |
API authentication | Laravel Passport, Laravel Sanctum | 3rd party JWT and OAuth packages |
Creating API | Built-in | Dingo API and similar packages |
Working with DB structure | Migrations | Working with DB structure directly |
Localization | Built-in | 3rd party packages |
Realtime user interfaces | Laravel Echo, Pusher | 3rd party packages and working with WebSockets directly |
Generating testing data | Seeder classes, Model Factories, Faker | Creating testing data manually |
Task scheduling | Laravel Task Scheduler | Scripts and 3rd party packages |
DB | MySQL, PostgreSQL, SQLite, SQL Server | MongoDB |
Follow Laravel Naming Conventions – Quy Ước Đặt Tên
Follow theo PSR Standards. Ngoài ra, hãy tuân theo các quy ước đặt tên được chấp nhận bởi cộng đồng Laravel:
What | How | Good | Bad |
---|---|---|---|
Controller | singular | ArticleController | |
Route | plural | articles/1 | |
Named route | snake_case with dot notation | users.show_active | |
Model | singular | User | |
hasOne or belongsTo relationship | singular | articleComment | |
All other relationships | plural | articleComments | |
Table | plural | article_comments | |
Pivot table | singular model names in alphabetical order | article_user | |
Table column | snake_case without model name | meta_title | |
Model property | snake_case | $model->created_at | |
Foreign key | singular model name with _id suffix | article_id | |
Primary key | – | id | |
Migration | – | 2017_01_01_000000_create_articles_table | |
Method | camelCase | getAll | |
Method in resource controller | table | store | |
Method in test class | camelCase | testGuestCannotSeeArticle | |
Variable | camelCase | $articlesWithAuthor | |
Collection | descriptive, plural | $activeUsers = User::active()->get() | |
Object | descriptive, singular | $activeUser = User::active()->first() | |
Config and language files index | snake_case | articles_enabled | |
View | kebab-case | show-filtered.blade.php | |
Config | snake_case | google_calendar.php | |
Contract (interface) | adjective or noun | AuthenticationInterface | |
Trait | adjective | Notifiable |
Use Shorter and More Readable Syntax Where Possible – Sử Dụng Cú Pháp Ngắn Gọn Và Dễ Đọc Nếu Có Thể
Không Nên:
$request->session()->get('cart'); $request->input('name');
Nên:
session('cart'); $request->name;
Một vài ví dụ khác:
Common syntax | Shorter and more readable syntax |
---|---|
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') |
Use IoC Container or Facades Instead of New Class – Sử Dụng IoC Container Hoặc Facades Thay Vì Tạo Mới Class
Không Nên:
$user = new User; $user->create($request->validated());
Nên:
public function __construct(User $user) { $this->user = $user; } .... $this->user->create($request->validated());
Do Not Get Data From the .env File Directly – Không Lấy Dữ Liệu Trực Tiếp Từ File .env
Thay vào đó, hãy chuyển dữ liệu vào file cấu hình và sau đó sử dụng hàm trợ giúp config () để sử dụng dữ liệu trong ứng dụng.
Không Nên:
$apiKey = env('API_KEY');
Nên:
// config/api.php 'key' => env('API_KEY'), // Use the data $apiKey = config('api.key');
Store Dates in the Standard Format. Use Accessors and Mutators to Modify Date Format – Lưu Trữ Ngày Ở Định Dạng Chuẩn
Không Nên:
{{ Carbon::createFromFormat('Y-d-m H-i', $object->ordered_at)->toDateString() }} {{ Carbon::createFromFormat('Y-d-m H-i', $object->ordered_at)->format('m-d') }}
Nên làm như vậy:
// 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 }}
Một Vài Phương Pháp Khác
- Không viết logic code trong routes files.
- Giảm thiểu sử dụng vanilla PHP trong blade templates.
Nguồn: https://github.com/alexeymezenin/laravel-best-practices