[英]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的方法更好:
您的模型應僅包括邏輯部分:這包括與數據庫的通信(事務和回滾), 而不包括要打印給用戶的錯誤消息的格式。
保持模型干凈:這是MVC結構中最重要的部分。 如果搞砸了,將很難發現任何錯誤。
外包錯誤處理:如果將其放在控制器中,則可以選擇在其中進行處理(也許您需要此方法的某些特殊格式的輸出,或者需要一些其他函數來調用),或者在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提供了一些不錯且方便的方式來組織代碼。 您可以將Event
和Observers
用於模型中的並發操作。
最佳實踐因用戶的知識和經驗而異。 誰知道,您問題的最佳答案尚未到來。
我寧願保留與模型交互的控制器和系統的任何其他部分,盡可能與模型的內部運作方式無關。 因此,例如,我將嘗試避免在模型之外意識到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.