Laravel 同时处理来自 API 和表单的请求的最佳策略

Laravel best strategy to serve requests from API and form at the same time

使用 Laravel 7.*,我的任务是创建一个简单的应用程序来发送付款请求,用户填写表格并发送数据,然后我验证用户输入并创建一个新的付款实例.

然后用户被重定向回同一页面。 (当然还有其他列出所有付款和更新付款的请求):

   //In PaymentController.php

   public function store()
    {
        $inputData = $this->validateRequest();

        $person = $this->personRepository->findOneByAttribute('id_number', request('id_number'));

        if ($person instanceof Person) {
            $this->paymentRepository->create($inputData, $person);
            return back()->with('successMessage', 'Your payment request registered successfully.');
        } else {
            return back()->with('failureMessage', 'Shoot! Cannot find a peron with the given Identification Number.')->withInput();
        }
    }

一切都很好,但我需要实现一个 Restful API 来执行相同的请求并获得有效的 json 响应,假设没有前端 JavaScript 框架,实现这个目标的最佳方法是什么?

我应该创建一个单独的控制器吗?或者简单地检查请求是从传统表单还是 API 客户端发送的?我是否缺少设计模式?

最简单的方法是检查您应该发回什么样的响应:

   //In PaymentController.php

   public function store()
    {
        $inputData = $this->validateRequest();

        $person = $this->personRepository->findOneByAttribute('id_number', request('id_number'));

        if ($person instanceof Person) {
            $this->paymentRepository->create($inputData, $person);
            if (request()->expectsJson()) {
                return response('', 201); // Just a successfully created response 
            }
            return back()->with('successMessage', 'Your payment request registered successfully.');
        } else {
            if (request()->expectsJson()) {
                return response()->json([ 'error' => 'Not found' ], 404); // You can change the error code and message (or remove the message) as needed
            }
            return back()->with('failureMessage', 'Shoot! Cannot find a person with the given Identification Number.')->withInput();
        }
    }

您当然可以选择将其封装在实现 Responsible 接口的 class 中

class PaymentResponse implements Responsible {
     private $success;
     public function __construct($success) {
           $this->success = $success;
     }

     public function toResponse($request) {
         if ($this->success) {
            if (request()->expectsJson()) {
                return response()->json([ 'error' => 'Not found' ], 404); // You can change the error code and message (or remove the message) as needed
            }
            return back()->with('failureMessage', 'Shoot! Cannot find a person with the given Identification Number.')->withInput();
          } 
          if (request()->expectsJson()) {
              return response()->json([ 'error' => 'Not found' ], 404); // You can change the error code and message (or remove the message) as needed
          }
          return back()->with('failureMessage', 'Shoot! Cannot find a person with the given Identification Number.')->withInput();
     }
  
}

那么您的代码将是:

//In PaymentController.php

   public function store()
    {
        $inputData = $this->validateRequest();

        $person = $this->personRepository->findOneByAttribute('id_number', request('id_number'));

        if ($person instanceof Person) {
            $this->paymentRepository->create($inputData, $person);
            return new PaymentResponse(true);
        } else {
            return new PaymentResponse(false);
        }
    }

您当然也可以将控制器逻辑提取到一个单独的库中,然后有两个单独的控制器方法,如果需要,仍然可以使用负责的对象。这实际上取决于您的用例以及最适合您的方法

我认为另一种方法是使用服务存储库模式。您将应用程序服务包装在一个单独的 class 中。服务是控制器和存储库之间的交互器。流程看起来像 [request] -> [controller] -> [service] -> [repository].

通过利用此模式,您可以在应用程序的不同区域重新使用您的服务。例如,您可以通过返回 JSON 数据,拥有一个专门用于服务传统 Web 应用程序的控制器和一个用于服务 SPA 的控制器,但服务于相同的业务流程。

例如:

付款响应:

class PaymentStoreResponse
{
    protected $message;
    protected $code;
    protected $extraData;

    public function __construct($message, $code, $extraData = "")
    {
        $this->message = $message;
        $this->code = $code;
        $this->extraData = $extraData;
    }

    public function getMessage()
    {
        return $this->message;
    }

    public function getCode()
    {
        return $this->code;
    }
    
    public function getExtraData()
    {
        return $this->extraData;
    }
}

支付服务:

function store($data)
{
    $person = $this->personRepository->findOneByAttribute('id_number', $data('id_number'));

    if ($person instanceof Person) {
        $this->paymentRepository->create($inputData, $person);
        return new PaymentResponse("paymentSuccess", 201, "successMessage");
    } else {
        return new PaymentResponse("notFound", 404, "failureMessage");
    }
}

控制器:

// App\Controllers\Web\PaymentController.php
function store(Request $request)
{
    $inputData = $this->validateRequest();
    $response = $this->paymentService->store($inputData);
    
    return back()->with($response->getExtraData(), $response->getMessage()) 
}


// App\Controllers\Api\PaymentController.php
function store(Request $request)
{
    // validation might be different because
    // api might need to authenticate with jwt etc.
    $inputData = $this->validateRequest();
    $response = $this->paymentService->store($inputData);

    return response()->json(['message' => $response->getMessage()], $response->getCode());
}

这将导致控制器更简洁,因为控制器将仅处理请求验证和响应,同时将业务流程委托给服务 class(支付服务)。业务逻辑也集中在服务层,这意味着如果业务发生变化,它将应用到 API 控制器和 Web 控制器。因此,它将使您免于重构噩梦。

您可以探索不同的架构,例如 Clean Architecture + DDD。让你的开发体验更好,领域逻辑集中,层与层之间依赖抽象实现低耦合。