【问题标题】:How to properly decode response paginated resource from an API in laravel 8?如何从 laravel 8 中的 API 正确解码响应分页资源?
【发布时间】:2022-01-23 07:41:48
【问题描述】:

我正在尝试开发一个 laravel 应用程序,该应用程序必须有一个内部 API 来始终获取数据和前端控制器来使用这个 API 来呈现视图。该 API 将被移动应用程序使用,因此所有请求都将由该 API 处理。

这是我的 API 索引操作,运行良好:

public function index(Request $request)
    {
        $filters = $request->all();

        $query = Place::query()->with('user');

        if(!isset($filters['filterType']) || !in_array(Str::lower($filters['filterType']), ['and', 'or']) ){
            $filters['filterType'] = 'or';
        }

        //apply filters
        foreach($filters as $filter => $value){
            if(Place::hasProperty($filter, app(Place::class)->getTable())){
                if($filters['filterType'] == 'and'){
                    $query->where($filter, $value); 
                }
                else{
                    $query->orWhere($filter, $value);                    
                }
            }
        }

        //sorting
        if(!isset($filters['sortOrder']) || !in_array($filters['sortOrder'], ['asc', 'desc'])){
            $sortOrder = 'desc';
        }
        else{
            $sortOrder = $filters['sortOrder'];
        }

        if(isset($filters['sortBy'])){
            $sortBy = $filters['sortBy'];
            foreach(explode(',', $sortBy) as $sortField){
                if(Place::hasProperty($sortField, app(Place::class)->getTable())){
                    $query->orderBy($sortField, $sortOrder);
                }
            }
        }
        
        //default pagination
        if(!isset($filters['maxResults'])){
            $filters['maxResults'] = 5;
        }
        if(!isset($filters['page'])){
            $filters['page'] = 1;
        }

        //apply pagination
        $results = $query->paginate($filters['maxResults'], ['*'], 'page', $filters['page']);
        $resultsCollectionResource = PlaceResource::collection($results);

        return $resultsCollectionResource;
    }

如果我通过邮递员提出这个要求 http://api.site.test/places?fields=id,name,user_id

{
    "maxResults": 2
}

我得到了预期的结果,具有 metalinks 属性:

{
    "data": [
        {
            "id": 1,
            "name": "Lubowitz Group (Customer-focused real-time complexity)",
            "user_id": 3
        },
        {
            "id": 2,
            "name": "Heaney, Dietrich and Spencer (Fully-configurable multi-state processimprovement)",
            "user_id": 10
        }
    ],
    "links": {
        "first": "http://api.ourplaces.test/places?page=1",
        "last": "http://api.ourplaces.test/places?page=5",
        "prev": null,
        "next": "http://api.ourplaces.test/places?page=2"
    },
    "meta": {
        "current_page": 1,
        "from": 1,
        "last_page": 5,
        "links": [
            {
                "url": null,
                "label": "« Previous",
                "active": false
            },
            {
                "url": "http://api.ourplaces.test/places?page=1",
                "label": "1",
                "active": true
            },
            {
                "url": "http://api.ourplaces.test/places?page=2",
                "label": "2",
                "active": false
            },
            {
                "url": "http://api.ourplaces.test/places?page=3",
                "label": "3",
                "active": false
            },
            {
                "url": "http://api.ourplaces.test/places?page=4",
                "label": "4",
                "active": false
            },
            {
                "url": "http://api.ourplaces.test/places?page=5",
                "label": "5",
                "active": false
            },
            {
                "url": "http://api.ourplaces.test/places?page=2",
                "label": "Next »",
                "active": false
            }
        ],
        "path": "http://api.ourplaces.test/places",
        "per_page": 2,
        "to": 2,
        "total": 10
    }
}

然后我有前端控制器,这个动作:

    public function index()
    {
        $request = Request::create(env('API_URL').'/places', 'GET');

        $response = Route::dispatch($request);
        $responseContent =  $response->content();
        $places = json_decode($responseContent);
        return view('places.index', ['places' => $places]);
    }

还有这个观点:

