#laravel #api #rest #model-view-controller
#laravel #API #остальное #модель-представление-контроллер
Вопрос:
Когда я пишу API с помощью Laravel, я часто использую тот же метод, что и GitHub API v3. Я добавляю URL-адреса для облегчения навигации во время разработки, а также для пользователей, которые будут разрабатывать с использованием этого API.
В этом примере я вручную добавляю URL-адреса в каждое поле, а затем добавляю a count
для пейджеров во внешнем интерфейсе. Иногда у меня возникают более сложные вещи, если я хочу добавить необходимое для фильтрации результатов (если используется с Vuetify или Kendo Grid).
class UserController extends Controller
{
function index() {
$users = User::all()->each(function ($item, $key) {
$item['activities'] = url("/api/users/{$item['id']}/activities");
$item['classrooms'] = url("/api/users/{$item['id']}/classrooms");
});
return [
'count' => count($users),
'courses' => $users,
];
}
}
Есть ли способ сделать этот код менее шаблонным? Есть ли пакет, который делает все из коробки?
Ответ №1:
Я fractal
большой поклонник fractal
пакета spaties. Это позволяет преобразовывать объекты в ответы.
Во фрактальных сериализаторах и трансформаторах есть две концепции. Сериализаторы — это весь запрос, метаданные и т. Д. Трансформатор — это способ преобразования каждой модели или объекта. Обычно вы не стали бы создавать пользовательские сериализаторы, но в вашем случае это может решить большую часть ваших проблем.
use LeagueFractalSerializerArraySerializer;
class YourSerializer extends ArraySerializer {
public function collection($resourceKey, array $data)
{
return [
$resourceKey => $data,
'count' => count($data),
];
}
}
Создайте свой трансформатор для вашего пользователя. Другая важная вещь, которую вы получаете за это, — это то, что у вас есть одна игра, которая отвечает за преобразование. Вместо того, чтобы у каждого контроллера должна быть своя логика, которая будет распространена, и вы должны не забыть ее включить.
use LeagueFractalTransformerAbstract;
class AccountBalanceTransformer extends TransformerAbstract
{
public function transform(User $user)
{
return [
'id' => $user->id,
'name' => $user->id,
// other user fields
'activities' => url("/api/users/{$user->id}/activities"),
'classrooms' => url("/api/users/{$user->id}/classrooms"),
];
}
}
Вы должны назначить serializer
в своей fractal.php
конфигурации.
'default_serializer' => YourSerializer::class,
Теперь вы должны иметь возможность преобразовывать свои ответы следующим образом.
return fractal($users, new UserTransformer())
->withResourceName('courses')
->respond(Response::HTTP_OK);
Чтобы упростить его использование и избежать повторения, я обычно использую метод на родительском контроллере, подобный этому. При настройке трансформатора на объекте.
public function respond($data, $resourceKey, $status = Response::HTTP_OK) {
return fractal($data, $this->transformer)
->withResourceName($resourceKey)
->respond($status);
}
Это приведет к ответу, аналогичному тому, который был указан в вопросе.
Комментарии:
1. Вероятно, есть также решение с использованием eloquent resources, я был большим поклонником fractal до появления eloquent resources и все еще думаю, что он немного лучше подходит для моих нужд.
2. Выглядит потрясающе! Как где вы сохраняете свои трансформаторы (в
AppTransformer
)? Есть ли способ избежатьuse AppTransformerMyTransformer
в каждом контроллере?. Я думаюreturn factal($users, new MyTransformer)
, это некрасиво. Может быть, есть способ написать это:return $users->transform()
?3. Я сохраняю их в App Transformers. Как вы их преобразуете, зависит от вас, я вставляю каждый трансформатор в конструктор контроллера, который работает лучше всего для меня. Поскольку это имеет смысл, поскольку ответственность контроллеров заключается в делегировании преобразования ответов.
4. … так что да, сейчас я использую Фрактал.