![Laravel best practices](/images/logo-english.png?raw=true)
You might also want to check out the [real-world Laravel example application](https://github.com/alexeymezenin/laravel-realworld-example-app) and [Eloquent SQL reference](https://github.com/alexeymezenin/eloquent-sql-reference)
Translations:
[Nederlands](https://github.com/Protoqol/Beste-Laravel-Praktijken) (by [Protoqol](https://github.com/Protoqol))
[Indonesia](indonesia.md) (by [P0rguy](https://github.com/p0rguy), [Doni Ahmad](https://github.com/donyahmd))
[한국어](https://github.com/xotrs/laravel-best-practices) (by [cherrypick](https://github.com/xotrs))
[日本語](japanese.md) (by [2bo](https://github.com/2bo))
[简体中文](chinese.md) (by [xiaoyi](https://github.com/Shiloh520))
[繁體中文](traditional-chinese.md) (by [woeichern](https://github.com/woeichern))
[ภาษาไทย](thai.md) (by [kongvut sangkla](https://github.com/kongvut))
[বাংলা](bangla.md) (by [Anowar Hossain](https://github.com/AnowarCST))
[فارسی](persian.md) (by [amirhossein baghaie](https://github.com/ohmydevops))
[Português](https://github.com/jonaselan/laravel-best-practices) (by [jonaselan](https://github.com/jonaselan))
[Українська](ukrainian.md) (by [Tenevyk](https://github.com/tenevyk))
[Русский](russian.md)
[Tiếng Việt](https://chungnguyen.xyz/posts/code-laravel-lam-sao-cho-chuan) (by [Chung Nguyễn](https://github.com/nguyentranchung))
[Español](spanish.md) (by [César Escudero](https://github.com/cedaesca))
[Français](french.md) (by [Mikayil S.](https://github.com/mikayilsrt))
[Polski](polish.md) (by [Karol Pietruszka](https://github.com/pietrushek))
[Română](romanian.md) (by [als698](https://github.com/als698))
[Türkçe](turkish.md) (by [Burak](https://github.com/ikidnapmyself))
[Deutsch](german.md) (by [Sujal Patel](https://github.com/sujalpatel2209))
[Italiana](italian.md) (by [Sujal Patel](https://github.com/sujalpatel2209))
[Azərbaycanca](https://github.com/Maharramoff/laravel-best-practices-az) (by [Maharramoff](https://github.com/Maharramoff))
[العربية](arabic.md) (by [ahmedsaoud31](https://github.com/ahmedsaoud31))
[اردو](urdu.md) (by [RizwanAshraf1](https://github.com/RizwanAshraf1))
[မြန်မာဘာသာ](burmese.md) (by [Kaung Zay Yan](https://github.com/KaungZayY))
[![Laravel example app](/images/laravel-real-world-banner.png?raw=true)](https://github.com/alexeymezenin/laravel-realworld-example-app)
## Contents
[Single responsibility principle](#single-responsibility-principle)
[Methods should do just one thing](#methods-should-do-just-one-thing)
[Fat models, skinny controllers](#fat-models-skinny-controllers)
[Validation](#validation)
[Business logic should be in service class](#business-logic-should-be-in-service-class)
[Don't repeat yourself (DRY)](#dont-repeat-yourself-dry)
[Prefer to use Eloquent over using Query Builder and raw SQL queries. Prefer collections over arrays](#prefer-to-use-eloquent-over-using-query-builder-and-raw-sql-queries-prefer-collections-over-arrays)
[Mass assignment](#mass-assignment)
[Do not execute queries in Blade templates and use eager loading (N + 1 problem)](#do-not-execute-queries-in-blade-templates-and-use-eager-loading-n--1-problem)
[Chunk data for data-heavy tasks](#chunk-data-for-data-heavy-tasks)
[Comment your code, but prefer descriptive method and variable names over comments](#prefer-descriptive-method-and-variable-names-over-comments)
[Do not put JS and CSS in Blade templates and do not put any HTML in PHP classes](#do-not-put-js-and-css-in-blade-templates-and-do-not-put-any-html-in-php-classes)
[Use config and language files, constants instead of text in the code](#use-config-and-language-files-constants-instead-of-text-in-the-code)
[Use standard Laravel tools accepted by community](#use-standard-laravel-tools-accepted-by-community)
[Follow Laravel naming conventions](#follow-laravel-naming-conventions)
[Convention over configuration](#convention-over-configuration)
[Use shorter and more readable syntax where possible](#use-shorter-and-more-readable-syntax-where-possible)
[Use IoC container or facades instead of new Class](#use-ioc-container-or-facades-instead-of-new-class)
[Do not get data from the `.env` file directly](#do-not-get-data-from-the-env-file-directly)
[Store dates in the standard format. Use accessors and mutators to modify date format](#store-dates-in-the-standard-format-use-accessors-and-mutators-to-modify-date-format)
[Do not use DocBlocks](#do-not-use-docblocks)
[Other good practices](#other-good-practices)
### **Single responsibility principle**
A class should have only one responsibility.
Bad:
```php
public function update(Request $request): string
{
$validated = $request->validate([
'title' => 'required|max:255',
'events' => 'required|array:date,type'
]);
foreach ($request->events as $event) {
$date = $this->carbon->parse($event['date'])->toString();
$this->logger->log('Update event ' . $date . ' :: ' . $);
}
$this->event->updateGeneralEvent($request->validated());
return back();
}
```
Good:
```php
public function update(UpdateRequest $request): string
{
$this->logService->logEvents($request->events);
$this->event->updateGeneralEvent($request->validated());
return back();
}
```
[🔝 Back to contents](#contents)
### **Methods should do just one thing**
A function should do just one thing and do it well.
Bad:
```php
public function getFullNameAttribute(): string
{
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:
```php
public function getFullNameAttribute(): string
{
return $this->isVerifiedClient() ? $this->getFullNameLong() : $this->getFullNameShort();
}
public function isVerifiedClient(): bool
{
return auth()->user() && auth()->user()->hasRole('client') && auth()->user()->isVerified();
}
public function getFullNameLong(): string
{
return 'Mr. ' . $this->first_name . ' ' . $this->middle_name . ' ' . $this->last_name;
}
public function getFullNameShort(): string
{
return $this->first_name[0] . '. ' . $this->last_name;
}
```
[🔝 Back to contents](#contents)
### **Fat models, skinny controllers**
Put all DB related logic into Eloquent models.
Bad:
```php
public function index()
{
$clients = Client::verified()
->with(['orders' => function ($q) {
$q->where('created_at', '>', Carbon::today()->subWeek());
}])
->get();
return view('index', ['clients' => $clients]);
}
```
Good:
```php
public function index()
{
return view('index', ['clients' => $this->client->getWithNewOrders()]);
}
class Client extends Model
{
public function getWithNewOrders(): Collection
{
return $this->verified()
->with(['orders' => function ($q) {
$q->where('created_at', '>', Carbon::today()->subWeek());
}])
->get();
}
}
```
[🔝 Back to contents](#contents)
### **Validation**
Move validation from controllers to Request classes.
Bad:
```php
public function store(Request $request)
{
$request->validate([
'title' => 'required|unique:posts|max:255',
'body' => 'required',
'publish_at' => 'nullable|date',
]);
...
}
```
Good:
```php
public function store(PostRequest $request)
{
...
}
class PostRequest extends Request
{
public function rules(): array
{
return [
'title' => 'required|unique:posts|max:255',
'body' => 'required',
'publish_at' => 'nullable|date',
];
}
}
```
[🔝 Back to contents](#contents)
### **Business logic should be in service class**
A controller must have only one responsibility, so move business logic from controllers to service classes.
Bad:
```php
public function store(Request $request)
{
if ($request->hasFile('image')) {
$request->file('image')->move(public_path('images') . 'temp');
}
...
}
```
Good:
```php
public function store(Request $request)
{
$this->articleService->handleUploadedImage($request->file('image'));
...
}
class ArticleService
{
public function handleUploadedImage($image): void
{
if (!is_null($image)) {
$image->move(public_path('images') . 'temp');
}
}
}
```
[🔝 Back to contents](#contents)
### **Don't repeat yourself (DRY)**
Reuse code when you can. SRP is helping you to avoid duplication. Also, reuse Blade templates, use Eloquent scopes etc.
Bad:
```php
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:
```php
public function scopeActive($q)
{
return $q->where('verified', true)->whereNotNull('deleted_at');
}
public function getActive(): Collection
{
return $this->active()->get();
}
public function getArticles(): Collection
{
return $this->whereHas('user', function ($q) {
$q->active();
})->get();
}
```
[🔝 Back to contents](#contents)
### **Prefer to use Eloquent over using Query Builder and raw SQL queries. Prefer collections over arrays**
Eloquent allows you to write readable and maintainable code. Also, Eloquent has great built-in tools like soft deletes, events, scopes etc. You may want to check out [Eloquent to SQL reference](https://github.com/alexeymezenin/eloquent-sql-reference)
Bad:
```sql
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:
```php
Article::has('user.profile')->verified()->latest()->get();
```
[🔝 Back to contents](#contents)
### **Mass assignment**
Bad:
```php
$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:
```php
$category->article()->create($request->validated());
```
[🔝 Back to contents](#contents)
### **Do not execute queries in Blade templates and use eager loading (N + 1 problem)**
Bad (for 100 users, 101 DB queries will be executed):
```blade
@foreach (User::all() as $user)
{{ $user->profile->name }}
@endforeach
```
Good (for 100 users, 2 DB queries will be executed):
```php
$users = User::with('profile')->get();
@foreach ($users as $user)
{{ $user->profile->name }}
@endforeach
```
[🔝 Back to contents](#contents)
### **Chunk data for data-heavy tasks**
Bad:
```php
$users = $this->get();
foreach ($users as $user) {
...
}
```
Good:
```php
$this->chunk(500, function ($users) {
foreach ($users as $user) {
...
}
});
```
[🔝 Back to contents](#contents)
### **Prefer descriptive method and variable names over comments**
Bad:
```php
// Determine if there are any joins
if (count((array) $builder->getQuery()->joins) > 0)
```
Good:
```php
if ($this->hasJoins())
```
[🔝 Back to contents](#contents)
### **Do not put JS and CSS in Blade templates and do not put any HTML in PHP classes**
Bad:
```javascript
let article = `{{ json_encode($article) }}`;
```
Better:
```php
Or