Xây dựng APIs bằng Laravel với Eloquent API Resources
Bài đăng này đã không được cập nhật trong 3 năm
Một chức năng mới được bổ sung trong Laravel 5.5 là API Resources
, bạn có thể đọc tại liệu chính thống tại đây
Trong khuôn khổ bài viết này mình sẽ tìm hiểu xem thằng này có rì ngon
Giới thiệu trên docs chính chủ:
When building an API, you may need a transformation layer that sits between your Eloquent models and the JSON responses that are actually returned to your application's users. Laravel's resource classes allow you to expressively and easily transform your models and model collections into JSON.
Đại khái là khi build API chúng ta sẽ cần chuyển đổi data từ Eloquent models sang JSON, API Resources sẽ giúp chúng ta dễ dàng hơn trong việc này.
Tạo API Resources
Để sử dụng resource trước tiên chúng ta cần tạo 1 model và resource cho table của chúng ta bằng command (ở đây mình ví dụ với model User
):
php artisan make:resource User
Command này sẽ tạo một class User ở app/Http/Resources
với nội dung như sau:
<?php
namespace App\Http\Resources;
use Illuminate\Http\Resources\Json\Resource;
class User extends Resource
{
/**
* Transform the resource into an array.
*
* @param \Illuminate\Http\Request
* @return array
*/
public function toArray($request)
{
return parent::toArray($request);
}
}
Có một tùy chọn khác cho command này là:
php artisan make:resource User --collection
Command này cũng tạo class User như trên nhưng extends từ class Illuminate\Http\Resources\Json\ResourceCollection
thay vì Illuminate\Http\Resources\Json\Resource
Một cách khác để tạo resource collection:
php artisan make:resource UserCollection
Hiểu đơn giản, resource bình thường thì làm việc với dữ liệu đơn còn resource collection thì làm việc vói dữ liệu là collection.
Sử dụng thế nào
Mặc định trong resource sẽ có sẵn phương thức toArray
có nội dung như sau:
public function toArray($request)
{
return parent::toArray($request);
}
Bạn có thể custom lại phương thức này để phù hợp với mục đích của mình, nếu để nguyên thì trả về dữ liệu mặc định thôi, ví dụ:
public function toArray($request)
{
return [
'id' => $this->id,
'name' => $this->name,
'email' => $this->email,
'created_at' => (string)$this->created_at,
'updated_at' => $this->updated_at,
'status' => 'success'
];
}
Chúng ta sử dụng như sau:
use App\User;
use App\Http\Resources\User as UserResource;
Route::get('/user', function () {
return new UserResource(User::find(1));
});
Kết quả trả về như ý muốn của chúng ta:
Hoặc đối với resource collection:
public function toArray($request)
{
return [
'data' => $this->collection,
'response' => [
'status' => 'success',
'code' => 200
],
];
}
Có 2 cách để sử dụng resource collection: Hoặc (*):
use App\User;
use App\Http\Resources\User as UserResource;
Route::get('/user', function () {
return UserResource::collection(User::all());
});
hoặc (**)
use App\User;
use App\Http\Resources\UserCollection as UserResource;
Route::get('/user', function () {
return new UserResource(User::all());
});
Bạn xem hai class khác nhau đấy nhé, coi chừng nhầm lẫn, nhắc lại nè, class App\Http\Resources\User
extends từ Illuminate\Http\Resources\Json\Resource
còn UserCollection
thì extends từ ResourceCollection
Kết quả của * sẽ như sau:
Còn kết quả của ** sẽ là:
Bạn xem lại cách viết phương thức toAray
ở hai resource và so sanh nhé với kết quả nhé.
Sử dụng với relationship
Mình muốn include danh sách posts
của User
thì mình thêm vào thôi:
use App\Http\Resources\Post;
...
public function toArray($request)
{
return [
'id' => $this->id,
'name' => $this->name,
'email' => $this->email,
'posts' => Post::collection($this->posts),
'created_at' => $this->created_at,
'updated_at' => $this->updated_at,
];
}
nếu muốn chỉ thêm trường posts
khi mà quan hệ đã được load thì dùng method whenLoaded
'posts' => Post::collection($this->whenLoaded('posts')),
Ngoài ra còn có một số phương thức hữu dụng khác như when
, mergeWhen
, whenPivotLoaded
// secret chỉ xuất hiện khi khi `$this->isAdmin()` trả về `true` và trả về 'secret-value'
// đối số thứ 2 có thể là 1 `Closure `
'secret' => $this->when($this->isAdmin(), 'secret-value'),
// cũng gần giống như trên
$this->mergeWhen($this->isAdmin(), [
'first-secret' => 'value',
'second-secret' => 'value',
]),
// chỉ khi pivot `expires_at` đã được load thì trường này mới xuất hiện
'expires_at' => $this->whenPivotLoaded('role_users', function () {
return $this->pivot->expires_at;
}),
Data Wrapping
Mặc định dữ liệu trả về từ resource sẽ được bao bọc bởi key data
, nếu không muốn bạn có thể remove nó đi bằng cách sử dụng phương thức withoutWrapping
của resource
<?php
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
use Illuminate\Http\Resources\Json\Resource;
class AppServiceProvider extends ServiceProvider
{
/**
* Perform post-registration booting of services.
*
* @return void
*/
public function boot()
{
Resource::withoutWrapping();
}
...
}
Phương thức này chỉ remove key data
mặc định đi, nếu bạn tự bao bọc dữ liệu của mình thì phương thức này sẽ không dám bỏ key của bạn đi đâu
Lúc đầu mình đọc tài liệu về phần này thì mình không hiểu cái này để làm cái beep gì nhỉ vì đâu cần nó vẫn trả về JSON từ eloquent được mà, nhưng mà tìm hiểu ra thì mới thấy nó tuyệt vời lắm đấy mọi người.
All rights reserved