Obay Obay - 1 year ago 107
PHP Question

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.

Answer Source

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.

Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download