The AppServiceProvider
file is often seen as a simple piece of code that you configure at the start of a project and then forget about. Yet it's one of the most important files for properly managing a Laravel application. If you want to organize your code, centralize certain configurations, or even add global functionalities, this is where it all happens.
In this article, I'll show you why the AppServiceProvider
deserves more attention, and how you can really leverage it to make your application cleaner and more efficient.
Understanding the role of the AppServiceProvider
The AppServiceProvider
is a file generated by default when you create a Laravel project. It is located in the app/Providers directory and contains two main methods: register
and boot
. These two methods are used to initialize services or configure global aspects of the application.
The register()
method
The register
method is used to register services in the dependency injection container. This is where you
you add any bindings or singletons you want to make accessible throughout your application.
Here's an example:
public function register(): void
{
$this->app->singleton(MyAwesomeService::class, function ($app) {
return new MyAwesomeService();
});
}
Here, we register a MyAwesomeService
class as a singleton. This means that Laravel will create
a single instance of this class, and share it wherever it's injected.
The boot()
method
The boot
method, on the other hand, is called after all services have been registered. This is where you can
perform actions that require everything to be already loaded, such as defining custom validation rules,
configure views or listen to events.
Here's an example:
use Illuminate\Support\Facades\Schema;
public function boot(): void
{
Schema::defaultStringLength(191);
}
In this example, if you're using MySQL and are having problems with string lengths, you can define a default length.
Protect against destructive commands
When you're working on an application, it's essential to protect your database, especially in production.
With the APP_ENV=production
option, this protection prevents dangerous commands from executing and risking
damage your data. During development, however, there's no need to worry about this,
because commands such as db:wipe
, migrate:fresh
, migrate:refresh
or migrate:reset
are often used to
test, reset or clean the database.
Business logic
Laravel offers us a Facade called Illuminate\Support\Facades\DB
, which contains a static method called
prohibitDestructiveCommands()
. This method takes a boolean parameter. Fortunately, our
AppServiceProvider inherits from AppServiceProvider, allowing us to access $app
, a reference to the application.
This allows us to use isProduction()
to find out if we're in a production environment.
Here's how you can integrate it into your AppServiceProvider:
$this->app->isProduction() // return: true or false
Once this logic is in place, simply pass the value returned by isProduction()
into
DB::prohibitDestructiveCommands()
. In this way, dangerous commands will be blocked in production.
In the boot()
of your AppServiceProvider, call the configureCommands()
method as follows:
use Illuminate\Support\Facades\DB;
final class AppServiceProvider extends ServiceProvider
{
/**
* Bootstrap any application services.
*/
public function boot(): void
{
$this->configureCommands();
}
/**
* Configure the application's commands.
*/
private function configureCommands(): void
{
DB::prohibitDestructiveCommands(
$this->app->isProduction()
);
}
}
With this configuration, you're now protected against destructive commands, and can continue with date configuration!
Making dates immutable
When working with dates in Laravel, we often use the [Carbon] library (https://carbon.nesbot.com/), which can pose a silent problem. problem. Indeed, after a few tests, it seems that dates are by default mutable in Laravel, which can lead to unexpected unexpected behavior.
This is just my opinion, of course. You don't have to adopt this approach, but personally, I think that dates in Laravel should be immutable by default.
Example of the problem
Let's take a model with two columns starts_at
and ends_at
, both instantiated in Carbon
. Let's imagine
we define a variable $now
, containing the current date and time, and add seven days for ends_at
.
$now = now();
$data = [
'starts_at' => $now,
'ends_at' => $now->addDays(7),
];
dd(
$data['starts_at']->format('d-m-Y'), // 24-01-2025
$data['ends_at']->format('d-m-Y'), // 24-01-2025
);
When you use dd()
to display $data
, you'll notice a small problem: the two dates, starts_at
and
ends_at
, display exactly the same date (+7 days), which is not what we expected. On the other hand, your
model will have the right data.
Solution
To correct this problem, we can use the Facade Illuminate\Support\Facades\Date
and tell it to always
to always use CarbonImmutable::class
by default, an immutable version of Carbon.
Here's how to do it in your AppServiceProvider :
use Carbon\CarbonImmutable;
use Illuminate\Support\Facades\Date;
final class AppServiceProvider extends ServiceProvider
{
/**
* Bootstrap any application services.
*/
public function boot(): void
{
$this->configureDates();
}
/**
* Configure the application's dates.
*/
private function configureDates(): void
{
Date::use(CarbonImmutable::class);
}
}
Now, if you go back to your dd()
, you'll see that the dates are correct.
dd(
$data['starts_at']->format('d-m-Y'), // 17-01-2025
$data['ends_at']->format('d-m-Y'), // 24-01-2025
);
Now you're ready to start configuring your models!
Models
I know that some people will disagree with me on this point, but after several years working with Laravel, I no longer swear by the famous $fillables = [];
that Laravel recommends by default.
working with Laravel, I no longer swear by the famous $fillables = [];
that Laravel recommends by default.
For me, it's a real time-saver not to have to manage it for each new application.
Of course, you're free to skip this step if you don't want to disable the $fillables = [];
and not have to add $guarded = [];
to each template.
Should be Strict!
In my approach, templates should be strict by default. Here's a concrete example:
$user = User::find(1);
$user->propertyDoesntExist;
If you use the standard Laravel implementation, it will throw an exception to tell you that the property
property does not exist in the User
model. It's a great way of detecting errors quickly!
To force this approach in your application, you can add this piece of code to your AppServiceProvider :
use Illuminate\Database\Eloquent\Model;
Model::shouldBeStrict(! $this->app->isProduction());
Disabling Mass Assignment
I'd now like to talk about managing Mass Assignment in Laravel, which is great for beginners, but can become a bit beginners, but can become a bit restrictive when you're already familiar with the framework. After six years of use, I've found that it's more of a hindrance than anything else to working quickly and efficiently. to work quickly and efficiently.
If you don't want to apply this feature, you can skip this step.
Here's how to disable the Mass Assignment mechanism:
use Illuminate\Database\Eloquent\Model;
Model::unguard();
Simply remove the one you don't want to use.
Putting both in place
If you want to apply both strict templates and disable Mass Assignment, you can set this up in your AppServiceProvider :
use Illuminate\Database\Eloquent\Model;
final class AppServiceProvider extends ServiceProvider
{
/**
* Bootstrap any application services.
*/
public function boot(): void
{
$this->configureModels();
}
/**
* Configure the application's models.
*/
private function configureModels(): void
{
Model::shouldBeStrict(! $this->app->isProduction());
Model::unguard();
}
}
Now you're ready to start configuring your passwords!
Password validation
Password validation is something I often configure in my projects. By default, Laravel offers a fairly basic password rule, which may not be sufficient to guarantee user security in more serious applications. Depending on the context of your application, I prefer them to be more robust.
Why is this important?
In my opinion, depending on the application environment, it's important to configure strict password rules in production, while retaining a certain degree of flexibility flexibility in development. In production, the aim is to ensure that no weak or compromised or compromised passwords can be used, while in development, rigor can be reduced to facilitate testing.
Business logic
Here's how I implement this in my AppServiceProvider. I use the
I'm using the Facade IlluminateValidationRulesPassword
to define the validation rule for passwords.
validation rule, and make it conditional on the application environment
application environment (production or development).
use Illuminate\Validation\Rules\Password;
final class AppServiceProvider extends ServiceProvider
{
/**
* Bootstrap any application services.
*/
public function boot(): void
{
$this->configurePasswordValidation();
}
/**
* Configure the application's password validation.
*/
private function configurePasswordValidation(): void
{
Password::defaults(
fn () => $this->app->isProduction()
? Password::min(8)->uncompromised()
: null
);
}
}
Now you're ready to configure the HTTPS scheme!
Force HTTPS Schema
For me, HTTPS is an absolute necessity, even locally. Working exclusively with HTTPS has become a habit, as it simplifies things and ensures a certain consistency between development and production environments. I mainly use Laravel Herd locally, which makes it easy for me to activate HTTPS on my projects without having to worry about additional configurations.
With a view to securing all connections, whether for users or for the application itself, I always make sure to force the use of the HTTPS, even in my development environment. This avoids any confusion and ensures that all communications are encrypted from the outset.
Business logic
Here's how I set this up in my AppServiceProvider to force the use of the HTTPS scheme throughout the application, regardless of the environment (local or production):
use Illuminate\Support\Facades\URL;
final class AppServiceProvider extends ServiceProvider
{
/**
* Bootstrap any application services.
*/
public function boot(): void
{
$this->configureUrls();
}
/**
* Configure the application's URLs.
*/
private function configureUrls(): void
{
URL::forceScheme('https');
}
}
This ensures that your application is ready for a secure environment environment right from the start. Now you're ready to start configuring Vite for your assets!
Use Aggressive Prefetching for Vite
Laravel 11 has introduced a very interesting feature: the ability to simplify your assets into a single file using Vite. This new feature can greatly improve your application's performance, notably by reducing the number of HTTP requests needed to load your assets.
Business logic
Here's how to activate this feature in your Laravel application:
use Illuminate\Support\Facades\Vite;
final class AppServiceProvider extends ServiceProvider
{
/**
* Bootstrap any application services.
*/
public function boot(): void
{
$this->configureVite();
}
/**
* Configure the application's Vite.
*/
private function configureVite(): void
{
Vite::useAggressivePrefetching();
}
}
Thank you for following this article to the end.
Your AppServiceProvider now
Here's the complete file with all the configurations. It allows you to to configure the application with the best practices we've discussed in the article :
use Carbon\CarbonImmutable;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\URL;
use Illuminate\Support\Facades\Date;
use Illuminate\Support\Facades\Vite;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Validation\Rules\Password;
final class AppServiceProvider extends ServiceProvider
{
/**
* Bootstrap any application services.
*/
public function boot(): void
{
$this->configureCommands();
$this->configureDates();
$this->configureModels();
$this->configurePasswordValidation();
$this->configureUrls();
$this->configureVite();
}
/**
* Configure the application's commands.
*/
private function configureCommands(): void
{
DB::prohibitDestructiveCommands(
$this->app->isProduction()
);
}
/**
* Configure the application's dates.
*/
private function configureDates(): void
{
Date::use(CarbonImmutable::class);
}
/**
* Configure the application's models.
*/
private function configureModels(): void
{
Model::shouldBeStrict(! $this->app->isProduction());
Model::unguard();
}
/**
* Configure the application's password validation.
*/
private function configurePasswordValidation(): void
{
Password::defaults(
fn () => $this->app->isProduction()
? Password::min(8)->uncompromised()
: null
);
}
/**
* Configure the application's URLs.
*/
private function configureUrls(): void
{
URL::forceScheme('https');
}
/**
* Configure the application's Vite.
*/
private function configureVite(): void
{
Vite::useAggressivePrefetching();
}
}