Simplifying Service Selection in Laravel Using Resolvers

Published: (December 27, 2025 at 10:34 AM EST)
2 min read
Source: Dev.to

Source: Dev.to

The Problem: Too Many if‑else Conditions

if ($paymentType === 'stripe') {
    $service = new StripePaymentService();
} elseif ($paymentType === 'paypal') {
    $service = new PaypalPaymentService();
} else {
    throw new Exception('Invalid payment type');
}

$service->charge($data);

What’s wrong with this approach?

  • Business logic is tightly coupled to concrete implementations.
  • Adding a new payment gateway requires modifying existing code.
  • Hard to test and maintain.
  • Violates the Open/Closed Principle.

The code works, but it doesn’t scale well.

What Is a Resolver?

A Resolver is a class responsible for deciding which service implementation should be used based on runtime data. In simple terms, a resolver returns the correct service class for you, keeping your business logic clean.

Real‑World Example: Payment Service Resolver

Step 1: Create a Common Interface

 $this->stripePaymentService,
            'paypal' => $this->paypalPaymentService,
            default => throw new InvalidArgumentException('Unsupported payment type'),
        };
    }
}

Why Use Constructor Injection?

  • Makes dependencies explicit and easier to understand.
  • Improves unit testability (services can be mocked).
  • Keeps the resolver focused on selection logic.
  • Leverages Laravel’s dependency injection container.

Note: You could also resolve services using app() inside the resolver, but constructor injection is generally preferred for cleaner architecture and better testing.

Step 4: Use the Resolver in Your Business Logic

get('payment_type');

        $service = $resolver->resolve($paymentType);
        $service->charge($request->all());

        return response()->json([
            'message' => 'Payment processed successfully',
        ]);
    }
}

✨ Clean, readable, and easy to extend.

Adding a New Service Is Easy

Want to add Razorpay later?

  1. Create RazorpayPaymentService that implements PaymentServiceInterface.
  2. Update the resolver:
'razorpay' => $this->razorpayPaymentService,

No changes are needed in controllers or other business logic.

Benefits of Using Resolvers

  • Cleaner and more readable code.
  • Easy to add new services.
  • Follows SOLID principles.
  • Centralized service selection logic.
  • Simple to unit test.

Bonus: Making the Resolver More Flexible

Config‑Based Mapping

// config/payment.php
return [
    'stripe' => StripePaymentService::class,
    'paypal' => PaypalPaymentService::class,
];

Resolver Using the Config

<?php
class PaymentServiceResolver
{
    public function resolve(string $type): PaymentServiceInterface
    {
        $service = config("payment.$type");

        if (! $service) {
            throw new InvalidArgumentException('Unsupported payment type');
        }

        return app($service);
    }
}

This approach makes the system even more configurable.

Final Thoughts

Resolvers are a simple yet powerful pattern for managing multiple service implementations in Laravel. They help you:

  • Avoid messy conditionals.
  • Keep business logic clean.
  • Build systems that scale gracefully.

If you work with multiple APIs, payment gateways, or providers, resolvers can significantly improve your code quality.

Back to Blog

Related posts

Read more »

Laravel FAQs (Beginner to Advanced)

Why Laravel still matters and what this FAQ solves Laravel remains one of the fastest ways to ship secure, maintainable PHP applications — from simple sites to...