From 5e3d242c35b8600e510bc4687dcdae44f65a657e Mon Sep 17 00:00:00 2001 From: Isern Palaus Date: Fri, 24 Jan 2014 00:50:41 +0100 Subject: [PATCH 1/2] Added a feature to use cursors to navigate collections. --- src/League/Fractal/Cursor/Cursor.php | 123 ++++++++++++++++++ src/League/Fractal/Cursor/CursorInterface.php | 26 ++++ src/League/Fractal/Resource/Collection.php | 36 ++++- src/League/Fractal/Scope.php | 31 ++++- .../Fractal/Test/Cursor/CursorAdapterTest.php | 28 ++++ .../Fractal/Test/Resource/CollectionTest.php | 9 ++ tests/League/Fractal/Test/ScopeTest.php | 34 ++++- 7 files changed, 280 insertions(+), 7 deletions(-) create mode 100644 src/League/Fractal/Cursor/Cursor.php create mode 100644 src/League/Fractal/Cursor/CursorInterface.php create mode 100644 tests/League/Fractal/Test/Cursor/CursorAdapterTest.php diff --git a/src/League/Fractal/Cursor/Cursor.php b/src/League/Fractal/Cursor/Cursor.php new file mode 100644 index 00000000..22b3f00a --- /dev/null +++ b/src/League/Fractal/Cursor/Cursor.php @@ -0,0 +1,123 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace League\Fractal\Cursor; + +/** + * A generic cursor adapter. + * + * @author Isern Palaus + */ +class Cursor implements CursorInterface +{ + + /** + * Current cursor value. + * + * @var mixed + */ + protected $current; + + /** + * Next cursor value. + * + * @var mixed + */ + protected $next; + + /** + * Items being holded for the current cursor position. + * + * @var integer + */ + protected $count; + + /** + * Create a new Cursor instance. + * + * @param mixed $current + * @param mixed $next + * @param integer $count + */ + public function __construct($current = null, $next = null, $count = null) + { + $this->current = $current; + $this->next = $next; + $this->count = $count; + } + + /** + * Get the current cursor value. + * + * @return mixed + */ + public function getCurrent() + { + return $this->current; + } + + /** + * Set the current cursor value. + * + * @param mixed $current + * @return League\Fractal\Cursor\Cursor + */ + public function setCurrent($current) + { + $this->current = $current; + return $this; + } + + /** + * Get the next cursor value. + * + * @return mixed + */ + public function getNext() + { + return $this->next; + } + + /** + * Set the next cursor value. + * + * @param mixed $next + * @return League\Fractal\Cursor\Cursor + */ + public function setNext($next) + { + $this->next = $next; + return $this; + } + + /** + * Returns the total items in the current cursor. + * + * @return integer + */ + public function getCount() + { + return $this->count; + } + + /** + * Set the total items in the current cursor. + * + * @param integer $count + * @return League\Fractal\Cursor\Cursor + */ + public function setCount($count) + { + $this->count = $count; + return $this; + } + +} diff --git a/src/League/Fractal/Cursor/CursorInterface.php b/src/League/Fractal/Cursor/CursorInterface.php new file mode 100644 index 00000000..b6f9ae0d --- /dev/null +++ b/src/League/Fractal/Cursor/CursorInterface.php @@ -0,0 +1,26 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace League\Fractal\Cursor; + +/** + * A common interface for cursors to use. + * + * @author Isern Palaus + */ +interface CursorInterface +{ + + public function getCurrent(); + public function getNext(); + public function getCount(); + +} diff --git a/src/League/Fractal/Resource/Collection.php b/src/League/Fractal/Resource/Collection.php index 5822151c..a68f4289 100644 --- a/src/League/Fractal/Resource/Collection.php +++ b/src/League/Fractal/Resource/Collection.php @@ -11,12 +11,13 @@ namespace League\Fractal\Resource; +use League\Fractal\Cursor\CursorInterface; use League\Fractal\Pagination\PaginatorInterface; /** * Resource Collection * - * The data can be a collection of any sort of data, as long as the + * The data can be a collection of any sort of data, as long as the * "collection" is either array or an object implementing ArrayIterator. */ class Collection implements ResourceInterface @@ -27,7 +28,7 @@ class Collection implements ResourceInterface * @var array|ArrayIterator */ protected $data; - + /** * A collection of data * @@ -35,6 +36,13 @@ class Collection implements ResourceInterface */ protected $paginator; + /** + * Cursor implementation. + * + * @var League\Fractal\Cursor\CursorInterface + */ + protected $cursor; + /** * A callable to process the data attached to this resource * @@ -51,7 +59,7 @@ public function __construct($data, $transformer) $this->data = $data; $this->transformer = $transformer; } - + /** * Getter for data * @@ -72,6 +80,16 @@ public function getPaginator() return $this->paginator; } + /** + * Set the cursor implementation. + * + * @return League\Fractal\Cursor\CursorInterface + */ + public function getCursor() + { + return $this->cursor; + } + /** * Getter for transformer * @@ -94,4 +112,16 @@ public function setPaginator(PaginatorInterface $paginator) $this->paginator = $paginator; return $this; } + + /** + * Set the cursor implementation. + * + * @param League\Fractal\Cursor\CursorInterface $cursor + */ + public function setCursor(CursorInterface $cursor) + { + $this->cursor = $cursor; + return $this; + } + } diff --git a/src/League/Fractal/Scope.php b/src/League/Fractal/Scope.php index 498777f6..31f65f30 100644 --- a/src/League/Fractal/Scope.php +++ b/src/League/Fractal/Scope.php @@ -15,6 +15,7 @@ use League\Fractal\Resource\Collection; use League\Fractal\Resource\ResourceInterface; use League\Fractal\Pagination\PaginatorInterface; +use League\Fractal\Cursor\CursorInterface; class Scope { @@ -39,7 +40,7 @@ public function embedChildScope($scopeIdentifier, $resource) { return $this->resourceManager->createData($resource, $scopeIdentifier, $this); } - + /** * Getter for currentScope * @@ -59,7 +60,7 @@ public function getParentScopes() { return $this->parentScopes; } - + public function isRequested($checkScopeSegment) { if ($this->parentScopes) { @@ -122,6 +123,12 @@ public function toArray() if ($paginator !== null and $paginator instanceof PaginatorInterface) { $output['pagination'] = $this->outputPaginator($paginator); } + + $cursor = $this->resource->getCursor(); + + if ($cursor !== null and $cursor instanceof CursorInterface) { + $output['cursor'] = $this->outputCursor($cursor); + } } return $output; @@ -157,7 +164,7 @@ protected function fireTransformer($transformer, $data) $this->availableEmbeds = $transformer->getAvailableEmbeds(); } - + return $processedData; } @@ -189,6 +196,24 @@ protected function outputPaginator(PaginatorInterface $paginator) return $pagination; } + /** + * Generates output for cursor adapters. We don't type hint current/next + * because they can be either a string or a integer. + * + * @param League\Fractal\Cursor\CursorInterface $cursor + * @return array + */ + protected function outputCursor(CursorInterface $cursor) + { + $cursor = array( + 'current' => $cursor->getCurrent(), + 'next' => $cursor->getNext(), + 'count' => (int) $cursor->getCount(), + ); + + return $cursor; + } + protected function runAppropriateTransformer() { // if's n shit diff --git a/tests/League/Fractal/Test/Cursor/CursorAdapterTest.php b/tests/League/Fractal/Test/Cursor/CursorAdapterTest.php new file mode 100644 index 00000000..70678d5b --- /dev/null +++ b/tests/League/Fractal/Test/Cursor/CursorAdapterTest.php @@ -0,0 +1,28 @@ +assertEquals($cursor->getCurrent(), 100); + $this->assertEquals($cursor->getNext(), 110); + $this->assertEquals($cursor->getCount(), 10); + + $cursor->setCurrent(110); + $cursor->setNext(114); + $cursor->setCount(4); + + $this->assertEquals($cursor->getCurrent(), 110); + $this->assertEquals($cursor->getNext(), 114); + $this->assertEquals($cursor->getCount(), 4); + } + +} diff --git a/tests/League/Fractal/Test/Resource/CollectionTest.php b/tests/League/Fractal/Test/Resource/CollectionTest.php index ccaab577..1feafcc7 100755 --- a/tests/League/Fractal/Test/Resource/CollectionTest.php +++ b/tests/League/Fractal/Test/Resource/CollectionTest.php @@ -1,5 +1,6 @@ assertInstanceOf('Illuminate\Pagination\Paginator', $collection->getPaginator()); } + public function testGetCursor() + { + $cursor = new Cursor; + $collection = Mockery::mock('League\Fractal\Resource\Collection')->makePartial(); + $collection->setCursor($cursor); + $this->assertInstanceOf('League\Fractal\Cursor\Cursor', $collection->getCursor()); + } + public function tearDown() { Mockery::close(); diff --git a/tests/League/Fractal/Test/ScopeTest.php b/tests/League/Fractal/Test/ScopeTest.php index 96c01534..700f0293 100755 --- a/tests/League/Fractal/Test/ScopeTest.php +++ b/tests/League/Fractal/Test/ScopeTest.php @@ -1,5 +1,6 @@ shouldReceive('getPerPage')->once()->andReturn($perPage); $paginator->shouldReceive('getCurrentPage')->once()->andReturn($currentPage); $paginator->shouldReceive('getLastPage')->once()->andReturn($lastPage); - $paginator->shouldReceive('getUrl')->times(2)->andReturnUsing(function ($page) { + $paginator->shouldReceive('getUrl')->times(2)->andReturnUsing(function ($page) { return 'http://example.com/foo?page='.$page; }); @@ -245,6 +246,37 @@ public function testPaginatorOutput() $this->assertEquals($expectedOutput, $rootScope->toArray()); } + public function testCursorOutput() + { + $manager = new Manager(); + + $collection = new Collection(array(array('foo' => 'bar', 'baz' => 'ban')), function (array $data) { + return $data; + }); + + $cursor = new Cursor(0, 'ban', 2); + + $collection->setCursor($cursor); + + $rootScope = $manager->createData($collection); + + $expectedOutput = array( + 'cursor' => array( + 'current' => 0, + 'next' => 'ban', + 'count' => 2, + ), + 'data' => array( + array( + 'foo' => 'bar', + 'baz' => 'ban', + ), + ), + ); + + $this->assertEquals($expectedOutput, $rootScope->toArray()); + } + public function tearDown() { Mockery::close(); From eb502d561eb910668bdbb21e2d259245ed6db33f Mon Sep 17 00:00:00 2001 From: Isern Palaus Date: Fri, 24 Jan 2014 01:05:18 +0100 Subject: [PATCH 2/2] Fixed PRS-2 CodingStyle. --- src/League/Fractal/Cursor/Cursor.php | 2 -- src/League/Fractal/Cursor/CursorInterface.php | 2 -- src/League/Fractal/Resource/Collection.php | 1 - 3 files changed, 5 deletions(-) diff --git a/src/League/Fractal/Cursor/Cursor.php b/src/League/Fractal/Cursor/Cursor.php index 22b3f00a..67d1d072 100644 --- a/src/League/Fractal/Cursor/Cursor.php +++ b/src/League/Fractal/Cursor/Cursor.php @@ -18,7 +18,6 @@ */ class Cursor implements CursorInterface { - /** * Current cursor value. * @@ -119,5 +118,4 @@ public function setCount($count) $this->count = $count; return $this; } - } diff --git a/src/League/Fractal/Cursor/CursorInterface.php b/src/League/Fractal/Cursor/CursorInterface.php index b6f9ae0d..4f22ee15 100644 --- a/src/League/Fractal/Cursor/CursorInterface.php +++ b/src/League/Fractal/Cursor/CursorInterface.php @@ -18,9 +18,7 @@ */ interface CursorInterface { - public function getCurrent(); public function getNext(); public function getCount(); - } diff --git a/src/League/Fractal/Resource/Collection.php b/src/League/Fractal/Resource/Collection.php index a68f4289..606a19a8 100644 --- a/src/League/Fractal/Resource/Collection.php +++ b/src/League/Fractal/Resource/Collection.php @@ -123,5 +123,4 @@ public function setCursor(CursorInterface $cursor) $this->cursor = $cursor; return $this; } - }