簡體   English   中英

具有數據庫事務的Laravel控制器模型異常處理結構

[英]Laravel Controller-Model Exception Handling structure with database transactions

關於體系結構,將異常從模型扔到控制器時,哪兩種是好的做法?

結構A:

UserController.php

public function updateUserInfo(UserInfoRequest $request, UserModel $userModel)
{
    $isError = false;
    $message = 'Success';

    try {
        $message = $userModel->updateUserInfo($request->only(['username', 'password']));
    } catch (SomeCustomException $e) {
        $isError = true;
        $message = $e->getMessage();
    }

    return json_encode([
        'isError' => $isError,
        'message' => $message
    ]);
}

UserModel.php

public function updateUserInfo($request)
{
    $isError = false;
    $message = 'Success';

    $username = $request['username'];
    $password = $request['password'];

    try {
        $this->connect()->beginTransaction();

        $this->connect()->table('users')->where('username', $username)->update(['password' => $password]);

        $this->connect()->commit();
    } catch (\Exception $e) {
        $this->connect()->rollback();
        $isError = true;
        $message = $e->getMessage();        
    }

    return [
        'isError' => $isError,
        'message' => $message
    ];
}

結構B:

UserController.php

public function updateUserInfo(UserInfoRequest $request, UserModel $userModel)
{
    $isError = false;
    $message = 'Success';

    try {
        $userModel->updateUserInfo($request->only(['username', 'password']));
    } catch (SomeCustomException $e) {
        $isError = true;
        $message = $e->getMessage();
    } catch (QueryException $e) {
        $isError = true;
        $message = $e->getMessage();    
    }

    return json_encode([
        'isError' => $isError,
        'message' => $message
    ]);
}

UserModel.php

public function updateUserInfo($request)
{
    $username = $request['username'];
    $password = $request['password'];

    try {
        $this->connect()->beginTransaction();

        $this->connect()->table('users')->where('username', $username)->update(['password' => $password]);

        $this->connect()->commit();
    } catch (\Exception $e) {
        $this->connect()->rollback();
        throw new QueryException();
    }
}

結構A中 ,模型捕獲任何異常,回滾事務,如果有錯誤或沒有錯誤返回給控制器。 然后,控制器僅返回從模型返回的任何內容。

當模型在結構B中捕獲任何異常時,回滾事務,如果發生異常,則拋出QueryException。 然后,控制器從模型中捕獲拋出的QueryException,然后捕獲返回的錯誤(如果有錯誤或沒有錯誤)。

結構B仍然存在問題的原因是模型應該是進行回滾的模型。 如果我要在這里刪除模型的try-catch,而控制器直接捕獲異常,那么回滾將在控制器上進行,我認為這會使控制器的功能混亂。

讓我知道你的想法。 謝謝!

為什么我認為B的方法更好:

  1. 您的模型應僅包括邏輯部分:這包括與數據庫的通信(事務和回滾), 而不包括要打印給用戶的錯誤消息的格式。

  2. 保持模型干凈:這是MVC結構中最重要的部分。 如果搞砸了,將很難發現任何錯誤。

  3. 外包錯誤處理:如果將其放在控制器中,則可以選擇在其中進行處理(也許您需要此方法的某些特殊格式的輸出,或者需要一些其他函數來調用),或者在App\\Exceptions\\Handler 在這種情況下,您可以在此處呈現此錯誤消息,而不必在控制器中執行。

因此,如果您不需要任何特殊的函數調用並且想使用Laravel的全部功能,我建議您使用Structure C

UserController.php

public function updateUserInfo(UserInfoRequest $request, UserModel $userModel)
{
    $userModel->updateUserInfo($request->only(['username', 'password']));
    return response()->json(['message' => 'updated user.']); 
}

UserModel.php

public function updateUserInfo($request)
{
    $username = $request['username'];
    $password = $request['password'];
    try {
        $this->connect()->beginTransaction();

        $this->connect()->table('users')->where('username', $username)->update(['password' => $password]);

        $this->connect()->commit();
    } catch (\Exception $e) {
        $this->connect()->rollback();
        throw new QueryException();
    }
}

應用\\異常\\處理程序

public function render($request, Exception $exception)
{
    //catch everything what you want
    if ($exception instanceof CustomException) {
        return response()->json([
          'message' => $exception->getMessage()
        ], 422);
    }

    return parent::render($request, $exception);
}

