[hyperf]關於資料返回封裝的另一種實現思考

Colorado發表於2021-06-26

資料返回都需要這樣的封裝,return $this->success($result);

class IndexController extends AbstractController
{
    public function index()
    {
        $user = $this->request->input('user', '399001');
        $method = $this->request->getMethod();

        $result = [
            'method'  => $method,
            'message' => "Hello {$user}.",
        ];

        return $this->success($result);
    }
}

一般的實現方式是在基類中實現success方法,但會讓基類愈加龐大,於是改良使用trait,基類中引入trait,這樣也挺好。
突然無聊想到的方法,感覺挺好的:定義返回服務類,使用 __call 在基類中呼叫服務類的方法。感覺能更方便的解耦耶,貼程式碼

ResponseService

<?php


namespace App\Service\Utils;


use Hyperf\Di\Annotation\Inject;
use Hyperf\HttpServer\Contract\ResponseInterface;

/**
 * API請求返回服務類
 *
 * Class ResponseService
 *
 * @package App\Service\Utils
 */
class ResponseService
{
    /**
     * @var int
     */
    private $http_code = 200;

    /**
     * http頭部資訊
     *
     * @var string[]
     */
    private $http_headers = [
        'Author' => 'Colorado',
    ];

    /**
     * 業務返回碼
     * 組成結構:xx(業務模組) xx(業務子模組) xx(細化編碼)
     * 舉例:110101 11(APP模組)01(鑑權模組)01(登入失敗)
     *
     * @var int
     */
    private $business_code = 100000;

    /**
     * 業務返回訊息
     *
     * @var string
     */
    private $business_msg = 'ok';

    /**
     * @Inject
     * @var ResponseInterface
     */
    private $response;

    /**
     * 設定http狀態碼
     *
     * @param int $code
     *
     * @return $this
     */
    public function setHttpCode(int $code = 200): self
    {
        $this->http_code = $code;

        return $this;
    }

    /**
     * 設定http頭部資訊
     *
     * @param string $name
     * @param mixed  $value
     *
     * @return $this
     */
    public function setHttpHeader(string $name, $value): self
    {
        $this->http_headers[$name] = (string)$value;

        return $this;
    }

    /**
     * 成功資料返回
     *
     * @param mixed $data           返回資料
     * @param int   $business_code  業務返回碼
     *
     * @return ResponseInterface|\Psr\Http\Message\ResponseInterface
     */
    public function success($data, int $business_code = 100000)
    {
        $this->business_code = $business_code;

        return $this->response($data);
    }

    /**
     * 失敗返回
     *
     * @param string $error_msg      錯誤資訊
     * @param mixed  $data           返回資料
     * @param int    $business_code  錯誤業務碼
     *
     * @return ResponseInterface|\Psr\Http\Message\ResponseInterface
     */
    public function fail(string $error_msg = 'fail', $data = null, int $business_code = 999999)
    {
        $this->business_code = $business_code;
        $this->business_msg = $error_msg;

        return $this->response($data);
    }

    /**
     * 返回資料
     *
     * @param $data
     *
     * @return ResponseInterface|\Psr\Http\Message\ResponseInterface
     */
    private function response($data)
    {
        $this->response = $this->response->json($this->normalizeData($data))->withStatus($this->http_code);

        if (! empty($this->http_headers)) {
            foreach ($this->http_headers as $name => $value) {
                $this->response = $this->response->withHeader($name, $value);
            }
        }

        return $this->response;
    }

    /**
     * 標準化返回資料格式
     *
     * @param mixed $data  業務返回資料
     *
     * @return array
     */
    private function normalizeData($data): array
    {
        return [
            'code'      => $this->business_code,
            'data'      => $data,
            'message'   => $this->business_msg,
            'timestamp' => time(),
        ];
    }
}

基類AbstractController

<?php

declare(strict_types=1);
/**
 * This file is part of Hyperf.
 *
 * @link     https://www.hyperf.io
 * @document https://hyperf.wiki
 * @contact  group@hyperf.io
 * @license  https://github.com/hyperf/hyperf/blob/master/LICENSE
 */
namespace App\Controller;

use App\Service\Utils\ResponseService;
use Hyperf\Di\Annotation\Inject;
use Hyperf\HttpServer\Contract\RequestInterface;
use Hyperf\HttpServer\Contract\ResponseInterface;
use Psr\Container\ContainerInterface;

/**
 * Class AbstractController
 * @method ResponseService setHttpCode(int $code = 200)
 * @method ResponseService setHttpHeader(string $name, $value)
 * @method success(mixed $data, int $business_code = 100000)
 * @method fail(string $error_msg = 'fail', mixed $data = null, int $business_code = 999999)
 * @package App\Controller
 */
abstract class AbstractController
{
    /**
     * @Inject
     * @var ContainerInterface
     */
    protected $container;

    /**
     * @Inject
     * @var RequestInterface
     */
    protected $request;

    /**
     * @Inject
     * @var ResponseInterface
     */
    protected $response;

    /**
     * @param $name
     * @param $arguments
     *
     * @return mixed
     */
    public function __call($name, $arguments)
    {
        if (method_exists(ResponseService::class, $name)) {
            return make(ResponseService::class)->{$name}(...$arguments);
        }
    }
}

使用demo

控制器中

class IndexController extends AbstractController
{
    public function index()
    {
        $user = $this->request->input('user', '399001');
        $method = $this->request->getMethod();

        $result = [
            'method'  => $method,
            'message' => "Hello {$user}.",
        ];

        // 成功返回
        return $this->success($result);
        // 失敗返回
        return $this->fail('失敗啦');
        // 設定http狀態碼
        return $this->setHttpCode(201)->success($result);
        return $this->setHttpCode(500)->fail('伺服器掛了');
        // 設定http頭部資訊
        return $this->setHttpHeader('server', 'hyperf-server')->success($result);
    }
}

其他地方,如ExceptionHandler

使用make該類即可呼叫類的方法

class ValidationExceptionHandler extends ExceptionHandler
{
    public function handle(Throwable $throwable, ResponseInterface $response)
    {
        $this->stopPropagation();
        $body = $throwable->validator->errors()->first();

        return make(ResponseService::class)->fail($body);
    }

    public function isValid(Throwable $throwable): bool
    {
        return $throwable instanceof ValidationException;
    }
}
本作品採用《CC 協議》,轉載必須註明作者和本文連結

相關文章