Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is calling a Controller From Another Controller a good practice in Laravel?

I was able to implement a PaypalController, with a reusable postPayment() method, which accepts items and their prices, and creates a Paypal payment, and redirects to a Paypal payment page.

class PaypalController extends Controller {

    private static $_api_context;

    private static function initialize() {
        //initialize api context
    }

    public static function postPayment($items, $currency, $description) {
        self::initialize();

        //create item list, transaction, payment objects, etc

        $payment->create(PaypalController::$_api_context);
        ...
        return redirect()->away($redirect_url); // redirect to paypal
    }
}

PaypalController is called statically by other controllers. For example, the AuthController might call it to request payment from the user right after the user registers to my site:

class AuthController extends Controller {
    public function postRegister(Request $request) {
        return PaypalController::postPayment($items, 'JPY', 'description');
    }
}

Basically, PaypalController returns a Redirect to AuthController, which also returns it, to perform the redirect to the Paypal payment page.

I was wondering if this is a good design - a controller calling a different controller, is it?

If not, what would be a better way to do this? Maybe move my code from PaypalController into custom Service Provider, or custom Helper, or something else? I am very new to Laravel, and I would appreciate some guidance.

like image 465
Obay Avatar asked Feb 26 '16 07:02

Obay


2 Answers

No it's not a good practice. You should abstract the business logic to a service/repository class. So for example:

Create an interface as Contract:

namespace App\Services\Paypal;

interface PaypalInterface {

     public function PostRegister(Array $array, /*More $params if necessary*/); 
}

Then implement the Contract:

namespace App\Services\Paypal;

class PaypalService implements PaypalInterface {

    // Must match the method signature declared in the interface
    public function PostRegister(Array $array, /*$More $params if necessary*/) {

        // Do the process here
    }
}

Then use the contract/interface as dependency. So, in your PaypalController or in any other Controller you may (re)use it like:

namespace App\Http\Controllers;

use App\Http\Request;
use App\Services\Paypal\PaypalInterface;

class AuthController extends Controller {
    public function postPayment(Request $request, PaypalInterface $paypalService) {
        return $paypalService->postRegister($request->all());
    }
}

In this case, register the binding (interface to implementation) in a service provider (Basically in AppServiceProvider). That's the basic workflow. Why an interface because, Controllers (Client/Consumer classes) should talk to Contract/Interface instead of a concrete implementation.

This article of mine may help you but remember this is not the 100% decoupled, it's still coupled with Laravel framework and you can even more decouple the Service.


Note: It's a best practice but don't blindly follow this approach for every projects/problems, just chose wisely when you should do it, it really depends on the context but don't just die for it. The current context is fine to follow this.

like image 72
The Alpha Avatar answered Sep 30 '22 05:09

The Alpha


The fact that your postPayment is a static method is a code smell for me that tells "Nope, not inside a controller".

  • As you said, I think a service would be a better place for that, you can take a look at Omnipay if you want to.

  • You can make PaypalController abstract too and AuthController would extend this one (only if you have to use postPayment inside multiple Controllers).

  • You can do a PaypalTrait and use it inside your AuthController (only if you have to use postPayment inside multiple Controllers).

  • Of course you can combine the first solution with the others if it makes sense to you.

There are many answers for this question and I don't think that the perfect one exists, it really depends on what you're building and what you need.

like image 29
Elie Faës Avatar answered Sep 30 '22 06:09

Elie Faës