0% found this document useful (0 votes)
39 views13 pages

3.best PractiseGitHub - Alexeymezenin - Laravel-Best-Practices - Laravel Best Practices

This document describes a Laravel best practices repository on GitHub. It contains sections on single responsibility principle, methods doing one thing, fat models and skinny controllers, validation, business logic in services, DRY principles, and other Laravel development best practices.

Uploaded by

Antonius Ajalah
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
39 views13 pages

3.best PractiseGitHub - Alexeymezenin - Laravel-Best-Practices - Laravel Best Practices

This document describes a Laravel best practices repository on GitHub. It contains sections on single responsibility principle, methods doing one thing, fat models and skinny controllers, validation, business logic in services, DRY principles, and other Laravel development best practices.

Uploaded by

Antonius Ajalah
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 13

alexeymezenin / laravel-best-practices Public

Laravel best practices

10.6k stars 2.3k forks Branches Tags Activity

Star Notifications

Code Issues 26

master 1 Branch 0 Tags Go to file Go to file Code

alexeymezenin Added link to Eloquent to SQL reference 3 months ago

images Added banner 3 years ago

README.md Added link to Eloquent to SQL reference 3 months ago

arabic.md $dates property is deprecated, use $casts inste… 10 months ago

bangla.md $dates property is deprecated, use $casts inste… 10 months ago

chinese.md $dates property is deprecated, use $casts inste… 10 months ago

french.md fix: links on french doc 9 months ago

german.md Merge pull request #166 from datlechin/master 9 months ago

indonesia.md $dates property is deprecated, use $casts inste… 10 months ago

italian.md $dates property is deprecated, use $casts inste… 10 months ago

japanese.md $dates property is deprecated, use $casts inste… 10 months ago

persian.md Update persian.md 4 months ago

polish.md Fix typo 3 months ago

romanian.md Create romanian.md 9 months ago

russian.md Added link to Eloquent to SQL reference 3 months ago

spanish.md $dates property is deprecated, use $casts inste… 10 months ago

thai.md $dates property is deprecated, use $casts inste… 10 months ago

traditional-chinese.md $dates property is deprecated, use $casts inste… 10 months ago

turkish.md $dates property is deprecated, use $casts inste… 10 months ago

ukrainian.md $dates property is deprecated, use $casts inste… 10 months ago

urdu.md $dates property is deprecated, use $casts inste… 10 months ago

README

You might also want to check out the real-world Laravel example application and Eloquent SQL reference

Translations:
Nederlands (by Protoqol)

Indonesia (by P0rguy, Doni Ahmad)

한국어 (by cherrypick)


日本語 (by 2bo)
简体中文 (by xiaoyi)
繁體中文 (by woeichern)
ภาษาไทย (by kongvut sangkla)

বাংলা (by Anowar Hossain)

)by amirhossein baghaie( ‫فارسی‬

Português (by jonaselan)

Українська (by Tenevyk)

Русский

Tiếng Việt (by Chung Nguyễn)

Español (by César Escudero)

Français (by Mikayil S.)

Polski (by Karol Pietruszka)

Română (by als698)

Türkçe (by Burak)

Deutsch (by Sujal Patel)

Italiana (by Sujal Patel)

Azərbaycanca (by Maharramoff)

)by ahmedsaoud31( ‫العربية‬

)by RizwanAshraf1( ‫اردو‬

Contents

Single responsibility principle

Methods should do just one thing

Fat models, skinny controllers

Validation

Business logic should be in service class

Don't repeat yourself (DRY)

Prefer to use Eloquent over using Query Builder and raw SQL queries. Prefer collections over arrays

Mass assignment

Do not execute queries in Blade templates and use eager loading (N + 1 problem)

Chunk data for data-heavy tasks

Comment your code, but 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
Use config and language files, constants instead of text in the code

Use standard Laravel tools accepted by community

Follow Laravel naming conventions

Convention over configuration

Use shorter and more readable syntax where possible

Use IoC container or facades instead of new Class

Do not get data from the .env file directly

Store dates in the standard format. Use accessors and mutators to modify date format

Do not use DocBlocks

Other good practices

Single responsibility principle


A class should have only one responsibility.

Bad:

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:

public function update(UpdateRequest $request): string


{
$this->logService->logEvents($request->events);

$this->event->updateGeneralEvent($request->validated());

return back();
}

🔝 Back to contents
Methods should do just one thing
A function should do just one thing and do it well.

Bad:

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:
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
Fat models, skinny controllers
Put all DB related logic into Eloquent models.

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(): Collection
{
return $this->verified()
->with(['orders' => function ($q) {
$q->where('created_at', '>', Carbon::today()->subWeek());
}])
->get();
}
}

