本文經Viraj Khatavkar同行評審。感謝所有SitePoint的同行評審員,使SitePoint的內容達到最佳狀態!
如果您之前構建過API,我敢打賭您習慣於直接將數據作為響應輸出。如果操作正確,這可能不會造成危害,但有一些實際的替代方案可以幫助解決這個問題。
其中一個可用的解決方案是Fractal。它允許我們在將模型作為響應返回之前,為模型創建一個新的轉換層。它非常靈活,易於集成到任何應用程序或框架中。
我們將使用Laravel 5.3應用程序來構建示例並將Fractal包與其集成,因此請繼續使用安裝程序或通過Composer創建一個新的Laravel應用程序。
<code>laravel new demo</code>
或
<code>composer create-project laravel/laravel demo</code>
然後,在文件夾內,我們需要Fractal包。
<code>composer require league/fractal</code>
我們的數據庫包含users和roles表。每個用戶都有一個角色,每個角色都有一個權限列表。
// app/User.php class User extends Authenticatable { protected $fillable = [ 'name', 'email', 'password', 'role_id', ]; protected $hidden = [ 'password', 'remember_token', ]; /** * @return \Illuminate\Database\Eloquent\Relations\BelongsTo */ public function role() { return $this->belongsTo(Role::class); } }
// app/Role.php class Role extends Model { protected $fillable = [ 'name', 'slug', 'permissions' ]; /** * @return \Illuminate\Database\Eloquent\Relations\HasMany */ public function users() { return $this->hasMany(User::class); } }
我們將為每個模型創建一個Transformer。我們的UserTransformer類如下所示:
// app/Transformers/UserTransformer.php namespace App\Transformers; use App\User; use League\Fractal\TransformerAbstract; class UserTransformer extends TransformerAbstract { public function transform(User $user) { return [ 'name' => $user->name, 'email' => $user->email ]; } }
是的,創建Transformer就這麼簡單!它只是以開發人員可以管理的方式轉換數據,而不是留給ORM或存儲庫。
我們擴展TransformerAbstract類並定義transform方法,該方法將使用User實例調用。 RoleTransformer類也是如此。
namespace App\Transformers; use App\Role; use League\Fractal\TransformerAbstract; class RoleTransformer extends TransformerAbstract { public function transform(Role $role) { return [ 'name' => $role->name, 'slug' => $role->slug, 'permissions' => $role->permissions ]; } }
我們的控制器應該在將數據發送回用戶之前轉換數據。我們現在將處理UsersController類,暫時只定義index和show操作。
// app/Http/Controllers/UsersController.php class UsersController extends Controller { /** * @var Manager */ private $fractal; /** * @var UserTransformer */ private $userTransformer; function __construct(Manager $fractal, UserTransformer $userTransformer) { $this->fractal = $fractal; $this->userTransformer = $userTransformer; } public function index(Request $request) { $users = User::all(); // 从数据库获取用户 $users = new Collection($users, $this->userTransformer); // 创建资源集合转换器 $users = $this->fractal->createData($users); // 转换数据 return $users->toArray(); // 获取转换后的数据数组 } }
index操作將從數據庫查詢所有用戶,使用用戶列表和轉換器創建一個資源集合,然後執行實際的轉換過程。
{ "data": [ { "name": "Nyasia Keeling", "email": "crooks.maurice@example.net" }, { "name": "Laron Olson", "email": "helen55@example.com" }, { "name": "Prof. Fanny Dach III", "email": "edgardo13@example.net" }, { "name": "Athena Olson Sr.", "email": "halvorson.jules@example.com" } // ... ] }
當然,一次返回所有用戶是沒有意義的,我們應該為此實現分頁器。
Laravel傾向於簡化事情。我們可以像這樣實現分頁:
<code>laravel new demo</code>
但是為了使這與Fractal一起工作,我們可能需要添加一些代碼來轉換數據,然後再調用分頁器。
<code>composer create-project laravel/laravel demo</code>
第一步是從模型分頁數據。接下來,我們像以前一樣創建一個資源集合,然後在集合上設置分頁器。
Fractal為Laravel提供了一個分頁器適配器來轉換LengthAwarePaginator類,它還為Symfony和Zend提供了一個適配器。
<code>composer require league/fractal</code>
請注意,它為分頁詳細信息添加了額外的字段。您可以在文檔中閱讀更多關於分頁的信息。
現在我們已經熟悉了Fractal,是時候學習如何在用戶請求時包含子資源(關係)到響應中了。
我們可以請求包含額外資源到響應中,例如http://demo.vaprobash.dev/users?include=role。我們的轉換器可以自動檢測正在請求的內容並解析include參數。
// app/User.php class User extends Authenticatable { protected $fillable = [ 'name', 'email', 'password', 'role_id', ]; protected $hidden = [ 'password', 'remember_token', ]; /** * @return \Illuminate\Database\Eloquent\Relations\BelongsTo */ public function role() { return $this->belongsTo(Role::class); } }
$availableIncludes屬性告訴轉換器我們可能需要包含一些額外的數據到響應中。如果include查詢參數請求用戶角色,它將調用includeRole方法。
// app/Role.php class Role extends Model { protected $fillable = [ 'name', 'slug', 'permissions' ]; /** * @return \Illuminate\Database\Eloquent\Relations\HasMany */ public function users() { return $this->hasMany(User::class); } }
$this->fractal->parseIncludes行負責解析include查詢參數。如果我們請求用戶列表,我們應該看到類似這樣的內容:
// app/Transformers/UserTransformer.php namespace App\Transformers; use App\User; use League\Fractal\TransformerAbstract; class UserTransformer extends TransformerAbstract { public function transform(User $user) { return [ 'name' => $user->name, 'email' => $user->email ]; } }
如果每個用戶都有一個角色列表,我們可以將轉換器更改為如下所示:
namespace App\Transformers; use App\Role; use League\Fractal\TransformerAbstract; class RoleTransformer extends TransformerAbstract { public function transform(Role $role) { return [ 'name' => $role->name, 'slug' => $role->slug, 'permissions' => $role->permissions ]; } }
包含子資源時,我們可以使用點表示法來嵌套關係。假設每個角色都有一個存儲在單獨表中的權限列表,並且我們想列出具有其角色和權限的用戶。我們可以這樣做include=role.permissions。
有時,我們需要默認包含一些必要的關聯,例如地址關聯。我們可以通過在轉換器中使用$defaultIncludes屬性來實現。
// app/Http/Controllers/UsersController.php class UsersController extends Controller { /** * @var Manager */ private $fractal; /** * @var UserTransformer */ private $userTransformer; function __construct(Manager $fractal, UserTransformer $userTransformer) { $this->fractal = $fractal; $this->userTransformer = $userTransformer; } public function index(Request $request) { $users = User::all(); // 从数据库获取用户 $users = new Collection($users, $this->userTransformer); // 创建资源集合转换器 $users = $this->fractal->createData($users); // 转换数据 return $users->toArray(); // 获取转换后的数据数组 } }
我最喜歡Fractal包的一件事是能夠將參數傳遞給include參數。文檔中的一個很好的例子是按順序排列。我們可以將其應用到我們的示例中,如下所示:
{ "data": [ { "name": "Nyasia Keeling", "email": "crooks.maurice@example.net" }, { "name": "Laron Olson", "email": "helen55@example.com" }, { "name": "Prof. Fanny Dach III", "email": "edgardo13@example.net" }, { "name": "Athena Olson Sr.", "email": "halvorson.jules@example.com" } // ... ] }
這裡重要的部分是list($orderCol, $orderBy) = $paramBag->get('order') ?: ['created_at', 'desc'];,這將嘗試從用戶include獲取order參數,並將其應用於查詢構建器。
我們現在可以通過傳遞參數來按順序排列包含的用戶列表(/roles?include=users:order(name|asc))。您可以在文檔中閱讀更多關於包含資源的信息。
但是,如果用戶沒有任何關聯的角色會怎樣?它將停止並出現錯誤,因為它期望的是有效數據而不是null。讓我們從響應中刪除該關係,而不是顯示其null值。
<code>laravel new demo</code>
因為Eloquent在訪問模型時會延遲加載模型,所以我們可能會遇到n 1問題。這可以通過一次性急切加載關係來解決,以優化查詢。
<code>composer create-project laravel/laravel demo</code>
這樣,在訪問模型關係時,我們將不會有任何額外的查詢。
我在閱讀Phil Sturgeon撰寫的《構建你不會討厭的API》時偶然發現了Fractal,這是一本很棒且內容豐富的讀物,我強烈推薦。
您在構建API時是否使用過轉換器?您是否有任何首選的執行相同工作的包,或者您只是使用json_encode?請在下面的評論部分告訴我們!
PHP Fractal是一個強大的工具,有助於為API呈現和轉換數據。它很重要,因為它提供了一種標準化的方法來輸出複雜、嵌套的數據結構,確保API的數據輸出一致、結構良好且易於理解。這使得開發人員更容易使用您的API,並減少了出錯的可能性。
PHP Fractal的工作原理是獲取複雜的數據結構並將其轉換為更易於使用的格式。它通過兩個主要組件來實現:Transformer和Serializer。 Transformer負責將復雜的數據轉換為更簡單的格式,而Serializer則格式化最終輸出。
PHP Fractal中的Transformer是定義應用程序數據應如何在API響應中輸出的類。它們獲取複雜的數據結構並將它們轉換為更簡單、更易於使用的格式。這允許您精確控制API響應中包含哪些數據以及數據的結構。
PHP Fractal中的Serializer負責格式化API的最終輸出。它們獲取已由Transformer轉換的數據,並將其格式化為特定的結構。這允許您確保API的輸出一致且易於理解。
在項目中實現PHP Fractal包括通過Composer安裝Fractal庫,為數據創建Transformer,然後使用Fractal類使用Transformer轉換數據。然後,您可以使用Fractal的Serializer之一輸出轉換後的數據。
是的,PHP Fractal是一個獨立的庫,可以與任何PHP項目一起使用。它不依賴於任何特定的框架或平台,這使得它成為任何PHP開發人員的通用工具。
使用PHP Fractal提供了許多好處。它確保API的輸出一致且結構良好,使開發人員更容易使用。它還提供了一種標準化的方法來轉換複雜的數據結構,減少了出錯的可能性,並使代碼更容易維護。
PHP Fractal以其簡單性和靈活性而脫穎而出。它提供了一種直接的方法來轉換複雜的數據結構,並且它使用Transformer和Serializer允許高度定制。這使得它成為任何使用API的開發人員的強大工具。
是的,PHP Fractal是高度可定制的。您可以創建自定義Transformer來精確控制數據的轉換方式,並且您可以使用不同的Serializer以不同的方式格式化輸出。這允許您根據您的特定需求調整API的輸出。
有很多資源可以幫助您了解更多關於PHP Fractal的信息。官方PHP League網站提供了全面的文檔,並且網上有許多教程和博文。此外,PHP Fractal GitHub存儲庫是一個探索代碼並查看其使用方法示例的好地方。
以上是PHP Fractal-使您的API json變得漂亮,永遠!的詳細內容。更多資訊請關注PHP中文網其他相關文章!