From ec01d74dfd02d86aba7bfd9a3ed84c3f11fdc0bd Mon Sep 17 00:00:00 2001 From: Douglas Greenshields Date: Fri, 11 Aug 2017 13:45:10 +0200 Subject: [PATCH] ensure resource array promises automatically filter out nulls --- src/Promise/ResolvedResourceTrait.php | 13 ++++++++++++- src/Promise/ResourceArrayPromise.php | 15 +++++++++++++++ src/ResourceArray.php | 17 +++++++++++++++-- tests/Promise/ResourceArrayPromiseTest.php | 17 ++++++++++++++++- tests/ResourceArrayTest.php | 3 ++- 5 files changed, 60 insertions(+), 5 deletions(-) diff --git a/src/Promise/ResolvedResourceTrait.php b/src/Promise/ResolvedResourceTrait.php index c7ca949..e3f0077 100644 --- a/src/Promise/ResolvedResourceTrait.php +++ b/src/Promise/ResolvedResourceTrait.php @@ -3,6 +3,7 @@ namespace Markup\Contentful\Promise; use function GuzzleHttp\Promise\is_fulfilled; +use GuzzleHttp\Promise\PromiseInterface; use Markup\Contentful\ResourceArray; use Markup\Contentful\ResourceInterface; @@ -21,7 +22,17 @@ private function ensureResolved() if (is_fulfilled($promise) && null !== $this->resolvedResource) { return; } - $this->resolvedResource = $promise->wait(); + $this->doResolve($promise); + } + + protected function doResolve(PromiseInterface $promise) + { + $this->setResolvedResource($promise->wait()); + } + + protected function setResolvedResource($resource) + { + $this->resolvedResource = $resource; } /** diff --git a/src/Promise/ResourceArrayPromise.php b/src/Promise/ResourceArrayPromise.php index 5ef4982..75c9670 100644 --- a/src/Promise/ResourceArrayPromise.php +++ b/src/Promise/ResourceArrayPromise.php @@ -145,4 +145,19 @@ protected function addRejectionHandlerToPromise(PromiseInterface $promise) return new ResourceArray([], 0, 0, 0); }); } + + protected function doResolve(PromiseInterface $promise) + { + $resolved = $promise->wait(); + //temporarily set resolved resource with array that may contain nulls + $this->setResolvedResource($resolved); + //now set it again but with access to skip/limit parameters etc - using a resource array will auto-filter nulls + $this->setResolvedResource(new ResourceArray( + $resolved, + $this->getTotal(), + $this->getSkip(), + $this->getLimit(), + $this->getEnvelope() + )); + } } diff --git a/src/ResourceArray.php b/src/ResourceArray.php index 396c8e1..5013968 100644 --- a/src/ResourceArray.php +++ b/src/ResourceArray.php @@ -45,9 +45,9 @@ public function __construct($items, $total, $skip, $limit, ResourceEnvelope $env return $resource instanceof ResourceInterface; }; if ($items instanceof \Traversable) { - $this->items = array_filter(array_values(iterator_to_array($items)), $filterNonResource); + $this->items = array_values(array_filter(array_values(iterator_to_array($items)), $filterNonResource)); } elseif (is_array($items)) { - $this->items = array_filter(array_values($items), $filterNonResource); + $this->items = array_values(array_filter(array_values($items), $filterNonResource)); } else { throw new \InvalidArgumentException('Items parameter should be an array or a traversable object.'); } @@ -157,4 +157,17 @@ public function offsetGet($offset) return $this->items[$offset]; } + + /** + * Ensures that this resource array strips out missing resources if they don't exist, so that counts and members + * always reflect resources only. + * + * This will resolve a resource array if it is a promise/ future. + * + * @return void + */ + public function ensureNoMissing() + { + // constructor already ensures no missing, so no action necessary here + } } diff --git a/tests/Promise/ResourceArrayPromiseTest.php b/tests/Promise/ResourceArrayPromiseTest.php index feb420b..22b2488 100644 --- a/tests/Promise/ResourceArrayPromiseTest.php +++ b/tests/Promise/ResourceArrayPromiseTest.php @@ -37,9 +37,11 @@ public function testIsPromise() public function testGetEnvelope() { $envelope = m::mock(ResourceEnvelope::class); - $inner = m::mock(ResourceArrayInterface::class) + $inner = m::spy(ResourceArrayInterface::class) ->shouldReceive('getEnvelope') ->andReturn($envelope) + ->shouldReceive('getIterator') + ->andReturn(new \ArrayIterator()) ->getMock(); $resourceArray = new ResourceArrayPromise(promise_for($inner)); $this->assertSame($envelope, $resourceArray->getEnvelope()); @@ -56,4 +58,17 @@ public function testGettersForArray() $this->assertInstanceOf(EntryInterface::class, $resourceArray->first()); $this->assertInstanceOf(AssetInterface::class, $resourceArray->last()); } + + public function testFiltersEmpty() + { + $resourceArray = new ResourceArrayPromise(promise_for([ + m::mock(EntryInterface::class), + null, + m::mock(AssetInterface::class), + ])); + $this->assertCount(2, $resourceArray); + $this->assertInstanceOf(EntryInterface::class, $resourceArray->first()); + $this->assertInstanceOf(AssetInterface::class, $resourceArray->last()); + $this->assertInstanceOf(AssetInterface::class, $resourceArray[1]); + } } diff --git a/tests/ResourceArrayTest.php b/tests/ResourceArrayTest.php index f90aebe..c037667 100644 --- a/tests/ResourceArrayTest.php +++ b/tests/ResourceArrayTest.php @@ -2,6 +2,7 @@ namespace Markup\Contentful\Tests; +use Markup\Contentful\EntryInterface; use Markup\Contentful\ResourceArray; use Markup\Contentful\ResourceArrayInterface; use Mockery as m; @@ -85,6 +86,6 @@ public function testLastWhenArrayEmptyReturnsNull() private function getMockEntry() { - return m::mock('Markup\Contentful\EntryInterface'); + return m::mock(EntryInterface::class); } }