🔝 Back to contents
Validation
Move validation from controllers to Request classes.

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(): array
{
return [
'title' => 'required|unique:posts|max:255',
'body' => 'required',
'publish_at' => 'nullable|date',
];
}
}

🔝 Back to 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:

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): void
{
if (!is_null($image)) {
$image->move(public_path('images') . 'temp');
}
}
}

🔝 Back to 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:

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', 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
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

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();

🔝 Back to contents
Mass assignment
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());
🔝 Back to 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):

@foreach (User::all() as $user)


{{ $user->profile->name }}
@endforeach

Good (for 100 users, 2 DB queries will be executed):

$users = User::with('profile')->get();

@foreach ($users as $user)


{{ $user->profile->name }}
@endforeach

🔝 Back to contents
Chunk data for data-heavy tasks
Bad:

$users = $this->get();

foreach ($users as $user) {


...
}

Good:

$this->chunk(500, function ($users) {


foreach ($users as $user) {
...
}
});

🔝 Back to contents
Prefer descriptive method and variable names over comments
Bad:

// Determine if there are any joins


if (count((array) $builder->getQuery()->joins) > 0)

Good:

if ($this->hasJoins())

🔝 Back to contents
Do not put JS and CSS in Blade templates and do not put any HTML in PHP classes
Bad:

let article = `{{ json_encode($article) }}`;

Better:

<input id="article" type="hidden" value='@json($article)'>

Or
<button class="js-fav-article" data-article='@json($article)'>{{ $article->name }}<button>

In a Javascript file:

let article = $('#article').val();

The best way is to use specialized PHP to JS package to transfer the data.

🔝 Back to contents
Use config and language files, constants instead of text in the code
Bad:

public function isNormal(): bool


