[英]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.