Reputation: 446
I have a platform that enables users to download sound clips. Producers of the sound clips can see their history of downloads, but they can also delete files they don't want available for download anymore. Each download event has a DB record with a polymorphic relationship to two different tables, depending on the sound clip type.
I'm trying to put together a GraphQL query that will return the download statistics within a selected date range, including download stats of soft deleted files. The Laravel query is returning the soft deleted records but the soft deleted records aren't available on the graphQL side.
Laravel Download
Model (each download event creates a record here) - it could be of a BirdSound
or a HumanSound
record:
class Download extends BaseModel
{
protected $types = [
'bird' => BirdSound::class,
'human' => HumanSound::class,
];
/**
* @return MorphTo|EloquentBuilder|QueryBuilder
*/
public function downloadable() : MorphTo
{
return $this->morphTo();
}
}
Laravel BirdSound
Model (there is an equivalent HumanSound
model). There is one record in this table for each file available for download:
class BirdSounds extends Sounds
{
use SoftDeletes;
/**
* @return MorphMany|EloquentBuilder|QueryBuilder
*/
public function Download(): MorphMany
{
return $this->morphMany(Download::class, 'downloadable');
}
}
GraphQL Schema:
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 function that gets the data:
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();
}
The above Laravel query returns both the download info AND the related downloadable data, whether it's been soft deleted or not, and with the graphQL schema described above, I can access the downloadable
object through the MediaInfo
relation on the Download
object. However, I can't figure out how to get the actual downloadable
relation as defined on the models available in graphQL - the relation always shows null
.
I've tried the following:
Changing the Media type:
type Media {
id: Int
downloadable_id: Int
name: String
downloadable_type: String
count: Int
downloadable: Downloadable @morphTo @softDeletes
}
Adding trashed: Trash
to the query (which I assume is only single-dimensional, which is why it wouldn't work):
extend type Query {
myTopDownloads(trashed: Trash, downloaded_at: DateRange)): [Download]
@field(resolver: "App\\GraphQL\\Queries\\FreeDownloads@topDownloads")
}
...I've tried multiple variations of the above examples, all of which result in a null
for the downloadable
object or I get an error.
Query example:
{
myTopDownloads{
downloadable_id
downloadable_type
count
downloadable(trashed:WITH) {
__typename
id
name
}
}
}
"debugMessage": "Use @trashed only for Model classes that use the SoftDeletes trait.",
The Laravel models all use the SoftDeletes trait, and I couldn't find documentation on adding a specific trait to the graphQL type (ex: in my BirdSound
type).
I assume the issue may be with the fact that it's a polymorphic relationship, but this is my first project using GraphQL and I'm still trying to wrap my head around some of the details...Any insight into this would be great!
Upvotes: 0
Views: 1469
Reputation: 31
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.
Upvotes: 3