{
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'));

🔝 Back to contents
Use standard Laravel tools accepted by community
Prefer to use built-in Laravel functionality and community packages instead of using 3rd party packages and tools. Any developer who will
work with your app in the future will need to learn new tools. Also, chances to get help from the Laravel community are significantly lower when
you're using a 3rd party package or tool. Do not make your client pay for that.

Task Standard tools 3rd party tools

Authorization Policies Entrust, Sentinel and other packages

Compiling assets Laravel Mix, Vite Grunt, Gulp, 3rd party packages

Development Environment Laravel Sail, Homestead Docker

Deployment Laravel Forge Deployer and other solutions

Unit testing PHPUnit, Mockery Phpspec, Pest

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 Standard tools 3rd party tools

Task scheduling Laravel Task Scheduler Scripts and 3rd party packages

DB MySQL, PostgreSQL, SQLite, SQL Server MongoDB

🔝 Back to contents
Follow Laravel naming conventions
Follow PSR standards.

Also, follow naming conventions accepted by Laravel community:

What How Good Bad

Controller singular ArticleController ArticlesController

Route plural articles/1 article/1

snake_case with dot


Route name users.show_active users.show-active, show-active-users
notation

Model singular User Users

hasOne or
belongsTo singular articleComment articleComments, article_comment
relationship

All other
plural articleComments articleComment, article_comments
relationships

Table plural article_comments article_comment, articleComments

singular model names


Pivot table article_user user_article, articles_users
in alphabetical order

snake_case without
Table column meta_title MetaTitle; article_meta_title
model name

Model property snake_case $model->created_at $model->createdAt

singular model name


Foreign key article_id ArticleId, id_article, articles_id
with _id suffix

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
table store saveArticle
controller

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


snake_case articles_enabled ArticlesEnabled; articles-enabled
files index

showFiltered.blade.php,
View kebab-case show-filtered.blade.php
show_filtered.blade.php

googleCalendar.php, google-
Config snake_case google_calendar.php
calendar.php

Contract (interface) adjective or noun AuthenticationInterface Authenticatable, IAuthentication

Trait adjective Notifiable NotificationTrait

Trait (PSR) adjective NotifiableTrait Notification

Enum singular UserType UserTypes, UserTypeEnum


What How Good Bad

UpdateUserFormRequest,
FormRequest singular UpdateUserRequest
UserFormRequest, UserRequest

Seeder singular UserSeeder UsersSeeder

🔝 Back to contents
Convention over configuration
As long as you follow certain conventions, you do not need to add additional configuration.

Bad:

// Table name 'Customer'


// Primary key 'customer_id'
class Customer extends Model
{
const CREATED_AT = 'created_at';
const UPDATED_AT = 'updated_at';

protected $table = 'Customer';


protected $primaryKey = 'customer_id';

public function roles(): BelongsToMany


{
return $this->belongsToMany(Role::class, 'role_customer', 'customer_id', 'role_id');
}
}

Good:

// Table name 'customers'


// Primary key 'id'
class Customer extends Model
{
public function roles(): BelongsToMany
{
return $this->belongsToMany(Role::class);
}
}

🔝 Back to contents
Use shorter and more readable syntax where possible
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()


Common syntax Shorter and more readable syntax

optional($object->relation)->id (in PHP 8: $object-


is_null($object->relation) ? null : $object->relation->id
>relation?->id )

return view('index')->with('title', $title)-


return view('index', compact('title', 'client'))
>with('client', $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')

🔝 Back to contents
Use IoC / Service container instead of new Class
new Class syntax creates tight coupling between classes and complicates testing. Use IoC container or facades instead.

Bad:

$user = new User;


$user->create($request->validated());

Good:

public function __construct(User $user)


{
$this->user = $user;
}

...

$this->user->create($request->validated());

🔝 Back to contents
Do not get data from the .env file directly
Pass the data to config files instead and then use the config() helper function to use the data in an application.

Bad:

$apiKey = env('API_KEY');

Good:

// config/api.php
'key' => env('API_KEY'),

// Use the data


$apiKey = config('api.key');

🔝 Back to contents
Store dates in the standard format. Use accessors and mutators to modify date format
A date as a string is less reliable than an object instance, e.g. a Carbon-instance. It's recommended to pass Carbon objects between classes
instead of date strings. Rendering should be done in the display layer (templates):

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 $casts = [
'ordered_at' => 'datetime',
];

// Blade view
{{ $object->ordered_at->toDateString() }}
{{ $object->ordered_at->format('m-d') }}

🔝 Back to contents
Do not use DocBlocks
DocBlocks reduce readability. Use a descriptive method name and modern PHP features like return type hints instead.

Bad:

/**
* The function checks if given string is a valid ASCII string
*
* @param string $string String we get from frontend which might contain
* illegal characters. Returns True is the string
* is valid.
*
* @return bool
* @author John Smith
*
* @license GPL
*/

public function checkString($string)


{
}

Good:

public function isValidAsciiString(string $string): bool


{
}

🔝 Back to contents
Other good practices
Avoid using patterns and tools that are alien to Laravel and similar frameworks (i.e. RoR, Django). If you like Symfony (or Spring) approach for
building apps, it's a good idea to use these frameworks instead.

Never put any logic in routes files.

Releases

No releases published

Packages

No packages published
Contributors 67

+ 53 contributors

You might also like