---
# System prepended metadata

title: laravel response and  API resouce
tags: [Laravel]

---

# 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`