rebing/graphql-laravel

Pagination on Relation

hniehorster opened this issue · 1 comments

Versions:

  • graphql-laravel Version: 9.1
  • Laravel Version: 10.10
  • PHP Version: 8.1

Description:

Trying to paginate the relations through a parent pivot.

A user has topics and a topic can have multiple users. Multiple users can be the owner of a topic. A topic has messages.

class Topic extends Model
{
    //Casts Fillables etc. 

    public function user(): BelongsToMany
    {
        return $this->belongsToMany(User::class, 'topic_users', 'topic_uuid', 'user_uuid')
            ->withPivot([
                'uuid',
                'user_uuid',
                'is_owner',
                'created_at',
                'updated_at',
            ])
            ->using(User::class);
    }

    public function messages(): HasMany
    {
        return $this->hasMany(Message::class, 'topic_uuid', 'uuid');
    }
}
class User extends Authenticatable
{
    //Casts Fillables etc

        public function topics(): BelongsToMany
    {
        return $this->belongsToMany(Topic::class, 'topic_users')
            ->withPivot(
                'uuid',
                'is_owner',
                'created_at',
                'updated_at'
            );
    }
}

TopicType

<?php

declare(strict_types=1);

namespace App\GraphQL\Types;

use App\Models\Topic;
use GraphQL\Type\Definition\Type;
use Rebing\GraphQL\Support\Facades\GraphQL;
use Rebing\GraphQL\Support\Type as GraphQLType;

class TopicType extends GraphQLType
{
    protected $attributes = [
        'name'        => 'Topic',
        'description' => 'The topic and it's messages',
        'model'       => Topic::class,
    ];

    public function resolveMessagesField($root, $args) {
        return $root->messages()->paginate($args['limit'], ['*'], 'page', $args['page']);
    }

    public function fields(): array
    {
        return [
            'uuid' => [
                'type'        => Type::string(),
                'description' => 'The UUID of the Topic',
            ],
            'title' => [
                'type'        => Type::string(),
                'description' => 'The title of the Topic',
            ],
            'slug' => [
                'type'        => Type::string(),
                'description' => 'The slug of the Topic',
            ],
            'key' => [
                'type'        => Type::string(),
                'description' => 'The unique key that identifies the topic',
            ],

            'last_message_at' => [
                'type'        => Type::string(),
                'description' => 'UTC Timestamp of when the last message has been send',
            ],
            'last_message' => [
                'type'        => Type::nonNull(GraphQL::type('Message')),
                'description' => 'The last message that has been sent in this topic',
            ],
            'messages' => [
                'type' => GraphQL::paginate('Message'),
                'description' => 'The messages in this topic',
                'args' => [
                    'limit' => [
                        'name' => 'limit',
                        'type' => Type::int(),
                        'rules' => ['required', 'numeric', 'min:1', 'max:20'],
                    ],
                    'page' => [
                        'name' => 'page',
                        'type' => Type::int(),
                    ],
                ]
            ]
        ];
    }
}

Message Type

<?php

declare(strict_types=1);

namespace App\GraphQL\Types;

use App\Models\Message;
use GraphQL\Type\Definition\Type;
use Rebing\GraphQL\Support\Facades\GraphQL;
use Rebing\GraphQL\Support\Type as GraphQLType;

class MessageType extends GraphQLType
{
    protected $attributes = [
        'name'        => 'Message',
        'description' => 'Represents the message type',
        'model'       => Message::class
    ];

    public function fields(): array
    {
        return [
            'uuid' => [
                'type'        => Type::nonNull(Type::string()),
                'description' => 'The UUID of the message',
            ],
            'topic_uuid' => [
                'type'        => Type::nonNull(Type::string()),
                'description' => 'The UUID of the Topic',
            ],
            'title' => [
                'type'        => Type::string(),
                'description' => 'Message Title',
            ],
            'message' => [
                'type'        => Type::string(),
                'description' => 'The actual message',
            ],
            'received_at' => [
                'type'        => Type::nonNull(Type::string()),
                'description' => 'The timestamp the server has received the message',
            ],
            'processed_at' => [
                'type'        => Type::string(),
                'description' => 'The timestamp the server was done processing the message',
            ],
        ];
    }
}

TopicQuery

<?php

declare(strict_types=1);

namespace App\GraphQL\Queries;

use App\GraphQL\Helpers\GraphQLHelpers;
use App\Models\Topic;
use Closure;
use GraphQL\Type\Definition\ResolveInfo;
use GraphQL\Type\Definition\Type;
use Illuminate\Support\Facades\Auth;
use Rebing\GraphQL\Support\Facades\GraphQL;
use Rebing\GraphQL\Support\Query;

class TopicQuery extends Query
{
    protected $attributes = [
        'name'        => 'topic',
        'description' => 'A query to fetch the topic and the last 25 messages',
    ];

    public function type(): Type
    {
        return GraphQL::type( 'Topic');
    }

    public function args(): array
    {
        return [
            'uuid' => [
                'type'        => Type::nonNull(Type::string()),
                'description' => 'The UUID of the Topic',
            ],
            'limit' => [
                'type'         => Type::int(),
                'defaultValue' => GraphQLHelpers::DEFAULT_PAGE_SIZE,
                'description'  => 'The amount of items you want to fetch',
                'rules'        => ['max:50'],
            ],
            'page' => [
                'type'         => Type::int(),
                'defaultValue' => GraphQLHelpers::DEFAULT_PAGE_NUMBER,
            ],
        ];
    }

    public function resolve($root, array $args, $context, ResolveInfo $resolveInfo, Closure $getSelectFields)
    {
        $fields = $getSelectFields();

        return= Topic::query()->whereHas('user', function ($query) {
            return $query->where('user_uuid', '=', Auth::guard('graphql')->user()->uuid);
        })
            ->where('uuid', $args['uuid'])
            ->select($fields->getSelect())
            ->with('messages')
            ->paginate($args['limit'], ['*'], 'page', $args['page']);
    }
}

The query I'm trying to achieve:

{
  topic(uuid: "7ca8af62-8bb5-4013-9141-82483fea06fa") {
    uuid
    title
    messages(limit: 2, page: 1) {
      data {
        uuid
      }
    }
  }
}

The args are passed correctly
image

I still get the 10 first messages, while the topic itself is correctly fetched.

It did work, but you will not see it in the dump .