Laravel Form Validation: Ensuring Data Integrity with Simple Rules
Validate user input with Laravel's built-in validation system.
Service Providers are the central place to configure and bootstrap your Laravel application. They tell Laravel how to register services, bind interfaces to implementations, and perform setup tasks.
Service Providers Handle:
# Create a basic service provider
php artisan make:provider PaymentServiceProvider
# Create a provider that implements DeferrableProvider
php artisan make:provider AnalyticsServiceProvider --defer
# Create multiple providers for different features
php artisan make:provider UserServiceProvider
php artisan make:provider OrderServiceProvider
php artisan make:provider NotificationServiceProvider
<?php
// app/Providers/PaymentServiceProvider.php
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
use App\Services\PaymentGateway;
use App\Services\StripePaymentGateway;
use App\Services\PayPalPaymentGateway;
class PaymentServiceProvider extends ServiceProvider
{
/**
* Register services.
*/
public function register(): void
{
// Register bindings and services here
$this->registerPaymentGateways();
$this->registerPaymentServices();
}
/**
* Bootstrap services.
*/
public function boot(): void
{
// Perform initialization, event listeners, etc.
$this->bootPaymentEvents();
}
/**
* Register payment gateways.
*/
protected function registerPaymentGateways(): void
{
$this->app->bind('payment.gateway.stripe', function ($app) {
return new StripePaymentGateway(
config('services.stripe.secret'),
config('services.stripe.public')
);
});
$this->app->bind('payment.gateway.paypal', function ($app) {
return new PayPalPaymentGateway(
config('services.paypal.client_id'),
config('services.paypal.secret'),
config('services.paypal.sandbox')
);
});
}
/**
* Register payment services.
*/
protected function registerPaymentServices(): void
{
$this->app->singleton(PaymentGateway::class, function ($app) {
$defaultGateway = config('payment.default_gateway', 'stripe');
return $app->make("payment.gateway.{$defaultGateway}");
});
}
/**
* Boot payment-related events.
*/
protected function bootPaymentEvents(): void
{
// Event listeners for payment processing
}
}
Laravel automatically registers providers listed in config/app.php:
<?php
// config/app.php
return [
'providers' => [
/*
* Laravel Framework Service Providers...
*/
Illuminate\Auth\AuthServiceProvider::class,
Illuminate\Broadcasting\BroadcastServiceProvider::class,
// ...
/*
* Package Service Providers...
*/
/*
* Application Service Providers...
*/
App\Providers\AppServiceProvider::class,
App\Providers\AuthServiceProvider::class,
App\Providers\EventServiceProvider::class,
App\Providers\RouteServiceProvider::class,
// Custom Service Providers
App\Providers\PaymentServiceProvider::class,
App\Providers\UserServiceProvider::class,
App\Providers\OrderServiceProvider::class,
App\Providers\NotificationServiceProvider::class,
],
];
<?php
// app/Providers/AppServiceProvider.php
public function register(): void
{
// Register providers conditionally
if ($this->app->environment('local')) {
$this->app->register(\Laravel\Telescope\TelescopeServiceProvider::class);
$this->app->register(\App\Providers\LocalServiceProvider::class);
}
if ($this->app->runningInConsole()) {
$this->app->register(\App\Providers\ConsoleServiceProvider::class);
}
// Register based on configuration
if (config('services.analytics.enabled')) {
$this->app->register(\App\Providers\AnalyticsServiceProvider::class);
}
}
<?php
// app/Providers/OrderServiceProvider.php
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
use App\Services\OrderService;
use App\Services\OrderProcessor;
use App\Services\OrderValidator;
use App\Contracts\PaymentGateway;
use App\Contracts\InventoryService;
use App\Contracts\NotificationService;
class OrderServiceProvider extends ServiceProvider
{
public function register(): void
{
$this->registerOrderValidation();
$this->registerOrderProcessing();
$this->registerOrderService();
}
protected function registerOrderValidation(): void
{
$this->app->singleton(OrderValidator::class, function ($app) {
return new OrderValidator(
$app->make('cache.store'),
$app->make('db')
);
});
}
protected function registerOrderProcessing(): void
{
$this->app->bind(OrderProcessor::class, function ($app) {
return new OrderProcessor(
$app->make(PaymentGateway::class),
$app->make(InventoryService::class),
$app->make(NotificationService::class),
$app->make(OrderValidator::class)
);
});
}
protected function registerOrderService(): void
{
$this->app->singleton(OrderService::class, function ($app) {
return new OrderService(
$app->make(OrderProcessor::class),
$app->make(OrderValidator::class),
$app->make('events')
);
});
}
public function boot(): void
{
// Publish configuration
$this->publishes([
__DIR__.'/../../config/orders.php' => config_path('orders.php'),
], 'order-config');
// Register commands
if ($this->app->runningInConsole()) {
$this->commands([
\App\Console\Commands\ProcessPendingOrders::class,
\App\Console\Commands\CleanupOldOrders::class,
]);
}
}
}
<?php
// app/Providers/StorageServiceProvider.php
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
use App\Contracts\FileStorage;
use App\Services\LocalFileStorage;
use App\Services\S3FileStorage;
use App\Services\CloudStorage;
class StorageServiceProvider extends ServiceProvider
{
public function register(): void
{
$this->registerStorageDrivers();
$this->registerDefaultStorage();
}
protected function registerStorageDrivers(): void
{
// Local file storage
$this->app->bind('storage.driver.local', function ($app) {
return new LocalFileStorage(
storage_path('app/uploads'),
config('app.url').'/storage/uploads'
);
});
// Amazon S3 storage
$this->app->bind('storage.driver.s3', function ($app) {
return new S3FileStorage(
config('filesystems.disks.s3.key'),
config('filesystems.disks.s3.secret'),
config('filesystems.disks.s3.region'),
config('filesystems.disks.s3.bucket')
);
});
// Cloud storage (generic)
$this->app->bind('storage.driver.cloud', function ($app) {
return new CloudStorage(
config('services.cloud_storage.endpoint'),
config('services.cloud_storage.token')
);
});
}
protected function registerDefaultStorage(): void
{
$this->app->bind(FileStorage::class, function ($app) {
$driver = config('filesystems.default', 'local');
return $app->make("storage.driver.{$driver}");
});
// Contextual binding - use different storage for different types
$this->app->when(\App\Services\UserAvatarService::class)
->needs(FileStorage::class)
->give('storage.driver.local');
$this->app->when(\App\Services\BackupService::class)
->needs(FileStorage::class)
->give('storage.driver.s3');
}
public function boot(): void
{
// Merge configuration
$this->mergeConfigFrom(
__DIR__.'/../../config/storage.php', 'storage'
);
}
}
<?php
// app/Providers/EventServiceProvider.php
namespace App\Providers;
use Illuminate\Auth\Events\Registered;
use Illuminate\Auth\Listeners\SendEmailVerificationNotification;
use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider;
class EventServiceProvider extends ServiceProvider
{
protected $listen = [
Registered::class => [
SendEmailVerificationNotification::class,
],
\App\Events\OrderCreated::class => [
\App\Listeners\ProcessOrderPayment::class,
\App\Listeners\UpdateInventory::class,
\App\Listeners\SendOrderConfirmation::class,
\App\Listeners\NotifyAdmin::class,
],
\App\Events\PaymentProcessed::class => [
\App\Listeners\UpdateOrderStatus::class,
\App\Listeners\GenerateInvoice::class,
\App\Listeners\SendPaymentConfirmation::class,
],
\App\Events\UserRegistered::class => [
\App\Listeners\SendWelcomeEmail::class,
\App\Listeners\CreateUserProfile::class,
\App\Listeners\AssignDefaultRole::class,
],
];
protected $subscribe = [
\App\Listeners\UserEventSubscriber::class,
\App\Listeners\OrderEventSubscriber::class,
];
public function boot(): void
{
parent::boot();
// Manually register events
\Event::listen('user.login', function ($user) {
// Track user login
$user->update(['last_login_at' => now()]);
});
\Event::listen('product.view', \App\Listeners\TrackProductView::class);
// Register model events
\App\Models\Order::saving(function ($order) {
// Validate order before saving
});
\App\Models\User::created(function ($user) {
// Perform actions after user creation
});
}
public function shouldDiscoverEvents(): bool
{
return true;
}
}
Deferred providers only load when their services are actually needed.
<?php
// app/Providers/AnalyticsServiceProvider.php
namespace App\Providers;
use Illuminate\Contracts\Support\DeferrableProvider;
use Illuminate\Support\ServiceProvider;
use App\Services\GoogleAnalytics;
use App\Services\FacebookAnalytics;
use App\Services\AnalyticsManager;
class AnalyticsServiceProvider extends ServiceProvider implements DeferrableProvider
{
public function register(): void
{
$this->app->singleton('analytics.google', function ($app) {
return new GoogleAnalytics(
config('services.google_analytics.tracking_id'),
config('services.google_analytics.client_id')
);
});
$this->app->singleton('analytics.facebook', function ($app) {
return new FacebookAnalytics(
config('services.facebook.pixel_id'),
config('services.facebook.access_token')
);
});
$this->app->singleton(AnalyticsManager::class, function ($app) {
return new AnalyticsManager($app);
});
}
/**
* Get the services provided by the provider.
*/
public function provides(): array
{
return [
'analytics.google',
'analytics.facebook',
AnalyticsManager::class,
];
}
}
For creating reusable packages:
<?php
// packages/your-package/src/YourPackageServiceProvider.php
namespace YourVendor\YourPackage;
use Illuminate\Support\ServiceProvider;
class YourPackageServiceProvider extends ServiceProvider
{
public function register(): void
{
// Merge package configuration
$this->mergeConfigFrom(
__DIR__.'/../config/your-package.php', 'your-package'
);
// Register package services
$this->app->singleton('your-package', function ($app) {
return new YourPackageManager($app);
});
}
public function boot(): void
{
// Publish configuration
$this->publishes([
__DIR__.'/../config/your-package.php' => config_path('your-package.php'),
], 'your-package-config');
// Publish migrations
$this->publishes([
__DIR__.'/../database/migrations/' => database_path('migrations'),
], 'your-package-migrations');
// Publish assets
$this->publishes([
__DIR__.'/../resources/assets' => public_path('vendor/your-package'),
], 'your-package-assets');
// Load routes
$this->loadRoutesFrom(__DIR__.'/../routes/web.php');
// Load views
$this->loadViewsFrom(__DIR__.'/../resources/views', 'your-package');
// Load migrations
$this->loadMigrationsFrom(__DIR__.'/../database/migrations');
// Load translations
$this->loadTranslationsFrom(__DIR__.'/../resources/lang', 'your-package');
}
}
<?php
// app/Providers/ContextualServiceProvider.php
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
class ContextualServiceProvider extends ServiceProvider
{
public function register(): void
{
// Different implementations based on context
$this->app->when(\App\Http\Controllers\Api\v1\UserController::class)
->needs(\App\Contracts\UserRepository::class)
->give(\App\Repositories\Api\UserRepository::class);
$this->app->when(\App\Http\Controllers\Admin\UserController::class)
->needs(\App\Contracts\UserRepository::class)
->give(\App\Repositories\Admin\UserRepository::class);
$this->app->when(\App\Console\Commands\ImportUsers::class)
->needs(\App\Contracts\UserRepository::class)
->give(\App\Repositories\Import\UserRepository::class);
// Environment-based bindings
if ($this->app->environment('production')) {
$this->app->bind(\App\Contracts\CacheService::class, \App\Services\RedisCacheService::class);
} else {
$this->app->bind(\App\Contracts\CacheService::class, \App\Services\ArrayCacheService::class);
}
// Configuration-based bindings
$mailDriver = config('mail.default');
$this->app->bind(\App\Contracts\MailService::class, "mail.driver.{$mailDriver}");
}
}
<?php
// app/Providers/EcommerceServiceProvider.php
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
use App\Services\CartService;
use App\Services\WishlistService;
use App\Services\ProductService;
use App\Services\CategoryService;
use App\Services\InventoryService;
use App\Services\ShippingService;
use App\Services\TaxService;
class EcommerceServiceProvider extends ServiceProvider
{
public function register(): void
{
$this->registerCoreServices();
$this->registerSupportServices();
$this->registerRepositories();
}
protected function registerCoreServices(): void
{
// Cart Service
$this->app->singleton(CartService::class, function ($app) {
return new CartService(
$app->make('session'),
$app->make('auth'),
$app->make('events')
);
});
// Wishlist Service
$this->app->singleton(WishlistService::class, function ($app) {
return new WishlistService(
$app->make('auth'),
$app->make('cache.store')
);
});
// Product Service
$this->app->singleton(ProductService::class, function ($app) {
return new ProductService(
$app->make(\App\Repositories\ProductRepository::class),
$app->make('cache.store'),
$app->make('events')
);
});
}
protected function registerSupportServices(): void
{
// Inventory Service
$this->app->singleton(InventoryService::class, function ($app) {
return new InventoryService(
$app->make('db'),
$app->make('cache.store'),
$app->make('events')
);
});
// Shipping Service
$this->app->singleton(ShippingService::class, function ($app) {
return new ShippingService(
config('services.shipping.api_key'),
config('services.shipping.base_url'),
$app->make('cache.store')
);
});
// Tax Service
$this->app->singleton(TaxService::class, function ($app) {
return new TaxService(
config('services.tax.provider'),
config('services.tax.api_key'),
$app->make('cache.store')
);
});
}
protected function registerRepositories(): void
{
$this->app->bind(
\App\Contracts\ProductRepository::class,
\App\Repositories\EloquentProductRepository::class
);
$this->app->bind(
\App\Contracts\CategoryRepository::class,
\App\Repositories\EloquentCategoryRepository::class
);
$this->app->bind(
\App\Contracts\OrderRepository::class,
\App\Repositories\EloquentOrderRepository::class
);
}
public function boot(): void
{
// Publish ecommerce configuration
$this->publishes([
__DIR__.'/../../config/ecommerce.php' => config_path('ecommerce.php'),
], 'ecommerce-config');
// Register ecommerce commands
if ($this->app->runningInConsole()) {
$this->commands([
\App\Console\Commands\UpdateProductInventory::class,
\App\Console\Commands\ProcessAbandonedCarts::class,
\App\Console\Commands\GenerateSalesReport::class,
]);
}
// Register view composers
$this->app['view']->composer('partials.cart', function ($view) {
$view->with('cartCount', app(CartService::class)->getItemCount());
});
$this->app['view']->composer('partials.categories', function ($view) {
$view->with('categories', app(CategoryService::class)->getActiveCategories());
});
// Register middleware
$this->app['router']->aliasMiddleware('cart.check', \App\Http\Middleware\CheckCart::class);
$this->app['router']->aliasMiddleware('inventory.check', \App\Http\Middleware\CheckInventory::class);
}
}
<?php
// app/Providers/NotificationServiceProvider.php
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
use App\Services\EmailNotificationService;
use App\Services\SMSNotificationService;
use App\Services\PushNotificationService;
use App\Services\SlackNotificationService;
use App\Services\NotificationManager;
class NotificationServiceProvider extends ServiceProvider
{
public function register(): void
{
$this->registerNotificationChannels();
$this->registerNotificationManager();
}
protected function registerNotificationChannels(): void
{
// Email notifications
$this->app->bind('notification.channel.email', function ($app) {
return new EmailNotificationService(
$app->make('mailer'),
config('mail.from.address'),
config('mail.from.name')
);
});
// SMS notifications
$this->app->bind('notification.channel.sms', function ($app) {
return new SMSNotificationService(
config('services.twilio.sid'),
config('services.twilio.token'),
config('services.twilio.phone_number')
);
});
// Push notifications
$this->app->bind('notification.channel.push', function ($app) {
return new PushNotificationService(
config('services.firebase.server_key'),
config('services.firebase.sender_id')
);
});
// Slack notifications
$this->app->bind('notification.channel.slack', function ($app) {
return new SlackNotificationService(
config('services.slack.webhook_url'),
config('services.slack.channel')
);
});
}
protected function registerNotificationManager(): void
{
$this->app->singleton(NotificationManager::class, function ($app) {
$manager = new NotificationManager($app);
// Register available channels
$manager->registerChannel('email', $app->make('notification.channel.email'));
$manager->registerChannel('sms', $app->make('notification.channel.sms'));
$manager->registerChannel('push', $app->make('notification.channel.push'));
$manager->registerChannel('slack', $app->make('notification.channel.slack'));
return $manager;
});
// Set default channels based on configuration
$this->app->when(NotificationManager::class)
->needs('$defaultChannels')
->giveConfig('notifications.default_channels');
}
public function boot(): void
{
// Merge notification configuration
$this->mergeConfigFrom(
__DIR__.'/../../config/notifications.php', 'notifications'
);
// Publish notification templates
$this->publishes([
__DIR__.'/../../resources/views/notifications/' => resource_path('views/vendor/notifications'),
], 'notification-templates');
// Register notification events
$this->app['events']->listen(
\App\Events\UserRegistered::class,
\App\Listeners\SendWelcomeNotification::class
);
$this->app['events']->listen(
\App\Events\OrderCreated::class,
\App\Listeners\SendOrderNotification::class
);
}
}
<?php
// tests/Unit/ServiceProviders/PaymentServiceProviderTest.php
namespace Tests\Unit\ServiceProviders;
use Tests\TestCase;
use App\Providers\PaymentServiceProvider;
use App\Contracts\PaymentGateway;
use App\Services\StripePaymentGateway;
class PaymentServiceProviderTest extends TestCase
{
public function test_payment_gateway_binding()
{
// Create the service provider
$provider = new PaymentServiceProvider($this->app);
$provider->register();
// Test that PaymentGateway interface is bound
$this->assertTrue($this->app->bound(PaymentGateway::class));
// Test that it resolves to StripePaymentGateway
$gateway = $this->app->make(PaymentGateway::class);
$this->assertInstanceOf(StripePaymentGateway::class, $gateway);
}
public function test_payment_gateway_configuration()
{
// Set configuration
config(['payment.default_gateway' => 'paypal']);
$provider = new PaymentServiceProvider($this->app);
$provider->register();
$gateway = $this->app->make(PaymentGateway::class);
$this->assertInstanceOf(\App\Services\PayPalPaymentGateway::class, $gateway);
}
public function test_singleton_binding()
{
$provider = new PaymentServiceProvider($this->app);
$provider->register();
$instance1 = $this->app->make(PaymentGateway::class);
$instance2 = $this->app->make(PaymentGateway::class);
$this->assertSame($instance1, $instance2);
}
}
// tests/Unit/ServiceProviders/DeferredServiceProviderTest.php
class DeferredServiceProviderTest extends TestCase
{
public function test_deferred_provider_only_loads_when_needed()
{
// Deferred provider shouldn't be loaded initially
$this->assertFalse($this->app->providerIsLoaded(\App\Providers\AnalyticsServiceProvider::class));
// When we request a service from it, it should load
$this->app->make('analytics.google');
$this->assertTrue($this->app->providerIsLoaded(\App\Providers\AnalyticsServiceProvider::class));
}
public function test_provides_method_returns_correct_services()
{
$provider = new \App\Providers\AnalyticsServiceProvider($this->app);
$services = $provider->provides();
$this->assertEquals([
'analytics.google',
'analytics.facebook',
\App\Services\AnalyticsManager::class,
], $services);
}
}
// Instead of one giant AppServiceProvider, create focused providers:
app/Providers/
├── UserServiceProvider.php
├── OrderServiceProvider.php
├── PaymentServiceProvider.php
├── NotificationServiceProvider.php
├── AnalyticsServiceProvider.php
└── ReportServiceProvider.php
public function register(): void
{
$this->registerRepositories();
$this->registerServices();
$this->registerManagers();
}
protected function registerRepositories(): void
{
// Repository bindings
}
protected function registerServices(): void
{
// Service bindings
}
protected function registerManagers(): void
{
// Manager bindings
}
class HeavyServiceProvider extends ServiceProvider implements DeferrableProvider
{
public function provides(): array
{
return [HeavyService::class, AnotherHeavyService::class];
}
}
public function boot(): void
{
// Merge configuration
$this->mergeConfigFrom(__DIR__.'/../../config/package.php', 'package');
// Publish configuration
$this->publishes([
__DIR__.'/../../config/package.php' => config_path('package.php'),
], 'package-config');
}
// Use deferred providers for optional features
class OptionalFeatureProvider extends ServiceProvider implements DeferrableProvider
{
public function provides(): array
{
return [OptionalService::class];
}
}
public function register(): void
{
// Only register if feature is enabled
if (config('features.analytics')) {
$this->app->singleton(AnalyticsService::class, function ($app) {
return new AnalyticsService($app->make('config'));
});
}
}
public function boot(): void
{
// Only boot if not in console
if (!$this->app->runningInConsole()) {
$this->registerWebRoutes();
$this->registerViewComposers();
}
}
You've now mastered Laravel Service Providers and can effectively organize and bootstrap your application services! In our next post, we'll explore Laravel Facades: How Do They Work? (The Magic Behind the Static Interface) to understand Laravel's static service access.