In the ever-evolving world of web development, Laravel stands as a sturdy foundation, aiding developers in crafting top-notch web applications. However, even with such a potent tool like Laravel at one's disposal, things can sometimes go awry, especially when core basics and best practices are overlooked. Many developers are well-acquainted with SOLID principles, design patterns, and other tried-and-true methods. But what about the nitty-gritty details that often fly under the radar?
This guide goes beyond just SOLID principles and patterns. Here, we focus on the often-ignored best practices in real-world Laravel projects. From naming conventions and readable code to structured data handling and efficient query execution - this guide encompasses it all to ensure your Laravel code not only runs, but runs optimally, maintainably, and future-proofed.
Whether you're just dipping your toes into Laravel or you're a seasoned developer, this guide offers invaluable insights and tips to ensure your code meets the mark and operates at its best. So, let's dive in and explore the Laravel best practices you might've been missing out on.
Table of Contents
- Embracing Domain-Driven Design
- Adhering to Laravel Naming Conventions
- Embracing Concise and Clear Syntax
- Upholding the Single Responsibility Principle
- Crafting Fat Models and Skinny Controllers
- Streamlining Validation with Request Classes
- Centralizing Business Logic in Service Classes
- Implementing DRY (Don't Repeat Yourself) Principles
- Prioritizing Eloquent ORM Over Query Builder and SQL
- Safeguarding Against Mass Assignment Vulnerabilities
- Avoiding Direct Database Queries in Blade Templates
- Minimizing Code Comments Through Self-Describing Names
- Separating JavaScript and CSS from Blade Templates
- Utilizing Config and Language Files for Text Management
- Integrating Community-Endorsed Laravel Tools
- Employing Dependency Injection and Facades Correctly
- Securely Managing Environment Variables with Config Files
- Standardizing Date Storage and Formatting Techniques
- Incorporating Additional Laravel Best Practices
Embracing Domain-Driven Design
At mindtwo, we believe in structuring our Laravel projects to maximize clarity, maintainability, and scalability. This approach is deeply rooted in Domain-Driven Design (DDD), a methodology that focuses on the core business logic and processes. Here, we outline our standard practices for organizing Laravel applications using DDD principles.
Project Structure for New Laravel Projects
In Laravel, organizing your project's directory structure is crucial for maintainability and scalability. The recommended structure for new projects includes two main directories:
"/app/App"-Directory
This directory houses all global and program-specific content. It is the central hub for your Laravel application.
/app/App
├── Console/Commands
├── Exceptions
├── Http
│ ├── Controllers
│ ├── Requests
│ ├── Resources
│ └── Middlewares
├── Nova
├── Policies
├── Providers
└── Views
"/app/Domain"-Directory
The Domain directory contains all domain-specific content, following the principles of Domain-Driven Design (DDD).
/app/Domain/Example/
├── Actions
├── Collections
├── DataTransferObjects
├── Enums
├── Events
├── Exceptions
├── Jobs
├── Listeners
├── Models
├── QueryBuilders
└── Rules
Configuration in `bootstrap/app.php` and `composer.json`
You'll need to configure your `bootstrap/app.php` and `composer.json` to reflect this structure:
bootstrap/app.php:
/*
|--------------------------------------------------------------------------
| Custom app directory
|--------------------------------------------------------------------------
*/
$app->useAppPath(realpath(__DIR__.'/../app/App'));
composer.json:
{
"autoload": {
"psr-4": {
"App\\": "app/App/",
"Domain\\": "app/Domain/",
"Database\\Factories\\": "database/factories/",
"Database\\Seeders\\": "database/seeders/"
}
}
}
Class Responsibility and Structure
Controller and Request Structure
- Controllers for web output are located directly in the Controllers folder.
- API-related controllers are bundled in an Api folder.
- Request classes mirror the folder structure of the Controller directory.
Query Builder vs. Model Scopes
- There's no hard rule; it's a matter of discretion.
- For simple cases, 1-2 scopes on the Model are acceptable unless they're overly complex.
- Both Query Builders and Scopes should return a Query Builder, not execute the query.
DTOs (Data Transfer Objects) and Resources
- DTOs are used for internal purposes, while Resources are used for return data.
- DTOs should be comprehensive, whereas Resources are tailored to specific use cases.
- Handle data like passwords, created_at, etc., differently between DTOs and Resources.
Actions, Services, and Jobs
- Both Services and Jobs can act as wrappers around Actions, performing one or more actions or other tasks.
- Actions are classes with a single `__invoke()` method.
- Services can implement multiple methods.
- Jobs are used for queuing tasks and can utilize Actions and/or Services.
These guidelines ensure our Laravel projects are not only well-organized but also adhere to the principles of Domain-Driven Design, enhancing both development efficiency and project scalability.
Adhering to Laravel Naming Conventions
Navigating through the robust world of Laravel, one quickly learns the importance of speaking its language — a language punctuated by conventions and standards that create a seamless dialogue between developer intent and framework execution. In this section, we'll focus on a cornerstone of Laravel development: adhering to naming conventions.
Laravel, a framework that prizes convention over configuration, offers a set of naming conventions that align with the PHP-FIG's PSR standards, as well as those organically adopted by the Laravel community. These conventions are not just recommendations; they are the collective wisdom of countless developers who have forged the framework's best practices through trial and error.
Good naming conventions serve as signposts throughout your application, making it easier to understand and navigate. Whether it's the singularity of a controller's name, the plurality of database tables, or the case style of table columns and functions, these naming strategies bring a uniformity that simplifies the development process and streamlines collaboration.
By adhering to these naming conventions, developers create a codebase that speaks with clarity and precision. The "Good" examples embody the practices that lead to a harmonious and intuitive structure, where each element is predictably placed and easily found. The "Bad" examples, however, show a divergence from these practices, leading to a codebase that can feel like a challenging puzzle.
As we delve into the specifics of naming within a Laravel application, remember that these conventions are more than just a means to an end. They are part of a philosophy that values clarity, consistency, and a shared understanding — traits that empower developers to write code that's not only functional but also part of a greater, well-ordered ecosystem.
What | How | Good | Bad |
---|---|---|---|
Controller | singular | ArticleController |
|
Route | plural | articles/1 |
|
Named route | snake_case with dot notation | users.show_active |
|
Model | singular | User |
|
hasOne or belongsTo relationship | singular | articleComment |
|
All other relationships | plural | articleComments |
|
Table | plural | article_comments |
|
Pivot table | singular model names in alphabetical order | article_user |
|
Table column | snake_case without model name | meta_title |
|
Foreign key | singular model name with _id suffix | article_id |
|
Primary key | - | id |
|
Migration | - | 2017_01_01_000000_create_articles_table |
|
Method | camelCase | getAll |
|
Function | snake_case | abort_if |
|
Method in resource controller | more infos: table | store |
|
Method in test class | camelCase | testGuestCannotSeeArticle |
|
Model property | snake_case | $model->model_property |
|
Variable | camelCase | $anyOtherVariable |
|
Collection | descriptive, plural | $activeUsers = User::active()->get() |
|
Object | descriptive, singular | $activeUser = User::active()->first() |
|
Config and language files index | snake_case | articles_enabled |
|
View | kebab-case | show-filtered.blade.php |
|
Config | kebab-case | google-calendar.php |
|
Contract (interface) | adjective or noun | Authenticatable |
|
Trait | adjective | Notifiable |
|
Embracing Concise and Clear Syntax
In the world of programming, clarity and brevity are not just stylistic choices—they are pillars of efficient and maintainable code. When it comes to Laravel, the beauty of the framework lies in its ability to simplify complex tasks with elegant and expressive syntax. By embracing Laravel's shortcuts and more readable syntax, developers can write code that is not just functional, but clean and intuitive. This section casts a spotlight on the art of refining Laravel code, stripping away the superfluous to reveal a more streamlined and direct form of expression. We'll compare common verbose patterns with their succinct counterparts, demonstrating how a few thoughtful choices in syntax can lead to vastly improved readability and a smoother development experience. Let’s explore these transformations together and elevate the conciseness of your code to new heights.
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() |
is_null($object->relation) ? $object->relation->id :
null
}
|
optional($object->relation)->id |
return view('index')->with('title', $title)->with('client',
$client)
|
return view('index', compact('title', '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') |
Upholding the Single Responsibility Principle
In the orchestra of object-oriented programming, the 'Single Responsibility Principle' (SRP) plays first chair. It dictates a simple yet profound tenet: a class or method should master one task alone, one piece of functionality, with a level of expertise akin to a virtuoso focusing on a single melody. Applying SRP to your Laravel application isn't just about reducing complexity; it's about crafting code that's as easy to maintain and update as it is to understand. So, let's unpack how adhering to this principle can turn your code into a harmonious symphony where each part performs its role to perfection.
Bad:
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;
}
}
Good:
protected function fullName(): Attribute
{
return Attribute::make(
get: fn () => $this->isVerifiedClient()
? $this->full_name_long
: $this->full_name_short,
);
}
protected function isVerifiedClient(): Attribute
{
return Attribute::make(
get: fn () => auth()->user()
&& auth()->user()->hasRole('client')
&& auth()->user()->isVerified(),
);
}
protected function fullNameLong(): Attribute
{
return Attribute::make(
get: fn () => 'Mr. ' . $this->first_name . ' ' . $this->middle_name . ' ' . $this->last_name,
);
}
protected function fullNameShort(): Attribute
{
return Attribute::make(
get: fn () => $this->first_name[0] . '. ' . $this->last_name,
);
}
Crafting Fat Models and Skinny Controllers
In the quest for clean and maintainable code within the Laravel framework, the mantra "Fat models, skinny controllers" is not just a catchy phrase—it's a guiding principle. By shifting the database-related heavy lifting into the robust arms of Eloquent models, or into dedicated Repository classes when dealing with Query Builder or raw SQL queries, we keep our controllers sleek and nimble. This approach allows for a clear separation of concerns, making your codebase more intuitive and scalable. Let's delve into how you can whip your application into shape by bulking up your models while keeping your controllers trim and focused solely on handling user input and returning responses.
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();
}
}
Streamlining Validation with Request Classes
In the tapestry of Laravel best practices, validation shines as a critical thread, ensuring data integrity and application security. Traditionally tucked within controllers, validation logic often leads to bloated and less maintainable code. By migrating this crucial process into dedicated Request classes, we embrace a cleaner, more organized structure, allowing for self-contained validation that is both reusable and easier to read. This section will walk you through the transition, demonstrating how your controllers can pass the baton to these specialized classes, transforming your validation workflow into a streamlined and robust system.
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',
];
}
}
Centralizing Business Logic in Service Classes
In the Laravel ecosystem, the architecture and flow of your application's code are just as critical as its functionality. This guideline delves into the best practices for structuring your business logic in a Laravel application. The tenet is straightforward: controllers should be kept clean and focused, with the heavy lifting offloaded to dedicated service classes.
The "Bad" example we're about to leave behind showcases a controller burdened with responsibilities that extend beyond its purview. It directly handles file uploads, intertwining business logic with request handling — a practice that, while functional, can lead to bloated controllers and a violation of the single responsibility principle.
The "Good" example illuminates the path to a more organized and maintainable structure. By introducing a service class, we encapsulate the business logic related to handling uploaded images, thereby adhering to the principle that each class should have only one reason to change. This not only makes our controllers leaner but also promotes code reusability and testing ease.
As we proceed, we'll explore the substantial benefits of extracting business logic into service classes, ensuring that controllers serve their intended purpose — to be the traffic cops of incoming requests, delegating the complex tasks to services that act as the dedicated workers of our application's business logic. By the end of this section, the concept of "fat models, skinny controllers" will be deeply ingrained in your Laravel craftsmanship, reinforcing the creation of a codebase that's as efficient and scalable as it is elegant.
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');
}
}
}
Implementing DRY (Don't Repeat Yourself) Principles
In the heart of efficient and effective coding practices lies a simple, yet powerful acronym: DRY, or "Don't Repeat Yourself." This principle is not just a rule of thumb; it's a strategy for reducing redundancy to streamline development and maintenance. By harnessing the Single Responsibility Principle (SRP), Laravel developers can create modular code that's easy to reuse across different parts of an application, from Eloquent models to Blade templates.
Within the elegant framework of Laravel, the DRY principle takes on a life of its own, offering a variety of tools such as Eloquent scopes and template inheritance to help you write less code while doing more. This section will dissect the DRY methodology within the context of Laravel, showcasing how you can refine your codebase into a more manageable, readable, and ultimately, a more reliable ecosystem. We'll look at concrete examples where the DRY principle eliminates redundancy, making your code cleaner and your life easier. Whether you're managing database queries or crafting views, let's explore how embracing DRY can transform your code from being merely functional to being exceptionally efficient.
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();
}
Prioritizing Eloquent ORM Over Query Builder and SQL
Embarking on database interactions in Laravel presents us with a choice: the raw power of SQL and the Query Builder, or the eloquent elegance of Eloquent ORM. This section of our Laravel Coding Guidelines champions the latter, advocating for Eloquent as the preferred ORM for its readability, maintainability, and rich array of features that elevate it beyond a simple object-mapping tool.
The "Bad" practice here is not about the use of raw SQL or Query Builder per se, but about choosing them when Eloquent offers a cleaner, more integrated approach. Raw SQL, while powerful, can quickly become unwieldy and obscure, making your codebase less approachable. The Query Builder streamlines SQL operations but lacks the object-oriented finesse and additional functionalities that Eloquent brings to the table.
On the other side of the spectrum, the "Good" example encapsulates complex SQL queries in a chain of method calls that are not just compact but are imbued with the full power of Eloquent's ORM capabilities. Soft deletes, model events, and global scopes are just a few of the features that Eloquent seamlessly integrates, allowing you to write code that's not only more expressive but also aligned with the "Laravel way."
In the following paragraphs, we'll explore why prioritizing Eloquent over raw SQL and Query Builder can result in code that is not only more expressive and in harmony with Laravel's design philosophy but also fosters a more sustainable and scalable codebase. Prepare to dive into the art of leveraging Eloquent's ORM to its fullest potential, transforming your database queries from mere operations into a fluent, intuitive conversation with your data.
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();
Safeguarding Against Mass Assignment Vulnerabilities
In the realm of web development, particularly within the Laravel ecosystem, the concept of mass assignment is a double-edged sword — it simplifies the process of creating and updating models with user input but also opens the door to potential security vulnerabilities. As we wade deeper into the waters of Laravel best practices, we must take a vigilant stand against these vulnerabilities. This section of our Laravel Coding Guidelines is dedicated to fortifying your application's defenses by properly managing mass assignment.
The "Bad" approach represents a naïve and risky handling of user input, assigning request data directly to model attributes without safeguards. This not only clutters our controller code but also exposes our application to the risk of overwriting sensitive data inadvertently. On the flip side, the "Good" practice encapsulates the power of Eloquent's relationships and mass assignment protection in one line of eloquent code, efficiently and securely creating an article within its associated category.
We will dissect these approaches, highlighting how the proper use of fillable or guarded properties, alongside leveraging relationship methods like `create()`, can streamline your CRUD operations while keeping the shield up against unauthorized updates. By the end of this section, you'll be equipped with the strategies needed to harness the full potential of mass assignment, turning it from a liability into a robust feature of your Laravel application.
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->all());
Avoiding Direct Database Queries in Blade Templates
Navigating the delicate balance between server-side logic and front-end presentation, Laravel developers often encounter the question of where to place database queries for maximum efficiency. As we turn our focus to the realm of Blade templates, it’s crucial to understand the impact of database interactions on the fluidity and responsiveness of our applications. This segment of our comprehensive guide on Laravel best practices delves into the common misstep of embedding direct database queries within Blade views, a practice that may seem convenient but can lead to significant performance bottlenecks.
To illustrate, let's consider the stark contrast in outcomes when fetching data for a list of users: the "Bad" approach indiscriminately executes a database query for each user, resulting in an alarming number of database hits. Conversely, the "Good" method showcases the elegance of eager loading with Eloquent's with() method, drastically reducing the query load to a mere two for the same task. Through this discussion, we’ll explore how to maintain the integrity of your templates by structuring your code to fetch and pass data efficiently, ensuring that your application's performance remains as compelling as its design. So, let's roll up our sleeves and refine our approach to Blade templates, transforming potential inefficiencies into exemplars of optimal performance.
Bad (for 100 users, 101 DB queries will be executed):
@foreach (User::all() as $user)
@endforeach
Good (for 100 users, 2 DB queries will be executed):
$users = User::with('profile')->get();
...
@foreach ($users as $user)
@endforeach
Minimizing Code Comments Through Self-Describing Names
As the adage goes, good code is like a good joke: it needs no explanation. In the realm of Laravel development, this philosophy encourages us to write code that’s self-explanatory, reducing the reliance on extensive commenting. In this section, we’re going to untangle the often-misunderstood relationship between code comments and code clarity. While comments have their place, over-commenting can clutter your codebase and distract from the logic at hand. We'll delve into the art of crafting self-describing method and variable names that stand on their own, rendering comments unnecessary. By demonstrating the transition from 'bad' to 'good', we’ll illustrate how eloquent naming conventions in Laravel can turn your code into a self-explanatory narrative, making it accessible and maintainable for any developer who may follow in your keystrokes. Let's refine our code to speak clearly and concisely, letting its structure and naming tell the story without a whisper of redundancy.
Bad:
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())
Separating JavaScript and CSS from Blade Templates
Embarking on a journey through Laravel’s Blade templates, one quickly discovers the intertwining of different languages: HTML's structure, PHP's logic, and often sprinkles of JavaScript and CSS. Yet, as in any well-composed symphony, each instrument—each language—should sing on its own to contribute to a harmonious whole. This section will tune into the nuances of separating concerns within your Blade views by moving JavaScript and CSS to their rightful stages. We'll explore the 'bad' and the 'better,' and then settle on 'the best' practices that not only optimize your application's performance but also maintain the pristine clarity of your code. By decoupling your scripts and styles from Blade templates, you create a more modular and maintainable codebase that hits all the right notes in scalability and efficiency. Let’s pull back the curtain and set the stage for a performance that leverages Laravel’s design to its fullest potential.
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.
Utilizing Config and Language Files for Text Management
In the vibrant tapestry of code that forms a Laravel application, hardcoding strings can be likened to knots that disrupt the flow, making localization and configuration a formidable task. Laravel’s philosophy encourages the use of config and language files—a method that not only untangles the strings of text scattered throughout your code but also paves the way for a more organized and scalable text management system. This section is a deep dive into the transformative practice of abstracting text into dedicated files. By examining the pitfalls of hardcoding and embracing the elegance of Laravel's built-in localization and configuration features, we will illustrate how to make your codebase cleaner, more maintainable, and ready to speak any language. Let's unpack the strategies that allow you to elegantly manage text and breeze through configuration like a maestro.
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'));
Integrating Community-Endorsed Laravel Tools
When navigating the rich ecosystem of Laravel, the compass that guides us toward best practices often points to the tools and packages that have been tried, tested, and endorsed by the vibrant Laravel community. This is not by coincidence. Opting for Laravel's native functionalities and community-backed packages over third-party alternatives is more than a mere preference—it's a commitment to maintainability, compatibility, and community support. In this section, we explore the rationale behind choosing community-vetted solutions that not only align with Laravel's philosophy but also ensure that anyone joining your project's journey can do so with ease. We'll delve into how this approach minimizes the learning curve for new developers, maximizes the potential for receiving community assistance, and avoids unnecessary costs for your clients. Let’s dive in and discover how to make the community's wisdom work for your Laravel projects.
Task | Standard tools | 3rd party tools |
---|---|---|
Authorization | Policies | Entrust, Sentinel and other packages |
Compiling assets | Laravel Mix | Grunt, Gulp, 3rd party packages |
Development Environment | Homestead | Docker |
Deployment | Laravel Forge | Deployer and other solutions |
Unit testing | PHPUnit, Mockery | Phpspec |
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 | 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 scheduling | Laravel Task Scheduler | Scripts and 3rd party packages |
DB | MySQL, PostgreSQL, SQLite, SQL Server | MongoDB |
Employing Dependency Injection and Facades Correctly
In the architectural dance of object-oriented programming, dependency injection and facades play leading roles in Laravel's choreography. The direct instantiation of objects using `new Class` is akin to a misstep in this dance, leading to tightly coupled components that hinder the elegance of your application's structure and its testability. Laravel's inversion of control (IoC) container and facades emerge as the graceful partners, ensuring your classes remain loosely coupled and harmoniously integrated. This section is a guide to mastering these partners, as it dissects the nuances of injecting dependencies and utilizing facades—the keystones of flexible and testable code in Laravel. Here, you'll learn how to let the framework handle the heavy lifting, allowing you to focus on crafting seamless interactions between your application's components.
Bad:
$user = new User;
$user->create($request->all());
Good:
public function __construct(User $user)
{
$this->user = $user;
}
....
$this->user->create($request->all());
Securely Managing Environment Variables with Config Files
Navigating the secure and efficient handling of environment-specific variables is akin to steering through the capricious straits of application development. Directly plucking data from the .env file or global environment variables is a practice fraught with perils, akin to reaching into the machinery while it’s in motion. Laravel, with its emphasis on best practices, teaches us a better way: channel these variables through the sanctum of configuration files, employing the config() helper function as the trustworthy intermediary. This section will illuminate the path from direct exposure to encapsulated access, illustrating how to bolster security and ensure your application's environment configurations are as orderly as they are accessible.
Bad:
$apiKey = env('API_KEY');
Good:
// config/api.php
'key' => env('API_KEY'),
// Use the data
$apiKey = config('api.key');
Standardizing Date Storage and Formatting Techniques
Time is the invisible thread that runs through every application, a silent chronometer of our digital interactions. In Laravel, the disciplined storage of dates in the standard and UTC format lays down the universal groundwork, ensuring that time — that most critical and ubiquitous datum — remains consistent and comparable across systems and geographies. Yet, the true versatility of Laravel's date handling comes to light with the judicious use of accessors and mutators, enabling us to present this time data in formats that are both locally relevant and user-friendly. In the following section, we'll delve into the best practices for managing this temporal data, transforming the immutable tick of the universal clock into a symphony of localized moments that resonate with users the world over.
Bad:
{{ $object->ordered_at->toDateString() }}
{{ $object->ordered_at->format('m-d') }}
{{ 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') }}
Incorporating Additional Laravel Best Practices
Never put any logic in routes files.
Minimize usage of vanilla PHP in Blade templates.