<x-app-layout>
    <x-slot name="header">
        <h2 class="font-semibold text-xl text-gray-800 leading-tight">
            {{ __('Places') }}
        </h2>
    </x-slot>

    <div class="py-12">
        <div class="max-w-7xl mx-auto sm:px-6 lg:px-8">
            <div class="bg-white overflow-hidden shadow-xl sm:rounded-lg">
                @foreach($places->data as $place)
                    <div class="row">
                        <div class="col-md-12 text-center">
                            <h1 class="post-title">{{ $place->name }}</h1>
                            <p>{{ $place->streetAddress }}!</p>
                            <p><a href="{{ route('web.places.show', [ 'place' => $place->id ]) }}">Ver Sitio...</a></p>
                        </div>
                    </div>
                    <hr>
                @endforeach

                <div class="row">
                    <div class="col-md-12 text-center">
                        {{ $places->links() }}
                    </div>
                </div>

            </div>
        </div>
    </div>
</x-app-layout>

但我得到了错误:

错误 调用未定义的方法 stdClass::links() (查看:C:\xampp\htdocs\ourplaces\resources\views\places\index.blade.php)

我认为问题出在前端控制器上,我认为我对 API 的请求很糟糕,因为在进行 json_decode 之后,我得到了 stdClass 对象,而不是 API 控制器生成的原始对象。

API 对象:

前端的解码对象:

我做错了什么? 谢谢大家。

编辑

根据@matiaslauriti 的响应,我将 API 调用更改为 guzzle:

public function index(Request $request)
    {
        //$places =  redirect()->route('api.places.index', ['request' => $request ])->content();

        $response = Http::get(env('API_URL').'/places');
        $places = $response->object();

        return view('places.index', ['places' => $places]);
    }

但我仍然遇到完全相同的问题。我测试了 $response->object() 以外的其他方法,比如收集。但是我从来没有得到一个可以在视图中使用 $places->links() 方法的对象。

【问题讨论】:

  • $places 不是collection 或类似的东西,它是一个数组,因为你已经完成了json_decode 并返回一个array...你也永远不会使用HTTP 调用另一个控制器,您可以重定向但永远不会创建一个新的Request 对象到所需的 URL...您可以使用 Guzzle 或类似的东西,或者您在可以使用该逻辑的 Service 类中拥有它,但您从不调用控制器在控制器内...
  • 是的,这就是问题所在。我需要 $places 是一个集合,但我不知道该怎么做。我正在尝试获取重定向响应,但我无法做到这一点。我将使用 Guzzle 进行测试,在这种情况下是一个内部 API,但它必须是外部的。我需要从控制器执行 API 请求获取分页数据。
  • 我无法从控制器使用 API,所以我改变了我的方法。也许这是一种糟糕的做法,但这对我有用。我将Controllers请求的数据抽象到Model: api controller: ```` public function index(Request $request) { return Place::getPlaces($request); } ``` 前端控制器: ``` public function index(Request $request) { $places = Place::getPlaces($request);返回视图('places.index', ['places' => $places]); } ``` 并且模型有所有曾经在我的 api 控制器中的查询

标签: php laravel api laravel-8 laravel-api


【解决方案1】:

$places-&gt;links()

你倾向于调用的这个函数是 Laravel 分页器类的成员,$places 应该是这个类的实例来支持这个函数

如果你想继续这个实现,并支持 Laravel 分页功能,你应该创建一个 Laravel 实例 lengthawarepaginator 并手动创建分页器实例。

根据它的参数,这可能看起来像这样:

在您的前端控制器中添加:

$paginatedPlaces = new LengthAwarePaginator($places->data, $places->meta->total, $places->meta->per_page)

然后将$paginatedPlaces 传递给您的视图

第二个选项我建议不要使用 -&gt;links() 函数,只需简单地从响应中打印出 links 属性即可。

【讨论】:

  • 非常感谢@alireza.2281。我比我自己更喜欢您使用 LengthAwarePaginator 的方法,但我不能让它正常工作。分页生成的链接不正确,它们重定向到基本 url /?page=X 而不是正确的路由。
  • 解决了将 currentPage 和 options 值添加到 LengthAwarePaginator 构造函数:$paginatedPlaces = new LengthAwarePaginator($places->data, $places->meta->total, $places->meta->per_page, $places ->meta->current_page, ['path' => route('web.places.index') ]);谢谢!
猜你喜欢
  • 2019-04-11
  • 2019-09-09
  • 2021-01-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-09-28
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多