首页 > 解决方案 > Laravel:控制器或中间件

问题描述

我正在使用 Laravel 6.9.0。这是我的付款控制器:

class PaymentController extends Controller
{
    public function __construct(){
        $this->middleware('payment.test');
        $this->middleware('payment.check');
    }

    public function pay(){
        $this->payment->pay();
    }

    public function refund(){
        $this->payment->refund();
    }

    public function checkOrder(){
        $this->payment->checkOrder();
    }
}

payment.test中间件做了这些:

  1. 写请求日志
  2. 检查商家是否存在
  3. 解密请求

payment.check中间件用于检查它是哪种付款方式,例如 applepay 或 googlelpay 。

但是从官方文档来看,中间件是用来过滤 HTTP 请求的,验证你的应用程序的用户是否经过身份验证。

好像payment.check不属于这个。因此,我将payment.check中间件更改为控制器。因为所有的方法都要检查支付,所以我把它放在了构造函数中。但是,我必须在检查之前解密请求,所以我的构造函数现在是

$this->middleware('payment.test');

$this->middleware(function ($request, $next) {
    $this->checkPayment($request);

    return $next($request);
});

checkPayment看起来像这样:

private function checkPayment($request){
    if($request->aaa == 'aaa'){
        switch($request->type){
            case '001':
                $type = 'apple';
                break;
            case '111':
                $type = 'google';
                break;
            ...

        }
    }else{
        switch($request->code){
            case 'android':
                $type = 'android';
                break;
            ...
        }
    }


    $this->payment = app($type);
}

它使我的控制器丑陋。我觉得将它写入中间件看起来更加模块化和清晰。将它写入控制器真的比中间件更好吗?或者有什么其他建议?

标签: laravelcontrollermiddleware

解决方案


正如我所看到的那样,您的整个问题是,即使您的逻辑最终通过调用来实例化正确的实现也是如此$this->payment = app($type);。核心概念还是在这个上下文中,你需要哪个提供者。这有很多选项,服务,工厂等,但是做一个容器提供者版本,当你在Laravel.

想象一下拥有你的PaymentProviderInterface,我会绑定它来解决使用哪个支付提供商。将它与您在任何情况下都可以解析请求对象相结合,您可以将所有这些移动到提供者,我会创建一个新的。PaymentProvider并将其注册为任何其他提供者。

class PaymentsProvider {
    public const APPLE_PAYMENT_PROVIDER = 'apple';

    public const GOOGLE_PAYMENT_PROVIDER = 'google';

    public const ANDROID_PAYMENT_PROVIDER = 'android';

    public function boot() {
        $this->app->bind(PaymentProviderInterface::class, function () {
           /** @var Request $request **/
           $request = resolve(Request::class);

           $type = null;

           if($request->aaa == 'aaa') {
               $type = $this->resolvePaymentByType($request->type);
           } else {
               $type = $this->resolvePaymentByCode($request->code);
           }

           return resolve($type);
        });
    }

    private function resolvePaymentByType(string $type): string
    {
        if ($type === '001') {
            return static::APPLE_PAYMENT_PROVIDER;
        }

        if ($type === '111') {
            return static::GOOGLE_PAYMENT_PROVIDER;
        }

        throw new Exception('Invalid state');
    }

    private function resolvePaymentByCode(string $code): string
    {
        if ($type === static::ANDROID_PAYMENT_PROVIDER) {
            return static::ANDROID_PAYMENT_PROVIDER;
        }

        throw new Exception('Invalid state');
    }
}

一般来说,我不喜欢开关盒,如果你喜欢,你可以使用它们,我认为这更干净。另一个小的优化,而不是让魔术字符串使用常量,它更具可读性并且有很多好处。由于我们将所有内容绑定到PaymentProviderInterface,因此通过像这样解析您的提供者,这将使您的控制器真正干净。为了避免您没有请求的情况,我会在每种方法中注入。

class PaymentController extends Controller
{
    public function pay(PaymentProviderInterface $payment){
        $payment->pay();
    }

推荐阅读