Best Practices for Laravel

$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;}Fat models, skinny controllersPut all DB related logic into Eloquent models or into Repository classes if you’re using Query Builder or raw SQL queries.

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(); }}ValidationMove 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() { return [ 'title' => 'required|unique:posts|max:255', 'body' => 'required', 'publish_at' => 'nullable|date', ]; }}Business logic should be in service classA 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) { if (!is_null($image)) { $image->move(public_path('images') .

'temp'); } }}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', 1)->whereNotNull('deleted_at');}public function getActive(){ return $this->active()->get();}public function getArticles(){ return $this->whereHas('user', function ($q) { $q->active(); })->get();}Prefer to use Eloquent over using Query Builder and raw SQL queries.

Prefer collections over arraysEloquent allows you to write readable and maintainable code.

Also, Eloquent has great built-in tools like soft deletes, events, scopes etc.

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` DESCGood:Article::has('user.

profile')->verified()->latest()->get();Mass assignmentBad:$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());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 }}@endforeachGood (for 100 users, 2 DB queries will be executed):$users = User::with('profile')->get();.

@foreach ($users as $user) {{ $user->profile->name }}@endforeachComment your code, but prefer descriptive method and variable names over commentsBad: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())Do not put JS and CSS in Blade templates and do not put any HTML in PHP classesBad: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.

Use config and language files, constants instead of text in the codeBad: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'));Use IoC container or facades instead of new Classnew 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());Do not get data from the .

env file directlyPass 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');Store dates in the standard format.

Use accessors and mutators to modify date formatBad:{{ 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:// Modelprotected $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 }}Other good practicesNever put any logic in routes files.

Minimize usage of vanilla PHP in Blade templates.

.. More details

Leave a Reply