Khi làm việc nhóm thì coding convention là cực kì quan trọng, nó là rule để mọi người làm việc cùng nhau, coding convesion giúp mọi người trong team hiểu được code của bạn dễ dàng, bài viết này sẽ hướng dẫn về laravel coding convention. Laravel coding conventions được nhiều người sử dụng nhất là Laravel best practices chúng ta cùng tìm hiểu về nó xem sao nhé.
Một lớp hoặc một phương thức chỉ nên có 1 nhiệm vụ duy nhất. Đây là nguyên tắc đầu tiên trong các nguyên tắc SOLID của hướng đối tượng.
// Bad
public function getFullNameAttribute()
{
if ($this->gender) {
$genderText = 'Mr. ';
} else {
$genderText = 'Mrs. ';
}
if (auth()->user() && auth()->user()->hasRole('client') && auth()->user()->isVerified()) {
return $genderText . $this->first_name . ' ' . $this->middle_name . ' ' . $this->last_name;
} else {
return $this->first_name . ' ' . $this->last_name;
}
}
// Good
public function getFullNameAttribute()
{
return $this->isVerifiedClient() ? $this->getFullNameLong() : $this->getFullNameShort();
}
public function isVerifiedClient()
{
// Trả về true or false
return auth()->user() && auth()->user()->hasRole('client') && auth()->user()->isVerified();
}
public function getFullNameLong()
{
return $this->gender_text . $this->first_name . ' ' . $this->middle_name . ' ' . $this->last_name;
}
public function getFullNameShort()
{
return $this->first_name . ' ' . $this->last_name;
}
public function getGenderTextAttribute()
{
// Nếu là nam
if ($this->gender) {
return 'Mr. ';
}
return 'Mrs. ';
}
Việc tách code ra mỗi hàm chỉ có một nhiệm vụ duy nhất giúp code rõ ràng hơn, dễ dàng nâng cấp bảo trì, không bị lặp code.
Các logic lấy dữ liệu phải viết trong model, or repository, controller chỉ việc gọi một dòng duy nhất
// Bad code
public function index()
{
$clients = Client::verified()
->with(['orders' => function ($q) {
$q->where('created_at', '>', Carbon::today()->subWeek());
}])
->get();
return view('index', ['clients' => $clients]);
}
// Good
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();
}
}
Tại sao phải viết 1 hàm lấy dữ liệu ở model riêng vì nó giúp mình tái sử dụng code và logic lấy dữ liệu có thay đổi thì mình chỉ việc sửa ở 1 chỗ là xong.
Chuyển dữ liệu validate từ controller sang Request class
// Bad
public function store(Request $request)
{
$request->validate([
'title' => 'required|unique:posts|max:255',
'body' => 'required',
'publish_at' => 'nullable|date',
]);
....
}
// Good
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',
];
}
}
Mỗi 1 method trong controller chỉ có 1 nhiệm vụ duy nhất, vì vậy các logic nghiệp vụ nên viết vào lớp service
// Bad
public function store(Request $request)
{
if ($request->hasFile('image')) {
$request->file('image')->move(public_path('images') . 'temp');
}
....
}
// Good
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');
}
}
}
Không được phép viết lặp code, code càng tái sử dụng được càng tốt
// Bad
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();
}
// Good
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();
}
Việc sử dụng Eloquent giúp việc đọc code vào maintain code dễ dàng
// Bad
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
// Good
Article::has('user.profile')->verified()->latest()->get();
Tính năng gán dữ liệu hàng loạt, thay vì gán từng giá trị gán hàng loạt sẽ giúp code gọn gàng hơn
// Bad
$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();
// Good
$category->article()->create($request->all());
// Bad
// 100 user có 101 query được thực thi
@foreach (User::all() as $user)
{{ $user->profile->name }}
@endforeach
// Good
// n user cũng chỉ có 2 query được thực thi mà thôi
$users = User::with('profile')->get();
...
@foreach ($users as $user)
{{ $user->profile->name }}
@endforeach
// Bad
if (count((array) $builder->getQuery()->joins) > 0)
// Better
// Determine if there are any joins.
if (count((array) $builder->getQuery()->joins) > 0)
// Good
if ($this->hasJoins())
// Bad
public function isNormal()
{
return $article->type === 'normal';
}
return back()->with('message', 'Your article has been added!');
// Good
public function isNormal()
{
return $article->type === Article::TYPE_NORMAL;
}
return back()->with('message', __('app.article_added'));
các message phải được định nghĩa trong file lang, giúp tái sử dụng và dễ maintain, các hằng số phải viết trong file constant or khay báo trong model, khi chỉ sửa message or hằng số chỉ cần sử 1 chỗ là xong.
Task | Standard tools | 3rd party tools |
---|---|---|
Authorization | Policies | Entrust, Sentinel and other packages |
Compiling assets | Laravel Mix | Grunt, Gulp, 3rd party packages |
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 |
What | How | Good | Bad |
---|---|---|---|
Controller | số ít | ArticleController | |
Route | số nhiều | articles/1 | |
Named route | snake_case với dấu chấm | users.show_active | |
Model | số ít | User | |
hasOne hoặc belongsTo relationship | số ít | articleComment | |
Tất cả các relationships khác | số nhiều | articleComments | |
Tên bảng | số nhiều | article_comments | |
Bảng Pivot | tên 2 bảng số ít, xếp tăng dần | article_user | |
Tên cột trong bảng | snake_case | meta_title | |
Thuộc tính của Model | snake_case | $model->created_at | |
Foreign key - khóa ngoại | tên model, kèm _id đằng sau | article_id | |
Primary key | - | id | |
Migration | - | xxx_create_articles_table | |
Method | camelCase | getAll | |
Method trong resource controller | xem tài liệu | store | |
Method in test class | camelCase | testArticle | |
Variable | camelCase | $articlesWithAuthor | |
Collection | có nghĩa mô tả, số nhiều | $activeUsers | |
Object | có nghĩa mô tả, số ít | $activeUser | |
Config and language files index | snake_case | articles_enabled | |
View | kebab_case | show-filtered.blade.php | |
Config | snake_case | google_calendar.php | |
Contract (interface) | tính từ hoặc danh từ | Authenticatable | |
Trait | tính từ | Notifiable |
// Bad
$request->session()->get('cart');
$request->input('name');
// Good
session('cart');
$request->name;
Common syntax | Shorter 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') |
// Bad
$user = new User;
$user->create($request->validated());
// Good
public function __construct(User $user)
{
$this->user = $user;
}
....
$this->user->create($request->validated());
// Bad
$apiKey = env('API_KEY');
// Good
// config/api.php
'key' => env('API_KEY'),
// Use the data
$apiKey = config('api.key');
// Bad
{{ Carbon::createFromFormat('Y-d-m H-i', $object->ordered_at)->toDateString() }}
{{ Carbon::createFromFormat('Y-d-m H-i', $object->ordered_at)->format('m-d') }}
// Good
// 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 }}
Như vậy là chúng ta đã tìm hiểu xong các quy tắc code với project laravel, nhớ sử dụng các quy tắc này nhé, code của chúng ta sẽ ngắn gọn và dễ đọc hơn nhiều đó :D