Как использовать СУХОЙ и сервисный слой в Lumen?

Я создаю API в Lumen Framework и недавно я прочитал о DRY и слое обслуживания. До сих пор я не использовал ничего из этого в своем коде, и вся логика была в контроллерах. Поэтому я хотел бы начать использовать его, но у меня есть некоторые проблемы с ним.

Это часть моего контроллера (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);
}
}

Посмотрев на этот код, я попытался изменить 2 вещи.

  1. Получить пользователя и вернуть ошибки можно в UserService.php (У меня есть этот код в других методах, поэтому я думаю, что это хорошая идея для этого метода в обслуживании). Но, как вы видите, я хочу возвращать ответ, когда есть ошибки, и когда я это делаю, мой код, как и предполагалось, пытался использовать метод удаления в ответе json, а не в пользовательской модели. Бросать исключения на мой взгляд нехорошо, потому что не совместим с принципом СУХОЙ. Есть идеи как это исправить?

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);
}
  1. Я использую так много ответов json в контроллерах, промежуточном программном обеспечении и т. Д., И я хочу объединить это, создав новый класс, но я не знаю, как правильно его использовать. Я имею в виду возвращение ответа JSON в ResponderService.php вероятно, не остановит выполнение в других местах, таких как контроллер. Или, может быть, я должен создать это как помощник?

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);
}
}

Я тоже читал о репозиториях, но я не думаю, что этот шаблон будет хорошим в моем проекте. Если у вас есть другие предложения, которые могут быть улучшены в коде контроллера, я открыт для них.

2

Решение

Я не вижу проблем с использованием исключений для вашего сценария.

<?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;
}
}

И Ларавел будет разбрасывать Illuminate\Database\Eloquent\ModelNotFoundException если User не может быть найден

Таким образом, вам не нужно беспокоиться о создании 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);
});
}

Этот метод подходит только для вашей модели User. И, как я обнаружил, кстати, заглянув внутрь HasEvents черта на Eloquent\Model,

Теперь, все это говорит, я бы на самом деле поместил всю логику удаления в вашем UserService и переименуйте метод из getUserById в deleteById, Альтернатива немного странная, потому что вы говорите, что не хотите получать пользователя по идентификатору, если это администратор.

На самом деле вы пытаетесь инкапсулировать логику удаления пользователя, поэтому просто перенесите все это в метод службы или, еще лучше, просто используйте 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 в моем коде. Обработчик исключений Laravel поймает его и вызовет render метод.

Так что, если я хочу инкапсулировать логику оплаты билета внутри сервиса, мне просто нужно throw App\Exceptions\TicketNotPayableException; и тогда мой контроллер должен только сделать что-то вроде: $ticketPaymentService->pay($ticket); и нет необходимости в попытке поймать. Если возникнет исключение, оно будет пузыриться, попадать в обработчик, и render будет вызван метод, который вернет соответствующий ответ JSON — нет необходимости Responder,

1

Другие решения

Других решений пока нет …

По вопросам рекламы [email protected]