- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我正在 Lumen 框架中创建 api,最近我阅读了有关 DRY 和服务层的内容。直到今天我都没有在我的代码中使用这些,所有的逻辑都在 Controller 中。所以我想开始使用它,但我遇到了一些问题。
这是我的 Controller (UsersController.php) 的一部分,因为整个代码太长了。
<?php
namespace App\Http\Controllers;
use App\User;
use Illuminate\Http\Request;
class UsersController extends Controller
{
private $request;
public function __construct(Request $request) {
$this->request = $request;
}
public function destroy($id) {
$user = User::find($id);
if (!$user) {
return response()->json([
'error' => 'User not found'
], 404);
}
if ($user->role === 'admin') {
return response()->json([
'error' => 'You cant edit admin'
], 403);
}
$user->delete();
return response()->json([], 204);
}
}
看完这段代码后,我试图改变两件事。
UserService.php
<?php
namespace App\Services;
use App\User;
class UserService
{
public function getUserById($id)
{
$user = User::find($id);
if (!$user) {
return response()->json([
'error' => 'User not found'
], 404);
}
if ($user->role === 'admin') {
return response()->json([
'error' => 'You cant edit admin'
], 403);
}
return $user;
}
}
修改后的UsersController.php/destroy
public function destroy($id) {
$user = $this->userService->getUserById($id);
$user->delete(); // not working because sometimes it can return json response
return response()->json([], 204);
}
ResponderService.php
<?php
namespace App\Services;
class ResponderService
{
private function base($data, $status_code)
{
$data['status_code'] = $status_code;
return response()->json($data, $status_code);
}
public function error($message, $status_code)
{
$data['error'] = $message;
$data['status'] = 'error';
$this->base($data, $status_code);
}
}
我也读过有关存储库的内容,但我认为这种模式在我的项目中并不适用。如果您有其他可以改进 Controller 代码的建议,我愿意接受。
最佳答案
我没有发现在您的场景中使用异常有任何问题。
<?php
namespace App\Services;
use App\User;
class UserService
{
public function getUserById($id)
{
$user = User::find($id);
if (!$user) {
throw UserNotFoundException('User not found');
}
if ($user->role === 'admin') {
throw EditAdminException("You can't edit admin.");
}
return $user;
}
}
如果需要,这些异常是您在 app\Exception
中定义的自定义异常。然后 getUserById()
方法只能返回一个 User
否则会出现异常并向客户端返回 JSON 响应。
Laravel 也已经有了一个简单的方法来处理第一个异常。你可以这样做:
<?php
namespace App\Services;
use App\User;
class UserService
{
public function getUserById($id)
{
$user = User::findOrFail($id);
if ($user->role === 'admin') {
throw EditAdminException("You can't edit admin.");
}
return $user;
}
}
如果找不到 User
,Laravel 将处理抛出 Illuminate\Database\Eloquent\ModelNotFoundException
。
这样您就不必担心为异常已经为您做的事情创建一个ResponderService
。
如果您想对资源的响应进行标准化,您可以利用 Eloquent Resources 作为 API 的转换层:https://laravel.com/docs/5.7/eloquent-resources
最后,如果您发现要从多个位置删除资源并且不想重复响应,则可以将响应放在事件中:https://laravel.com/docs/5.7/eloquent#events
文档显示了处理事件的复杂方式,但我个人只会在您的模型开始感到臃肿时才这样做。
您可以将此作为创建 Event 和 Observer 类的更简单的替代方法:
public static function boot()
{
parent::boot();
static::deleted(function ($model) {
return response()->json([], 204);
});
}
该方法只适用于您的用户模型。顺便说一句,我发现的方法是查看 Eloquent\Model
上的 HasEvents
trait。
现在,综上所述,我实际上会将所有删除逻辑放入您的 UserService
并将该方法从 getUserById
重命名为 deleteById
。替代方案有点奇怪,因为您是说如果用户是管理员,您不希望能够通过 ID 获取用户。
实际上,您要做的是封装删除用户的逻辑,因此只需将其全部移动到服务的方法中,或者更好的是只使用模型上的 delete
事件并将所有逻辑放在那里。这样您甚至不必引入服务。
编辑
根据您在下方的评论,我认为您可能误解了如何在 Laravel 中使用异常。
在一个新的 Laravel 项目中,app\Exeptions\Handler
中有一个类可以捕获应用中所有未处理的异常。该类首先检查异常是否为 ModelNotFoundException
,然后返回 json 响应。
否则,它将捕获的异常传递给它父级的render
方法。
所以基本上当你想创建一个自定义异常时,你只需创建一个扩展 Exception
的类并实现一个 handle
方法。
这是一个示例异常类:
<?php
namespace App\Exceptions;
use Exception;
class TicketNotPayableException extends Exception
{
/**
* Render an exception into an HTTP response.
*
* @param \Illuminate\Http\Request $request
* @param \Exception $exception
* @return \Illuminate\Http\Response
*/
public function render()
{
return response()->json([
'errors' => [
[
'title' => 'Ticket Not Payable Exception',
'description' =>
'This ticket has already been paid.'
],
],
'status' => '409'
], 409);
}
}
现在响应完全可以重用,我的代码中不需要一堆 try-catch block 。 Laravel 的异常处理程序将捕获它,并调用 render
方法。
因此,如果我想在服务中封装支付票证的逻辑,我只需要throw App\Exceptions\TicketNotPayableException;
然后我的 Controller 只需要做类似的事情: $ticketPaymentService->pay($ticket);
并且不需要 try-catch。如果异常被抛出,它会冒泡,被处理程序捕获,并且 render
方法将被调用,这将返回适当的 JSON 响应 - 不需要 Responder
.
关于php - 如何在 Lumen 中使用 DRY 和服务层?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53017757/
我是一名优秀的程序员,十分优秀!