首页 > 解决方案 > Lighthouse/GraphQL - 在用于软删除的 GraphQL 模式中为多态关系获取“null”

问题描述

我有一个平台可以让用户下载声音片段。声音剪辑的制作者可以查看他们的下载历史记录,但他们也可以删除他们不想再下载的文件。每个下载事件都有一个 DB 记录,该记录与两个不同的表具有多态关系,具体取决于声音剪辑类型。

我正在尝试组合一个 GraphQL 查询,该查询将返回选定日期范围内的下载统计信息,包括软删除文件的下载统计信息。Laravel 查询正在返回软删除记录,但软删除记录在 graphQL 端不可用。

LaravelDownload模型(每个下载事件在此处创建一个记录) - 它可以是一个BirdSound或一个HumanSound记录:

class Download extends BaseModel
{
    protected $types = [
        'bird' => BirdSound::class,
        'human' => HumanSound::class,
    ];

    /**
     * @return MorphTo|EloquentBuilder|QueryBuilder
     */
    public function downloadable() : MorphTo
    {
        return $this->morphTo();
    }
}

LaravelBirdSound模型(有一个等效HumanSound模型)。对于可供下载的每个文件,此表中有一条记录:

class BirdSounds extends Sounds
{
    use SoftDeletes;

    /**
     * @return MorphMany|EloquentBuilder|QueryBuilder
     */
    public function Download(): MorphMany
    {
        return $this->morphMany(Download::class, 'downloadable');
    }
}

GraphQL 架构:

type Download {
    id: ID!
    name: String!
    email: String!
    downloadable: Downloadable @morphTo
}

interface Downloadable {
    id: ID!
    name: String
    freeDownloads: [Download!] @morphMany
}

type BirdSound implements Downloadable {
    id: ID!
    name: String
    duration: String
    user: User @belongsTo(relation: "user")
    filename: String
    freeDownloads: [Download] @morphMany 
}

type HumanSound implements Downloadable {
    id: ID!
    name: String
    snippet: Int
    user: User @belongsTo(relation: "user")
    artwork_id: Int
    freeDownloads: [Download] @morphMany
}

# Using DownloadCount type to handle the data returned by the laravel function that counts the downloads
type DownloadCount {
    downloadable_id: Int
    downloadable_type: String
    count: Int
    downloadable: MediaInfo
}

# Using MediaInfo instead of the actual `downloadable` object in order to include soft-deleted records, 
# which I can't get working with the polymorphic relationship for lighthouse
type MediaInfo {
    id: ID! # ID of the downloadable record
    name: String # name from the BirdSound or HumanSound
}

extend type Query {
    myTopDownloads(downloaded_at: DateRange)): [DownloadCount]  
        @field(resolver: "App\\GraphQL\\Queries\\FreeDownloads@topDownloads")
}

获取数据的 Laravel 函数:

public function topDownloads($rootValue, array $args, GraphQLContext $context, ResolveInfo $resolveInfo)
{
    return auth()->guard('graphql')->user()->freeDownloads()
                    ->selectRaw('id, downloadable_id, downloadable_type, count(downloadable_id) as count')
                    ->groupBy('downloadable_id')
                    ->orderBy('count', 'desc')
                    ->with(['downloadable' => function($query){
                        $query->withTrashed();
                    }])
                    ->limit($limit)
                    ->get();
}

上面的 Laravel 查询返回下载信息和相关的可下载数据,无论它是否被软删除,并且使用上面描述的 graphQL 模式,我可以通过对象上downloadableMediaInfo关系访问该Download对象。但是,我无法弄清楚如何获得downloadable在 graphQL 中可用的模型上定义的实际关系 - 关系总是显示null.

我尝试了以下方法:

更改媒体类型

type Media {
    id: Int
    downloadable_id: Int
    name: String
    downloadable_type: String
    count: Int
    downloadable: Downloadable @morphTo @softDeletes
}

添加trashed: Trash到查询中(我假设它只是一维的,这就是它不起作用的原因):

extend type Query {
    myTopDownloads(trashed: Trash, downloaded_at: DateRange)): [Download]  
        @field(resolver: "App\\GraphQL\\Queries\\FreeDownloads@topDownloads")
}

...我尝试了上述示例的多种变体,所有这些都导致对象 anulldownloadable我收到错误。

查询示例:

{
  myTopDownloads{
    downloadable_id
    downloadable_type
    count
    downloadable(trashed:WITH) {
      __typename
      id
      name
    }
  }
}

"debugMessage": "@trashed 仅用于使用 SoftDeletes 特征的模型类。",

Laravel 模型都使用 SoftDeletes 特征,我找不到有关向 graphQL 类型添加特定特征的文档(例如:在我的BirdSound类型中)。

认为问题可能在于它是一种多态关系,但这是我使用 GraphQL 的第一个项目,我仍在努力了解一些细节......对此的任何见解都会很棒!

标签: graphqllaravel-lighthouse

解决方案


According to lighthouse, in a polymorphic relationship, you should point to a union indicating which types it may return.

union Downloadable = HumanSound | BirdSound

https://lighthouse-php.com/master/eloquent/polymorphic-relationships.html#one-to-many

To query those types, use the notation to specify on a given type, which fields to return

{
  myTopDownloads{
    downloadable_id
    downloadable_type
    count
    downloadable(trashed:WITH) {
      __typename
      ... on BirdSound {
        id
        name
      }
      ... on HumanSound {
        id
        name
      }
    }
  }
}

https://graphql.org/learn/schema/#union-types

Don't forget you need to specify the relationships in the models, including the return type, and remove the implements.


推荐阅读