Applying the Interface Programming Principle in Laravel: From SMS to Email Notifications
This article explains the interface‑programming principle, demonstrates its benefits with SMS and email notification services in Laravel, shows how to refactor code using an INotification interface, and configures the service container to switch implementations seamlessly, illustrating a flexible, maintainable backend design.
Adhering to the "program to an interface" philosophy, this article explores how defining clear interfaces rather than concrete implementations improves flexibility, modularity, testability, and extensibility of software systems, aligning with core design principles such as Open/Closed.
Without the Principle
First, a simple SMS notification service is created:
// app/Services/SmsNotificationService.php
namespace App\Services;
use App\Models\User;
class SmsNotificationService{
public function sendRegistrationNotification(User $user){
// retrieve user phone number
// send registration SMS
}
public function subscriptionExpired(User $user, User $admin){
// retrieve user phone number
// notify user of expired subscription
// retrieve admin phone number
// notify admin
}
}The service is then used directly in a controller and a console command:
// app/Http/Controllers/AuthController.php
namespace App\Http\Controllers;
use App\Http\Controllers\Controller;
use App\Services\SmsNotificationService;
use Illuminate\Http\Request;
class AuthController extends Controller{
public function __construct(private SmsNotificationService $smsService){}
public function Register(Request $request){
// register user
...
$this->smsService->sendRegistrationNotification($user);
}
} // app/Console/Commands/CheckSubscriptionExpirationCommand.php
// runs daily
namespace App\Console\Commands;
use Illuminate\Console\Command;
use App\Services\SmsNotificationService;
class CheckSubscriptionExpirationCommand extends Command{
protected $signature = 'subscriptions:check';
public function handle(SmsNotificationService $smsService){
$adminUser = // retrieve admin
$expiredUsers = // retrieve expired users
// for each user
$smsService->subscriptionExpired($user, $admin);
return 0;
}
}When the owner decides to switch notifications from SMS to email, every place that directly uses the SMS service must be changed.
Principle
Introduce an interface that defines the contract for notification services:
// app/Interfaces/INotification.php
namespace App\Interfaces;
use App\Models\User;
class INotification{
public function sendRegistrationNotification(User $user);
public function subscriptionExpired(User $user, User $admin);
}Implement both SMS and Email services adhering to this interface:
// app/Services/SmsNotificationService.php
namespace App\Services;
use App\Models\User;
use App\Interfaces\INotification;
class SmsNotificationService implements INotification{
public function sendRegistrationNotification(User $user){
// send SMS
}
public function subscriptionExpired(User $user, User $admin){
// send SMS about expiration
}
} // app/Services/EmailNotificationService.php
namespace App\Services;
use App\Models\User;
use App\Interfaces\INotification;
class EmailNotificationService implements INotification{
public function sendRegistrationNotification(User $user){
// send email
}
public function subscriptionExpired(User $user, User $admin){
// send email about expiration
}
}Update the controller and command to depend on the interface instead of a concrete class:
// app/Http/Controllers/AuthController.php
namespace App\Http\Controllers;
use App\Http\Controllers\Controller;
use App\Interfaces\INotification;
use Illuminate\Http\Request;
class AuthController extends Controller{
public function __construct(private INotification $notificationService){}
public function Register(Request $request){
// register user
...
$this->notificationService->sendRegistrationNotification($user);
}
} // app/Console/Commands/CheckSubscriptionExpirationCommand.php
namespace App\Console\Commands;
use Illuminate\Console\Command;
use App\Interfaces\INotification;
class CheckSubscriptionExpirationCommand extends Command{
protected $signature = 'subscriptions:check';
public function handle(INotification $notificationService){
$adminUser = // retrieve admin
$expiredUsers = // retrieve expired users
// for each user
$notificationService->subscriptionExpired($user, $admin);
return 0;
}
}Finally, bind the interface to the desired implementation in the service container so the framework resolves the correct class automatically:
// app/Providers/AppServiceProvider.php
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
use App\Interfaces\INotification;
use App\Services\EmailNotificationService;
class AppServiceProvider extends ServiceProvider{
public function register(){
$this->app->bind(
INotification::class,
EmailNotificationService::class
);
}
}With this setup, switching the notification method only requires changing the binding in one place, demonstrating how programming to an interface decouples code, enhances maintainability, and prepares the system for future extensions.
php中文网 Courses
php中文网's platform for the latest courses and technical articles, helping PHP learners advance quickly.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.