Laravel Best Practices You Should Follow
To make Laravel development a breeze, you need to follow the best practices from time to time. Here in this article, I will be taking up the opportunity to discuss some of such best practices that you should follow in your Laravel project.
Best Practices for Laravel Development
Single responsibility principle
A class and a method should have only one responsibility.
Wrong approach:
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;
}
}
Recommended practices:
public function getFullNameAttribute()
{
return $this->isVerifiedClient() ? $this->getFullNameLong() : $this->getFullNameShort();
}
public function isVerfiedClient()
{
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;
}
Powerful model & simple controller
If you use a query constructor or raw SQL to query, put all database related logic into the eloquent model or repository class.
Bad approach:
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();
}
}
The business logic should be in the service class
A controller must have only one responsibility, so the business logic should be moved from the controller to the 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');
}
}
}
Don’t repeat yourself (dry)
Reuse code as much as possible. SRP (single responsibility principle) is helping you avoid duplication. Of course, this also includes the blade template, the scope of eloquent, 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', 1)->whereNotNull('deleted_at');
}public function getActive()
{
return $this->active()->get();
}public function getArticles()
{
return $this->whereHas('user', function ($q) {
$q->active();
})->get();
}
It’s best to use eloquent instead of query builder and native SQL queries.
Eloquent can write readable and maintainable code. In addition, eloquent also has great built-in tools, such as soft deletion, event, scope, etc.
For example, you write:
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
Might as well write:
Article::has('user.profile')->verified()->latest()->get();
Instead of trying to annotate your code, write a descriptive name for a method or variable
Bad:
if (count((array) $builder->getQuery()->joins) > 0)
Good:
//Determine if there are any connections.
if (count((array) $builder->getQuery()->joins) > 0)
best:
if ($this->hasJoins())
Use configuration and language files and constants in your code instead of writing it dead
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'));
Don’t directly from.env
Get data from file
Pass the data to the configuration file, and then use auxiliary functions config()
Use data in your application.
Bad:
$apiKey = env('API_KEY');
Good:
// config/api.php
'key' => env('API_KEY'),// Use the data
$apiKey = config('api.key');
Store the date in a standard format and use accessors and modifiers to modify the date format if necessary
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 getMonthDayAttribute($date)
{
return $date->format('m-d');
}// View
{{ $object->ordered_at->toDateString() }}
{{ $object->ordered_at->monthDay }}
I hope these best practices are going to help you avoid any potential problems while building your application and you benefit from these tips that I discussed in this post. Thank you for reading!
If you have any Laravel-related questions or projects, feel free to reach me on my LinkedIn account & email address.