0

我正在使用 Laravel 的API 资源功能为客户端很好地格式化我的响应,但我遇到的问题是下面的代码;

/**
  * Transform the resource collection into an array.
  *
  * @param  \Illuminate\Http\Request  $request
  * @return array
  */
public function toArray($request)
{
    return [
        'data' => $this->collection->transform(function ($item)
        {
            return [
                'id' => $item->id,
                'title' => Str::limit($item->title, 32),
                'body' => Str::limit($item->body, 32),
                'created_at' => $item->created_at->format('d M Y, H:i a'),
                'user' => $item->user
            ];
        }),
        'links' => [
            'current_page' => $this->currentPage(),
            'total' => $this->total(),
            'per_page' => $this->perPage(),
        ],

    ];
}

使用此代码时,出现错误;属性"Call to a member function format() on null"上。created_at

但我已经习惯于dd($this->collection)确认实际上没有任何属性null,我不确定是什么原因造成的。我的迁移包含$table->timestamps();,并且在我的工厂内部,我根本没有覆盖时间戳,所以我不确定问题是什么。

这是我在下面运行的测试也得到了这个错误;

factory(News::class, 10)->create();

$user = factory(User::class)->create();

$this->actingAs($user)
    ->get('/news')
    ->assertOk()
    ->assertPropCount('news.data', 10)
    ->assertPropValue('news.data', function ($news)
    {
        $this->assertEquals(
            [
                'id', 'title', 'body', 'created_at',
                'user',
            ],
            array_keys($news[0])
        );
    });

当我在我的项目中使用InertiaJS时,额外的功能(例如assertPropCountassertPropValue)来自 InertiaJS 演示应用程序。

希望有人能够提供帮助,正如我在其他几个地方询问过的那样,似乎没有人知道这是什么原因,并且根据我的调试,似乎并没有太多有效的解释为什么created_at是空的。

作为一个注释,如果我也转向$item->user代码$item->user->toArray(),那么这也无法抱怨user它不是空的。似乎试图将任何方法链接到任何属性都会导致此空错误,我不确定为什么。

4

1 回答 1

2

首先请记住,transform您正在使用的函数会改变原始$this->collection属性,您最好使用map它与转换相同的目的而不改变原始数组。
这可能与您的问题有关,因为您正在修改要迭代的集合,这可能会导致问题。

此外,我建议您继续阅读此答案并尝试我在下面解释的两种重构替代方案之一。那是因为我认为您没有正确使用 API 资源,而正确使用它们实际上可以解决问题。

关于您的 API 资源结构
的建议正确的方法是拥有两个单独的文件:一个News资源和一个NewsCollection资源。
此设置允许定义单个新闻的渲染结构以及新闻集合,并在渲染后者时重用前者。

要正确实现 API 资源,有几种方法(根据您要实现的目标):

注意:在这两种方法中,我想当然地认为您已经有一个额外的User资源来定义渲染User模型的结构( a 的$this->user属性News)。

1)为单个资源和集合资源创建单独的类
您必须通过这两个工匠命令在资源文件夹中创建两个文件:

// Create the single News resource
php artisan make:resource News

// Create the NewsCollection resource
php artisan make:resource NewsCollection

现在您可以自定义收集逻辑:

NewsCollection.php

<?php

namespace App\Http\Resources;

use Illuminate\Http\Resources\Json\ResourceCollection;

class NewsCollection extends ResourceCollection
{
    /**
     * Transform the resource collection into an array.
     *
     * @param  \Illuminate\Http\Request  $request
     * @return array
     */
    public function toArray($request)
    {
        return [
            // Each $this->collection array item will be rendered automatically
            // with the News resource definition, so you can leave data as it is
            // and just customize the links section/add more data as you wish.
            'data' => $this->collection,
            'links' => [
                'current_page' => $this->currentPage(),
                'total' => $this->total(),
                'per_page' => $this->perPage(),
            ],
        ];
    }
}

以及单个 News 资源逻辑:

新闻.php

<?php

namespace App\Http\Resources;

use App\Http\Resources\User as UserResource;
use Illuminate\Http\Resources\Json\JsonResource;

class News extends JsonResource
{
    /**
     * Transform the resource into an array.
     *
     * @param  \Illuminate\Http\Request  $request
     * @return array
     */
    public function toArray($request)
    {
        return [
            'id' => $this->id,
            'title' => Str::limit($this->title, 32),
            'body' => Str::limit($this->body, 32),
            'created_at' => $this->created_at->format('d M Y, H:i a'),
            'user' => new UserResource($this->user)
        ];
    }
}

要呈现新闻集合,您只需执行以下操作:

use App\News;
use App\Http\Resources\NewsCollection;

// ...

return new NewsCollection(News::paginate());

当你将 NewsCollection 实例转换为响应时,Laravel 将自动重用资源类来渲染's数组News的每个元素。NewsCollection$this->collection

2)利用::collection单个新闻资源
的 方法此方法仅适用于您需要有关分页响应的元数据(这似乎是您试图通过代码实现的目标)。

您只需要一个可以生成的 News api 资源:

// Create the single News resource
php artisan make:resource News

然后根据自己的需要自定义单个资源:

新闻.php

<?php

namespace App\Http\Resources;

use App\Http\Resources\User as UserResource;
use Illuminate\Http\Resources\Json\JsonResource;

class News extends JsonResource
{
    /**
     * Transform the resource into an array.
     *
     * @param  \Illuminate\Http\Request  $request
     * @return array
     */
    public function toArray($request)
    {
        return [
            'id' => $this->id,
            'title' => Str::limit($this->title, 32),
            'body' => Str::limit($this->body, 32),
            'created_at' => $this->created_at->format('d M Y, H:i a'),
            'user' => new UserResource($this->user)
        ];
    }
}

然后渲染一个分页的新闻集合,只需执行以下操作:

use App\News;
use App\Http\Resources\News as NewsResource;

// ...

return NewsResource::collection(News::paginate());

第一种方法可以更好地整体控制生成的输出结构,但我不会构建$this->collection集合类内部。
定义每个集合元素的结构应该如何由News资源类负责。

第二种方法更快,并且与 Laravel 分页配合得非常好,允许您节省相当多的时间来生成带有链接的分页响应(这似乎是您希望从代码中实现的目标)。

对不起,很长的帖子,如果您需要进一步的解释,请询问。

于 2020-01-01T22:33:47.970 回答