# laravel response and API resouce https://hackmd.io/@javck/ByJgF8HRP/%2F0IRl5OAZRyiOJas4bfcvfA ## rsponse 附加錨點 ![](https://i.imgur.com/foDlxRt.png) https://www.youtube.com/watch?v=CeL-W_Ap1Ls ## 序列化 當模型或集合被轉型成字串時,會轉換成 JSON 格式,所以你可以直接從應用程式的路由或者控制器回傳 Eloquent 物件: ``` Route::get('users', function () { return App\User::all(); }); ``` 之前別人用present 用blade 轉string就是用這個 將模型轉換成一個陣列 如果要將模型還有它載入的關聯轉換成一個陣列,你可以使用 toArray 方法。這個方法是遞迴的,因此,所有屬性和所有關聯(包含關聯中的關聯)都會被轉換成陣列: ``` $user = App\User::with('roles')->first(); return $user->toArray() ; ``` 所以resource api的 map原理 當模型或集合被轉型成字串時,會轉換成 JSON 格式,所以你可以直接從應用程式的路由或者控制器回傳 Eloquent 物件: ``` Route::get('users', function () { return App\User::all(); }); ``` ## 注意 地雷 當你resource出來 裡面資料是看不到的 因為它是會轉成json 所以你dd看不出來 但json_decode就能變出來 不然就在blade用@json ## 更優雅的細節 author可以合併 原本 ![](https://i.imgur.com/GXiOu7Z.png) 簡化 ![](https://i.imgur.com/GvU6HQh.png) 作法 寫fumction ![](https://i.imgur.com/aXhPY4M.png) ![](https://i.imgur.com/QfDrlpt.png) **更棒的作法** 使用collection ![](https://i.imgur.com/Lcl05CX.png) ## cache https://www.youtube.com/watch?v=vrLcCxWlxOk 不用一直重複撈取 ![](https://i.imgur.com/WH2HfFd.png) ## 限制次數 ![](https://i.imgur.com/ZvejYRY.png) ## 同一個api回應 不同結果 https://www.youtube.com/watch?v=MxQJlFUYO30 第一使用when ![](https://i.imgur.com/efjjKU0.png) 因為request會在toArray裡面 本來就有了 多字串的話 ![](https://i.imgur.com/XvnAqhe.png) 也可以用whenAppend ![](https://i.imgur.com/NQCHj2m.png) 但在controller要寫 ![](https://i.imgur.com/dVA7yt9.png) ## 送api guzzle 7板之後可以用facade的http取代 https://laracasts.com/series/whats-new-in-laravel-7/episodes/5 $url = http:sdwdqwds; `Http::get($url)->josn();` 等於 ``` json_decode($this->guzzle()->get($url)->getBody()); ``` 轉josn更快 也不用body了 https://laracasts.com/series/whats-new-in-laravel-7/episodes/5 如果是html 就跟postman一樣head要加東西 ![](https://i.imgur.com/YHgbcSj.png) ## API 資源:有或沒有“數據”? 如果您使用 Eloquent API Resources 返回數據,它們將自動包裝在 'data' 中。如果你想刪除它,添加`JsonResource::withoutWrapping()`;在`app/Providers/AppServiceProvider.php`。 ``` class AppServiceProvider extends ServiceProvider { public function boot() { JsonResource::withoutWrapping(); } } ``` ## API 返回“一切順利” 如果您有執行某些操作但沒有響應的 API 端點,因此您只想返回“一切順利”,您可能會返回 204 狀態代碼“無內容”。在Laravel,很容易:`return response()->noContent();`。 ``` public function reorder(Request $request) { foreach ($request->input('rows', []) as $row) { Country::find($row['id'])->update(['position' => $row['position']]); } return response()->noContent(); } ``` ## posman不想出現html錯誤 想用json格式的 這種 ![](https://i.imgur.com/EmW2CUa.png) 不要這種 ![](https://i.imgur.com/BDWJZCy.png) 在header加上兩個 ![](https://i.imgur.com/4YrkhG1.png) 這樣他就知道你要你要json ## resouce用的 this $this是參照到目前的object ( 已經被宣告的實體上 ) 所以這才能用this ## 限定返回資料 php函数名后冒号+数据类型(返回值类型限制/php新特性)(只有在php 7以上才能用) https://blog.csdn.net/dianpujiu5323/article/details/101285711 ![](https://i.imgur.com/Rkh855R.png) ## 返回會json https://docs.laravel-dojo.com/laravel/5.5/responses 字串與陣列 所有路由與控制器應該回傳一個回應到使用者的瀏覽器。Laravel 提供幾個不同的方式來回傳回應。最基本的回應是只從路由和控制器中回傳一組字串。Laravel 會自動將字串轉換成完整的 HTTP 回應: ``` Route::get('/', function () { return 'Hello World'; }); 除了從路由和控制器中回傳字串,你也可以回傳陣列。Laravel 會自動將陣列轉換成 JSON 回應: Route::get('/', function () { return [1, 2, 3]; }); ``` `只有在 return情況下` Eloquent collections arrays string 集合 collection https://laravel.com/docs/7.x/responses#creating-responses **原理是這些裡面原本就有toarray可以轉換 要追回去 可看影片 248 但不可能只返回這些 所以要 ``` return response()->json([ 'status' => 200, 'data' => [ 'meta' => SongModel::find($songID) ] ]); ``` 但這也太麻煩 有**兩大缺點** **重複的格式定義**: 類似的 endpoint 可能都有類似的格式定義,可是卻散落在 Controller 各處。 **format 不固定**: 因為是直接將 model 輸出,如果資料表有更動的話,API 會直接受到衝擊。 ## JSON 回應 json() 會自動將 header 的 Content-Type 設定為 application/json,同時使用 PHP 的 json_encode() 將傳入的陣列轉換為 JSON ``` return response()->json([ 'name' => 'Abigail', 'state' => 'CA', ]); ``` 假如你想要生成一個 JSONP 回應,你可以在 json() 後面再接一個 withCallback() ``` return response() ->json(['name' => 'Abigail', 'state' => 'CA']) ->withCallback($request->input('callback')); ``` ## 型別轉換 跟 Eloquent: 序列化 兩個差別 雖然都是轉換 但對象不同 model相關 一個是單個的 ![](https://i.imgur.com/Z0YK3EU.png) 這叫單個 https://www.jollen.org/php/jollen_php_book_6_php.html 1. (int),(integer) - 轉換成 integer 型別 2. (bool),(boolean) - 轉換成 boolean 型別 3. (float),(double),(real) - 轉換成 float 型別 4. (string) - 轉換成 string 型別 5. (array) - 轉換成 array 型別 6. (object) - 轉換成 object 型別 https://laravel.tw/docs/5.2/eloquent-serialization 如果要將模型還有它載入的關聯轉換成一個陣列,你可以使用 toArray 方法。這個方法是遞迴的,因此,所有屬性和所有關聯(包含關聯中的關聯)都會被轉換成陣列: `$user = App\User::with('roles')->first();` return $user->toArray(); 你也可以將集合轉換成陣列: ``` $users = App\User::all(); return $users->toArray(); ``` 將模型轉換成 JSON 如果要將模型轉換成 JSON,你可以使用 toJson 方法。如同 toArray,toJson 方法也是遞迴的,所以,所有的屬性以及關聯都將被轉換成 JSON: ``` $user = App\User::find(1); return $user->toJson(); ``` 或者,你可以強制把一個模型或集合轉型成一個字串,它將會自動的呼叫 toJson 方法: ``` $user = App\User::find(1); return (string) $user; ``` 當模型或集合被轉型成字串時,會轉換成 JSON 格式,所以你可以直接從應用程式的路由或者控制器回傳 Eloquent 物件: ``` Route::get('users', function () { return App\User::all(); }); ``` **這邊踩個小雷** ![](https://i.imgur.com/OfnDEMO.png) 雖然是用find找 但關聯會變collection 所以resource要記得用collection的方法 -------------------------------- ----------------------------- 由於 Resource 是繼承自 JsonResource,因此輸出就會是 JSON 的格式內容,而我們要做的就是修改 toArray() method 當中的格式: 用new 的原因是要實體化 之前::都是調用class的靜態方法 靜態方法可以不實體化就調用 但他會直接站記憶體 簡單來說就是 一般物件成員 變 類別成員 當我new一個resource的時候 他原生涵式return是這樣 `return parent::toArray($request);` 所以當你傳入ex $post->comment;之類的 他會幫你區分 是不是data 還是...等 影片說是會有連結跟集合區分 還沒測試 -------- 注意的地方 如果日期之類的被轉成物件 請用(string)轉回來![](https://i.imgur.com/iXNTOTa.png) --- 這邊有個要注意點的點 這邊名子也是自己打 所以錯了顯示也是錯 我覺得可以用tinker抓出來直接複製屬性名 比較保險 --- ## collection 如果你今天不是傳一個是用all() 或get() 用 return Commnet::collection($post->all()); 去掉用resoucr原本的靜態方法把collection列出來 **Resource::collection() 使用,它會將 Collection 中的所有 Model 物件自動套用 Resource 的格式** https://blog.johnsonlu.org/laravel-eloquent-api-resources/ 如果想調用關聯的屬性 可在 return直接用 ``` 'id'=>$this->id 'comment'=>$this->id ``` **集合分頁** ![](https://i.imgur.com/CSJlFxT.png) ## 查詢特定的 https://ithelp.ithome.com.tw/articles/10220567 Model寫一個方法,名稱命名為 get某某某Attribute 例如 getAgeAttribute。必須用駝峰式的命名方式,如果我要取得Animal這個物件並想要的到age的值,會訪問這個方法,並依造你的程式回傳對應的值 ``` public function getAgeAttribute() { $diff = Carbon::now()->diff($this->birthday); return "{$diff->y}歲{$diff->m}月"; } ``` ## 條件載入 重要 防止lazy loading 有條件的關聯 除了載入有條件的屬性外,你可能會根據關聯是否已經載入到模型上,而有條件的在你的 資源回應中引入關聯。這可以讓你的控制器決定應該載入哪一個關聯到模型上,並讓你的資源能輕易的在它們確實要被載入時引入它們。 最終,這麼做可以較容易避免在你的資源中發生「N+1」的查詢問題。whenLoaded 方法可以被用於有條件的載入關聯。為了避免不必要的載入關聯,這個方法可以接受關聯名稱,而非關聯本身。 * 將資源轉換成陣列。 ``` public function toArray($request) { return [ 'id' => $this->id, 'name' => $this->name, 'email' => $this->email, 'posts' => Post::collection($this->whenLoaded('posts')), 'created_at' => $this->created_at, 'updated_at' => $this->updated_at, ]; } ``` 在本範例中,如果關聯沒有被載入,在它被傳送到客戶端前,posts 鍵會從資源回應中被完全刪除。 ## 終極寫法 ![](https://i.imgur.com/BZuF6TM.png) 用collection包 resource 不然你原本還要跑foreach 但如果變數命名 $collects對上reourse他就可以直接幫你跑 很有感覺但沒有很懂 resorce collection底層 AbstractPaginator這個是分業的底層抽象 ``` <?php namespace App\Http\Resources; use Illuminate\Http\Resources\Json\ResourceCollection; use Illuminate\Pagination\AbstractPaginator; class BaseResourceCollection extends ResourceCollection { /** * Transform the resource collection into an array. * * @param \Illuminate\Http\Request $request * * @return array */ public function toArray($request) { return $this->resource instanceof AbstractPaginator ? [ 'data' => $this->collection, 'current_page' => $this->currentPage(), 'last_page' => $this->lastPage(), 'per_page' => $this->perPage(), 'total' => $this->total(), ] : parent::toArray($request); } } ``` 很屌 因為原本就有 parent::toArray($request) 所以你父只要直接設定變數就好 ## api delete ![](https://i.imgur.com/Gw8toRO.png) noComment是204 剛好就是刪除 ###### tags: `Laravel`