Một class và một method nên có duy nhất một trách nhiệm
Bad:
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;
}
}
Good:
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;
}
Đặt tất cả logic liên quan đến DB vào các Eloquent models hoặc vào các Repository classes nếu bạn đang sử dụng Query Builder hoặc các truy vấn raw SQL.
Bad:
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();
}
}
Di chuyển validation từ controllers sang các 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ột controller phải có một trách nhiệm duy nhất, vì vậy hãy chuyển business logic từ các contoller sang các service class.
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');
}
}
}
Sử dụng lại code khi bạn có thể. SRP đang giúp bạn tránh trùng lặp. Ngoài ra, sử dụng lại các Blade templates, sử dụng Eloquent scopes, v.v.
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();
}
Ưu tiên sử dụng Eloquent hơn là sử dụng QUery Builder và các truy vấn raw SQL. Ưu tiên các collection hơn các array (Prefer to use Eloquent over using Query Builder and raw SQL queries. Prefer collections over arrays)
Eloquent cho phép bạn viết code đọc dễ hiểu và bảo trì. Ngoài ra, Eloquent có các công cụ tích hợp tuyệt vời như xóa mềm(soft deletes), sự kiện(events), phạm vi(scopes), v.v.
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();
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->validated());
Bad (Cho 100 người dùng, 101 truy vấn sẽ được thực hiện):
@foreach (User::all() as $user)
{{ $user->profile->name }}
@endforeach
Good (cho 100 người dùng, 2 truy vấn sẽ được thực hiện):
$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:
let article = `{{ json_encode($article) }}`;
Better:
<input id="article" type="hidden" value='@json($article)'>
Hoặc
<button class="js-fav-article" data-article='@json($article)'>{{ $article->name }}<button>
Trong một javascript file:
let article = $('#article').val();
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'));
Thích sử dụng các built-in và chức năng của Laravel tích hợp thay vì sử dụng các packages và công cụ của bên thứ 3. 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 học các công cụ mới. Ngoài ra, cơ hội nhận được sự 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ứ 3. Đừng bắt khách hàng của bạn trả tiền cho điều đó.
Task | Standard tools | 3rd party tools |
---|---|---|
Authorization | Policies | Entrust, Sentinel and other packages |
Compiling assets | Laravel Mix | Grunt, Gulp, 3rd party packages |
Development Environment | 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 | 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 |
Theo chuẩn PSR standards.
Ngoài ra, hãy làm theo các quy ước đặt tên được chấp nhận bởi cộng đồng Laravel như sau:
What | How | Good | Bad |
---|---|---|---|
Controller | singular | ArticleController | ArticlesController |
Route | plural | articles/1 | article/1 |
Named route | snake_case with dot notation | users.show_active | users.show-active, show-active-users |
Model | singular | User | Users |
hasOne or belongsTo relationship | singular | articleComment | articleComments, article_comment |
All other relationships | plural | articleComments | articleComment, article_comments |
Table | plural | article_comments | article_comment, articleComments |
Pivot table | singular model names in alphabetical order | article_user | user_article, articles_users |
Table column | snake_case without model name | meta_title | MetaTitle; article_meta_title |
Model property | snake_case | $model->created_at | $model->createdAt |
Foreign key | singular model name with _id suffix | article_id | ArticleId, id_article, articles_id |
Primary key | - | id | custom_id |
Migration | - | 2017_01_01_000000_create_articles_table | 2017_01_01_000000_articles |
Method | camelCase | getAll | get_all |
Method in resource controller | table | store | saveArticle |
Method in test class | camelCase | testGuestCannotSeeArticle | test_guest_cannot_see_article |
Variable | camelCase | $articlesWithAuthor | $articles_with_author |
Collection | descriptive, plural | $activeUsers = User::active()->get() | $active, $data |
Object | descriptive, singular | $activeUser = User::active()->first() | $users, $obj |
Config and language files index | snake_case | articles_enabled | ArticlesEnabled; articles-enabled |
View | kebab-case | show-filtered.blade.php | showFiltered.blade.php, show_filtered.blade.php |
Config | snake_case | google_calendar.php | googleCalendar.php, google-calendar.php |
Contract (interface) | adjective or noun | AuthenticationInterface | Authenticatable, IAuthentication |
Trait | adjective | Notifiable | NotificationTrait |
Bad:
$request->session()->get('cart');
$request->input('name');
Good:
session('cart');
$request->name;
More examples:
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') |
Cú pháp new Class tạo ra sự kết hợp chặt chẽ giữa các class và làm phức tạp việc kiểm tra. Sử dụng IoC container hoặc facade thay thế. Khó hiểu nhìn code :v
Bad:
$user = new User;
$user->create($request->validated());
Good:
public function __construct(User $user)
{
$this->user = $user;
}
....
$this->user->create($request->validated());
Đưa dữ liệu vào file config thay vào đó, rồi sau đó dùng chức năng helper config()
để sử dụng dữ liệu trong ứng dụng
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 }}
- Không bao giờ đẩy logic vào file routes
- Giảm thiểu việc sử dụng Vanilla PHP trong Blade templates.
(Thay vì viết {{ csrf_token() }} hoặc @csrf bạn lại đi viết
<?php echo csrf_field(); ?>
)
Source: laravel-best-practice