![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