Skip to content

Commit 54275af

Browse files
authored
Merge pull request #103 from OwlyCode/promise-paginator
Added promise handling on Paginator
2 parents 01c3a7d + 5d92925 commit 54275af

File tree

3 files changed

+88
-19
lines changed

3 files changed

+88
-19
lines changed

Relay/Connection/Paginator.php

Lines changed: 42 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -11,23 +11,34 @@
1111

1212
namespace Overblog\GraphQLBundle\Relay\Connection;
1313

14+
use GraphQL\Executor\Promise\Promise;
1415
use Overblog\GraphQLBundle\Definition\Argument;
1516
use Overblog\GraphQLBundle\Relay\Connection\Output\Connection;
1617
use Overblog\GraphQLBundle\Relay\Connection\Output\ConnectionBuilder;
1718

1819
class Paginator
1920
{
21+
const MODE_REGULAR = false;
22+
const MODE_PROMISE = true;
23+
2024
/**
2125
* @var callable
2226
*/
2327
private $fetcher;
2428

29+
/**
30+
* @var bool
31+
*/
32+
private $promise;
33+
2534
/**
2635
* @param callable $fetcher
36+
* @param bool $promise
2737
*/
28-
public function __construct(callable $fetcher)
38+
public function __construct(callable $fetcher, $promise = self::MODE_REGULAR)
2939
{
3040
$this->fetcher = $fetcher;
41+
$this->promise = $promise;
3142
}
3243

3344
/**
@@ -47,10 +58,12 @@ public function backward($args, $total, array $callableArgs = [])
4758

4859
$entities = call_user_func($this->fetcher, $offset, $limit);
4960

50-
return ConnectionBuilder::connectionFromArraySlice($entities, $args, [
51-
'sliceStart' => $offset,
52-
'arrayLength' => $total,
53-
]);
61+
return $this->handleEntities($entities, function ($entities) use ($args, $offset, $total) {
62+
return ConnectionBuilder::connectionFromArraySlice($entities, $args, [
63+
'sliceStart' => $offset,
64+
'arrayLength' => $total,
65+
]);
66+
});
5467
}
5568

5669
/**
@@ -68,14 +81,18 @@ public function forward($args)
6881
if (!is_numeric(ConnectionBuilder::cursorToOffset($args['after'])) || !$args['after']) {
6982
$entities = call_user_func($this->fetcher, $offset, $limit + 1);
7083

71-
return ConnectionBuilder::connectionFromArray($entities, $args);
84+
return $this->handleEntities($entities, function ($entities) use ($args) {
85+
return ConnectionBuilder::connectionFromArray($entities, $args);
86+
});
7287
} else {
7388
$entities = call_user_func($this->fetcher, $offset, $limit + 2);
7489

75-
return ConnectionBuilder::connectionFromArraySlice($entities, $args, [
76-
'sliceStart' => $offset,
77-
'arrayLength' => $offset + count($entities),
78-
]);
90+
return $this->handleEntities($entities, function ($entities) use ($args, $offset) {
91+
return ConnectionBuilder::connectionFromArraySlice($entities, $args, [
92+
'sliceStart' => $offset,
93+
'arrayLength' => $offset + count($entities),
94+
]);
95+
});
7996
}
8097
}
8198

@@ -97,6 +114,21 @@ public function auto($args, $total, $callableArgs = [])
97114
}
98115
}
99116

117+
/**
118+
* @param array|object $entities An array of entities to paginate or a promise
119+
* @param callable $callback
120+
*
121+
* @return Connection|object A connection or a promise
122+
*/
123+
private function handleEntities($entities, callable $callback)
124+
{
125+
if ($this->promise) {
126+
return $entities->then($callback);
127+
}
128+
129+
return call_user_func($callback, $entities);
130+
}
131+
100132
/**
101133
* @param Argument|array $args
102134
*

Resources/doc/helpers/relay-paginator.md

Lines changed: 27 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -16,20 +16,20 @@ This method can be used to get a slice of a data set by passing:
1616
- the args, as a `ConnectionArguments` object
1717
- the meta, as a `ArraySliceMetaInfo` object
1818

19-
The sliced data set must contains:
19+
The sliced data set must contains:
2020

2121
- the item before the first item you want
2222
- the item after the slice, so `PageInfo->hasNextPage` can be calculated
23-
24-
Exemple:
25-
23+
24+
Example:
25+
2626
- full data set is `['A','B','C','D','E']`
2727
- we want 2 items after `A`, meaning `['B','C']`
2828

2929

3030
- `after` cursor will be `arrayconnection:0`
3131
- `offset` will be calculated to `0`
32-
- so we need to passed a sliced data with `['A','B','C','D']` to `connectionFromArraySlice()`
32+
- so we need to passed a sliced data with `['A','B','C','D']` to `connectionFromArraySlice()`
3333

3434
## Paginator
3535

@@ -39,8 +39,8 @@ The purpose if this helper is to provide an easy way to paginate in a data set p
3939

4040
When constructing the paginator, you need to pass a callable which will be responsible for providing the sliced data set.
4141

42-
### Exemple
43-
42+
### Example
43+
4444
#### With a `first` Relay parameter
4545

4646
```php
@@ -120,7 +120,7 @@ $paginator = new Paginator(function ($offset, $limit) {
120120
$result = $paginator->forward(
121121
new Argument(
122122
[
123-
'first' => 1,
123+
'first' => 1,
124124
'after' => base64_encode('arrayconnection:2')
125125
]
126126
)
@@ -145,7 +145,7 @@ array(1) {
145145
```
146146

147147
**Important note:**
148-
148+
149149
The callback function will receive:
150150

151151
- `$offset = 2`
@@ -194,3 +194,21 @@ $result = $paginator->backward(
194194
```
195195

196196
You should get the 4 last items of the _data set_.
197+
198+
#### Promise handling
199+
200+
Paginator also supports promises if you [use that feature](https://github.com/webonyx/graphql-php/pull/67)
201+
with the bundle. All you have to do is to toggle the `MODE_PROMISE` flag on and
202+
update your callback to return a `Executor/Promise/Promise` instance.
203+
204+
```php
205+
// Let's pretend we use dataloader ( https://github.com/overblog/dataloader-php )
206+
public function resolveList($args)
207+
{
208+
$pagination = new Paginator(function ($offset, $limit) {
209+
return $this->dataLoader->loadMany($this->elasticsearch->getIds($offset, $limit));
210+
}, Paginator::MODE_PROMISE); // This flag indicates that we will return a promise instead of an array of instances
211+
212+
return $pagination->forward($args);
213+
}
214+
```

Tests/Relay/Connection/PaginatorTest.php

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -282,4 +282,23 @@ public function testTotalCallableWithArguments()
282282
$this->assertSameEdgeNodeValue(['B', 'C', 'D', 'E'], $result);
283283
$this->assertTrue($result->pageInfo->hasPreviousPage);
284284
}
285+
286+
public function testPromiseMode()
287+
{
288+
$promise = $this->getMockBuilder('GraphQL\Executor\Promise\Promise')
289+
->setMethods(['then'])
290+
->getMock()
291+
;
292+
293+
$promise->expects($this->once())->method('then');
294+
295+
$paginator = new Paginator(function ($offset, $limit) use ($promise) {
296+
$this->assertSame(0, $offset);
297+
$this->assertSame(5, $limit);
298+
299+
return $promise;
300+
}, Paginator::MODE_PROMISE);
301+
302+
$result = $paginator->auto(new Argument(['first' => 4]), 5);
303+
}
285304
}

0 commit comments

Comments
 (0)