从另一个控制器调用控制器是Laravel的一个好习惯吗?

时间:2016-02-26 07:27:54

标签: php laravel paypal laravel-5.1

我能够使用可重复使用的PaypalController方法实现postPayment(),该方法接受商品及其价格,并创建Paypal付款,并重定向到Paypal付款页。

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由其他控制器静态调用。例如,AuthController可能会在用户注册到我的网站后立即调用它来请求用户付款:

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

基本上,PaypalController会将Redirect返回给AuthController,并返回它,以执行重定向到Paypal付款页面。

我想知道这是不是一个好的设计 - 控制器调用不同的控制器,是吗?

如果没有,那么更好的方法是什么?也许将我的代码从PaypalController转移到自定义服务提供程序,或自定义助手,或其他什么?我对Laravel很新,我很感激一些指导。

3 个答案:

答案 0 :(得分:2)

不,这不是一个好习惯。您应该将业务逻辑抽象为服务/存储库类。例如:

创建一个接口作为合同:

namespace App\Services\Paypal;

interface PaypalInterface {

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

然后实施合同:

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
    }
}

然后使用contract / interface作为依赖项。因此,在您的PaypalController或任何其他控制器中,您可以(重新)使用它,如:

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());
    }
}

在这种情况下,服务提供商中的register the binding(实现接口)(基本上在AppServiceProvider中)。这是基本的工作流程。为什么是接口,因为控制器(客户端/消费者类)应该与Contract / Interface通信而不是具体的实现。

This article of mine may help you但请记住,这不是100%解耦,它仍然与Laravel框架相结合,您甚至可以将服务解耦。

注意:这是一种最佳实践,但不要盲目地对每个项目/问题采用这种方法,只要明智地选择应该这样做,它实际上取决于上下文,但不要只为它而死。目前的情况可以遵循这一点。

答案 1 :(得分:1)

你的postPayment是一种静态方法这一事实对我而言是一种代码味道,它告诉了#34; Nope,而不是在控制器内#34;

  • 正如您所说,我认为服务会是一个更好的地方,如果您愿意,可以查看Omnipay

  • 您也可以将PaypalController抽象化,AuthController将扩展此项(仅当您必须在多个控制器中使用postPayment时)。

  • 您可以执行PaypalTrait并在AuthController内使用它(仅当您必须在多个控制器中使用postPayment时)。

  • 当然,如果对您有意义,您可以将第一个解决方案与其他解决方案结合起来。

这个问题有很多答案,我不认为存在完美的答案,这实际上取决于你的建设和需要。

答案 2 :(得分:1)

这不是正确的方法。 Rember,控制器应该只接受请求并将其发送到正确的服务,而不是自己处理商务逻辑。

此规则适用:当您需要从另一个控制器调用控制器方法时,它会闻到错误的代码

相反,使用服务提供者和服务类来处理您的paypal逻辑。服务类将处理paypal逻辑,稍后当您在控制器中需要它时将使用它:

//SERVICE CLASS
class PayPalService
{
    public function processPayment(){ //... }
}

服务提供商用于在应用程序中注册服务类:您告诉Laravel当您需要PayPalService它应该构建并为您返回时

//SERVICE PROVIDER: binds the creation of the service in the ioc container
class PayPalServiceProvider extends ServiceProvider
{
    public function register()
    {
        //use singleton or bind to bind the service in the ioc container
        $this->app->singleton( PayPalService::class, function()
        {
            return new PayPalService();
        });
    }
}

然后,当您需要PayPalService课程时,您应该让Laravel在您的控制器中自动注入服务:

class AuthController extends Controller 
{   
    public function postRegister(Request $request, PayPalService $paypal) {
        return $paypal->processPayment();
    }
}

如果需要,可以使用服务类的接口

进一步改进此设计