您將數據庫內容(模型),表示內容(控制器)和錯誤處理(處理程序)完全分開。 結構C允許您在其他控制器功能相同的情況下,在其他功能中重用錯誤處理。

這是我的觀點,但是我願意討論任何您認為這種方法不是最佳解決方案的情況。

首先 ,對於您的示例,您甚至不需要使用Transaction。 您僅執行一個查詢。 那么為什么需要回滾? 您要回滾哪個查詢? 當您需要完全處理一組更改以使操作完整且有效時,應使用事務。 如果第一個成功,但是以下任何一個有任何錯誤,則可以回滾所有內容,就像什么都沒做一樣。

其次,讓我們指出良好實踐或最佳實踐。 Laravel建議使用Thin控制器和Thin模型。 因此,您的所有業務邏輯都應該在模型中,甚至應該在存儲庫中更好。 控制器將充當經紀人。 它將從存儲庫或模型中收集數據並將其傳遞給視圖。

另外 ,laravel提供了一些不錯且方便的方式來組織代碼。 您可以將EventObservers用於模型中的並發操作。

最佳實踐因用戶的知識和經驗而異。 誰知道,您問題的最佳答案尚未到來。

我寧願保留與模型交互的控制器和系統的任何其他部分,盡可能與模型的內部運作方式無關。 因此,例如,我將嘗試避免在模型之外意識到QueryException ,而是盡可能將其視為純PHP對象。

另外,我會避免使用自定義JSON響應結構,而使用HTTP狀態 如果有道理,也許更新用戶信息的路由返回更新后的資源,或者200 OK就足夠了。

// UserModel.php
public function updateUserInfo($request)
{
    $username = $request['username'];
    $password = $request['password'];

    try {
        $this->connect()->beginTransaction();

        $this->connect()->table('users')->where('username', $username)->update(['password' => $password]);

        $this->connect()->commit();

        return $this->connect()->table('users')->where('username', $username)->first();
        // or just return true;
    } catch (\Exception $e) {
        $this->connect()->rollback();

        return false;
    }
}

// UserController.php    
public function updateUserInfo(UserInfoRequest $request, UserModel $userModel)
{
    $updated = $userModel->updateUserInfo($request->only(['username', 'password']));

    if ($updated) {
        return response($updated);
        // HTTP 200 response. Returns JSON of updated user.
        // Alternatively,
        // return response('');
        // (200 OK response, no content)
    } else {
        return response('optional message', 422);
        // 422 or any other status code that makes more sense in the situation.
    }

(完全偏離主題,我想這是一個例子,但以防萬一,提醒不要存儲純文本密碼。)

我不明白,為什么您不觀看Jeffry課,但是對於更新用戶,您不需要try / catch部分。 您的控制器方法:

public function update(UpdateUserRequest $request, User $user) : JsonResponse
{
   return response()->json($user->update($request->all()))
}

您請求規則方法:

public function rules(): array
{
    return [
        'username' => 'required|string',
        'password' => 'required|min:6|confirmed',
    ];
}

然后您使用Exception Handler渲染方法:

public function render($request, Exception $exception)
{
    if ($request->ajax() || $request->wantsJson()) {
        $exception = $this->prepareException($exception);

        if ($exception instanceof \Illuminate\Http\Exception\HttpResponseException) {
            return $exception->getResponse();
        } elseif ($exception instanceof \Illuminate\Auth\AuthenticationException) {
            return $this->unauthenticated($request, $exception);
        } elseif ($exception instanceof \Illuminate\Validation\ValidationException) {
            return $this->convertValidationExceptionToResponse($exception, $request);
        }

        // we prepare custom response for other situation such as modelnotfound
        $response = [];
        $response['error'] = $exception->getMessage();

        if (config('app.debug')) {
            $response['trace'] = $exception->getTrace();
            $response['code'] = $exception->getCode();
        }

        // we look for assigned status code if there isn't we assign 500
        $statusCode = method_exists($exception, 'getStatusCode')
            ? $exception->getStatusCode()
            : 500;

        return response()->json($response, $statusCode);
    }
    return parent::render($request, $exception);
}

現在,如果您有異常,Laravel在Json中為您提供狀態代碼!= 200,否則給出成功結果!

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM