7

I have a following structure to use Open Close Principle

class Payment{ 

    //this is not a model class
    // according to OC principle this class should not focus on the implementation

    private $paymentInterface;

    public function __construct(PaymentInterface $paymentInterface)
    {
        $this->paymentInterface = $paymentInterface;
    }


    //so store method does not know which implementation it will get
    public function store($request,$id)
    {
        return $this->paymentInterface->store($request,$id);
    }

}

Interface

interface PaymentInterface{
    public function store($request,$id = null);
}

Payment Service Class containing implementation

class PaymentService implements PaymentInterface{
    public function store($request,$id = null){
        //payment store logic is here
    }
}

Controller

class PaymentsController extends Controller{

    protected $payment;

    public function __construct()
    {
        $this->payment = new Payment(new PaymentService);
    }

    public function storePayment(PaymentRequest $request, $id)
    {
        try {
             $response = $this->payment->store($request,$id);
             return redirect()->route($this->route.'.index')->with($response['status'],$response['message']);
        } catch (\Exception $e) {
            return $this->vendorDashboard($e);
        }
    }
}

My question is: Is it correct approach to use Open-Close-Principle ? Using above code I can tell controller that I can use PaymentService class for the implementation.

$payment = new Payment(new PaymentService);
return $payment->store($request,$id);

If later I want to make a payment in different way e.g. make a payment through invoice then I can create new controller, write new implementation in new class e.g. InvoicePaymentService and tell Payment class to use InvoicePaymentService as implementation

$payment = new Payment(new InvoicePaymentService);
return $payment->store($request,$id);

OR

$payment = new Payment(new PayPalPaymentService);
return $payment->store($request,$id);

OR

$payment = new Payment(new AliPayPaymentService);
return $payment->store($request,$id);

I know I can bind Interface with a class through a service provider but if I want to implement a different payment implementation then I will not be able to change the class, right ?

If I am doing it in wrong way please let me know.

Afraz Ahmad
  • 5,193
  • 28
  • 38
  • "but if I want to implement a different payment implementation" - do you mean different Payment class implementation? – ata Mar 07 '19 at 10:19
  • Yes. I have mentioned in question. Like I want to do payment with PayPal or AliPay – Afraz Ahmad Mar 07 '19 at 10:20

1 Answers1

7

This is what service container stands for. You should use contextual binding

Assuming you have an interface: FooInterface

And you have two concrete implementations: GoodFoo and BadFoo

In order to inject different implementations to controllers (or other classes) you must tell it to laravel.

$this->app->when(GoodController::class)
      ->needs(FooInterface::class)
      ->give(function () {
            return new GoodFoo();
      });


$this->app->when(BadController::class)
      ->needs(FooInterface::class)
      ->give(function () {
            return new BadFoo();
      });

And controllers should be:

class GoodController extends Controller
{
    protected $foo;

    public function __construct(FooInterface $foo)
    {
        $this->foo = $foo;
    }
}

class BadController extends Controller
{
    protected $foo;

    public function __construct(FooInterface $foo)
    {
        $this->foo = $foo;
    }
}

Please note that most of the time laravel promotes bad software design principles and it rather hard to practise SOLID principles in laravel.

Yarimadam
  • 1,128
  • 9
  • 12
  • Great. this is what I was finding on the internet – Afraz Ahmad Mar 07 '19 at 10:30
  • That means we don't have to write a generic class. We can directly connect implementations with interfaces on the basis of different controllers, right ? – Afraz Ahmad Mar 07 '19 at 10:38
  • 1
    It depends. If you have just one implementation and you don't plan to add another one in the future it's okay to not use an interface. – Yarimadam Mar 07 '19 at 10:41