Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[3.x] Re-worked pagination to not mutate the api classes #907

Merged
merged 3 commits into from
Dec 9, 2020
Merged

[3.x] Re-worked pagination to not mutate the api classes #907

merged 3 commits into from
Dec 9, 2020

Conversation

GrahamCampbell
Copy link
Contributor

@GrahamCampbell GrahamCampbell commented Jul 14, 2020

This re-works pagination to avoid mutating the API classes. It took a few attempts to get the implementation details right. Let me explain why I chose to use clone:

  1. If we were to use new static(...), then I would have had to have made the abstract api constructor final. This could cause problems for people mocking the api classes, and also limits the possibility to thunk up arguments to the api classes, say if we want to introduce an api class which takes an extra parameter which is always used (this is common place in the gitlab fork of this library).
  2. Is there another way to do things without making the constructor final or using clone? Well, we could have every api object implement perPage for themselves. This is kinda cumbersome though, and I don't think we want to do this.

This PR partially addresses #904.

So, I think cloning then setting the private property, within the abstract api class is the way to go, and nicely separates responsibilities. Classes that extend the abstract api class cannot mutate the property, and outsiders also cannot either. They have to call perPage which gives a fresh instance (one we've cloned).

@GrahamCampbell GrahamCampbell changed the title Re-worked pagination to not mutate the api classes [3.x] Re-worked pagination to not mutate the api classes Jul 16, 2020
@acrobat
Copy link
Collaborator

acrobat commented Sep 27, 2020

@GrahamCampbell I'm testing this PR but it doesn't seem to work. Is it a bug or am I doing something wrong?

$paginator  = new Github\ResultPager($client);
$api = $client->api('user');
$api = $api->perPage(2);

$result = $paginator->fetch($api, 'repositories', ['acrobat']);

This fetch function still uses the default of 100 items per page

@GrahamCampbell
Copy link
Contributor Author

Yeh, that is what I would expect to happen, and it's also how the current paginator works.

@GrahamCampbell
Copy link
Contributor Author

The paginator intentionally doesn't allow users to change the internal number of items per page.

@acrobat
Copy link
Collaborator

acrobat commented Sep 27, 2020

Well actually this does work with the old pager

dev-master

<?php

require_once 'vendor/autoload.php';

$client = new \Github\Client();
$client->authenticate('xxxxxx', \Github\Client::AUTH_ACCESS_TOKEN);

$paginator  = new Github\ResultPager($client);
$api = $client->api('user');
$api->setPerPage(2);

$result = $paginator->fetch($api, 'repositories', ['knplabs']);

echo \count($result) .PHP_EOL;
while (($result = $paginator->fetchNext()) !== []) {
    echo \count($result) .PHP_EOL;
}

Output

→ php test.php
2
2
2
2
2
2
2
2
2
2
2
// Stopped the script here

pager changes code:

<?php

require_once 'vendor/autoload.php';

$client = new \Github\Client();
$client->authenticate('xxxxx', \Github\Client::AUTH_ACCESS_TOKEN);

$paginator  = new Github\ResultPager($client);
$api = $client->api('user');
$api = $api->perPage(2);

$result = $paginator->fetch($api, 'repositories', ['knplabs']);

echo \count($result) .PHP_EOL;
while (($result = $paginator->fetchNext()) !== []) {
    echo \count($result) .PHP_EOL;
}

output

→ php test.php
100
8

Previously the callApi method was used as we now call the perPage method first

$result = $this->callApi($api, $method, $parameters);

https://github.com/KnpLabs/php-github-api/pull/907/files#diff-40de580826089d3ae27bf74e6b2b9402R76

@GrahamCampbell
Copy link
Contributor Author

Hmm, right. I suppose my question is why would anyone want to do that?

@acrobat
Copy link
Collaborator

acrobat commented Sep 29, 2020

I can see use cases for this like forexample the travis-ci settings page where you show all available repositories to enable. This could be a paginated list with 20 items per page. If there is no usecase for changing the per page amount, why do we have the perPage method?

@acrobat acrobat mentioned this pull request Sep 29, 2020
@GrahamCampbell
Copy link
Contributor Author

The perPage method allows the paginator to set the number of pages it wants. It has to be able to interface with the api classes somehow.

@acrobat
Copy link
Collaborator

acrobat commented Oct 5, 2020

Hmm I see there was the confusion on my part. Setting the per page amount in the resultpager constructor does the job.

$paginator  = new Github\ResultPager($client, 2);

But now I thinking that this might be confusing to for the users of this library. Could we keep the private property but use a closure bind setup to clone and update the perpage value without having a public perpage method which doesn't actually work with the paginator? What do you think @GrahamCampbell?

public function fetch(ApiInterface $api, string $method, array $parameters = [])
    {
        $paginatorPerPage = $this->perPage;
        $closure = \Closure::bind(function (AbstractApi $api) use ($paginatorPerPage) {
            $clone = clone $api;
            $clone->perPage = $paginatorPerPage;

            return $clone;
        }, null, AbstractApi::class);

        $api = $closure($api);
        $result = $api->$method(...$parameters);

        $this->postFetch();

        return $result;
    }

@acrobat
Copy link
Collaborator

acrobat commented Oct 24, 2020

@GrahamCampbell what do you think of the solution above? Would this be the way to finish this PR?

@acrobat
Copy link
Collaborator

acrobat commented Nov 12, 2020

@GrahamCampbell what do you think of the proposed solution? I would like to finish this PR and release the 3.0 😄

@GrahamCampbell
Copy link
Contributor Author

Sorry for the delay here. I will get to this soon. NB There are currently merge conflicts.

@acrobat
Copy link
Collaborator

acrobat commented Nov 20, 2020

No worries! 😄

@acrobat acrobat mentioned this pull request Nov 29, 2020
@GrahamCampbell
Copy link
Contributor Author

Rebased and made the relevant changes you wanted. How is this looking now, @acrobat?

Copy link
Collaborator

@acrobat acrobat left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A few things already, I will also try this change in my small test app later!

lib/Github/Api/Repo.php Show resolved Hide resolved
lib/Github/ResultPager.php Outdated Show resolved Hide resolved
test/Github/Tests/ResultPagerTest.php Show resolved Hide resolved
@GrahamCampbell
Copy link
Contributor Author

I've applied the changes you asked for. :)

@acrobat
Copy link
Collaborator

acrobat commented Dec 1, 2020

Yes, thank you! I will try to do a review and test in my demo app later tonight!

@acrobat
Copy link
Collaborator

acrobat commented Dec 3, 2020

My "test app" for the pagination worked great and intuitive with the latest changes 👌 I will do a quick code review later and prepare a PR for 2.x to deprecate some things changed/removed in this PR

@acrobat acrobat merged commit 9b0a7c3 into KnpLabs:master Dec 9, 2020
@acrobat
Copy link
Collaborator

acrobat commented Dec 9, 2020

Thank you @GrahamCampbell

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants