Skip to content

Commit

Permalink
Merge pull request #539 from esign/yield-models-on-request
Browse files Browse the repository at this point in the history
Add support for generators
  • Loading branch information
stephangroen authored Feb 24, 2022
2 parents ee86848 + c18472a commit 0c39ba2
Show file tree
Hide file tree
Showing 10 changed files with 616 additions and 52 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@ index.php
storage.txt
composer.lock
.idea
.phpunit.result.cache
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,10 @@ $item->find($id);
$item = new \Picqer\Financials\Exact\Item($connection);
$item->get();

// List items as a generator
$item = new \Picqer\Financials\Exact\Item($connection);
$item->getGenerator();

// List items with filter (using a filter always returns a collection)
$item = new \Picqer\Financials\Exact\Item($connection);
$items = $item->filter("Code eq '$item->Code'"); // Uses filters as described in Exact API docs (odata filters)
Expand Down
44 changes: 32 additions & 12 deletions src/Picqer/Financials/Exact/Query/Findable.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

namespace Picqer\Financials\Exact\Query;

use Generator;
use Picqer\Financials\Exact\Connection;

trait Findable
Expand Down Expand Up @@ -83,7 +84,14 @@ public function findId($code, $key = 'Code')
}
}

public function filter($filter, $expand = '', $select = '', $system_query_options = null, array $headers = [])
public function filter($filter, $expand = '', $select = '', $system_query_options = null, array $headers = []): array
{
return iterator_to_array(
$this->filterAsGenerator($filter, $expand, $select, $system_query_options, $headers)
);
}

public function filterAsGenerator($filter, $expand = '', $select = '', $system_query_options = null, array $headers = []): Generator
{
$originalDivision = $this->connection()->getDivision();

Expand Down Expand Up @@ -114,7 +122,7 @@ public function filter($filter, $expand = '', $select = '', $system_query_option
$this->connection()->setDivision($originalDivision); // Restore division
}

return $this->collectionFromResult($result, $headers);
return $this->collectionFromResultAsGenerator($result, $headers);
}

/**
Expand Down Expand Up @@ -146,21 +154,37 @@ public function getResultSet(array $params = [])
return new Resultset($this->connection(), $this->url(), get_class($this), $params);
}

public function get(array $params = [])
public function get(array $params = []): array
{
return iterator_to_array($this->getAsGenerator($params));
}

public function getAsGenerator(array $params = []): Generator
{
$result = $this->connection()->get($this->url(), $params);

return $this->collectionFromResult($result);
return $this->collectionFromResultAsGenerator($result);
}

public function collectionFromResult($result, array $headers = []): array
{
return iterator_to_array(
$this->collectionFromResultAsGenerator($result, $headers)
);
}

public function collectionFromResult($result, array $headers = [])
public function collectionFromResultAsGenerator($result, array $headers = []): Generator
{
// If we have one result which is not an assoc array, make it the first element of an array for the
// collectionFromResult function so we always return a collection from filter
if ((bool) count(array_filter(array_keys($result), 'is_string'))) {
$result = [$result];
}

foreach ($result as $row) {
yield new static($this->connection(), $row);
}

while ($this->connection()->nextUrl !== null) {
$nextResult = $this->connection()->get($this->connection()->nextUrl, [], $headers);

Expand All @@ -169,13 +193,9 @@ public function collectionFromResult($result, array $headers = [])
$nextResult = [$nextResult];
}

$result = array_merge($result, $nextResult);
}
$collection = [];
foreach ($result as $r) {
$collection[] = new static($this->connection(), $r);
foreach ($nextResult as $row) {
yield new static($this->connection(), $row);
}
}

return $collection;
}
}
32 changes: 17 additions & 15 deletions src/Picqer/Financials/Exact/Query/Resultset.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

namespace Picqer\Financials\Exact\Query;

use Generator;
use Picqer\Financials\Exact\Connection;

/**
Expand Down Expand Up @@ -45,16 +46,18 @@ public function __construct(Connection $connection, $url, $class, array $params)
$this->params = $params;
}

/**
* @return array
*/
public function next()
public function next(): array
{
return iterator_to_array($this->nextAsGenerator());
}

public function nextAsGenerator(): Generator
{
$result = $this->connection->get($this->url, $this->params);
$this->url = $this->connection->nextUrl;
$this->params = [];

return $this->collectionFromResult($result);
return $this->collectionFromResultAsGenerator($result);
}

/**
Expand All @@ -65,12 +68,14 @@ public function hasMore()
return $this->url !== null;
}

/**
* @param array $result
*
* @return array
*/
protected function collectionFromResult($result)
protected function collectionFromResult(array $result): array
{
return iterator_to_array(
$this->collectionFromResultAsGenerator($result)
);
}

protected function collectionFromResultAsGenerator(array $result): Generator
{
// If we have one result which is not an assoc array, make it the first element of an array for the
// collectionFromResult function so we always return a collection from filter
Expand All @@ -79,12 +84,9 @@ protected function collectionFromResult($result)
}

$class = $this->class;
$collection = [];

foreach ($result as $r) {
$collection[] = new $class($this->connection, $r);
yield new $class($this->connection, $r);
}

return $collection;
}
}
120 changes: 120 additions & 0 deletions tests/ModelTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
<?php

namespace Picqer\Tests;

use Generator;
use PHPUnit\Framework\TestCase;
use Picqer\Financials\Exact\Item;
use Picqer\Financials\Exact\Query\Resultset;
use Picqer\Tests\Support\MocksExactConnection;

class ModelTest extends TestCase
{
use MocksExactConnection;

public function testCanFindModel()
{
$handler = $this->createMockHandlerUsingFixture('item.json');
$connection = $this->createMockConnection($handler);

$response = (new Item($connection))->find('00000000-0000-0000-0000-000000000000');

$this->assertInstanceOf(Item::class, $response);
$this->assertEquals('00000000-0000-0000-0000-000000000000', $response->primaryKeyContent());
}

public function testCanGetFirstModel()
{
$handler = $this->createMockHandlerUsingFixture('item.json');
$connection = $this->createMockConnection($handler);

$response = (new Item($connection))->first();

$this->assertInstanceOf(Item::class, $response);
$this->assertEquals('00000000-0000-0000-0000-000000000000', $response->primaryKeyContent());
}

public function testCanGetModels()
{
$handler = $this->createMockHandlerUsingFixture('items.json');
$connection = $this->createMockConnection($handler);

$response = (new Item($connection))->get();

$this->assertIsArray($response);
$this->assertInstanceOf(Item::class, $response[0]);
$this->assertCount(2, $response);
}

public function testCanGetModelsAsGenerator()
{
$handler = $this->createMockHandlerUsingFixture('items.json');
$connection = $this->createMockConnection($handler);

$response = (new Item($connection))->getAsGenerator();

$this->assertInstanceOf(Generator::class, $response);
$this->assertCount(2, $response);
}

public function testCanFilterModels()
{
$handler = $this->createMockHandlerUsingFixture('items.json');
$connection = $this->createMockConnection($handler);

$response = (new Item($connection))->filter('IsWebshopItem eq 0');

$this->assertIsArray($response);
$this->assertInstanceOf(Item::class, $response[0]);
$this->assertCount(2, $response);
}

public function testCanFilterModelsAsGenerator()
{
$handler = $this->createMockHandlerUsingFixture('items.json');
$connection = $this->createMockConnection($handler);

$response = (new Item($connection))->filterAsGenerator('IsWebshopItem eq 0');

$this->assertInstanceOf(Generator::class, $response);
$this->assertCount(2, $response);
}

public function testCanGetCollectionFromResult()
{
$handler = $this->createMockHandlerUsingFixture('items.json');
$connection = $this->createMockConnection($handler);
$item = new Item($connection);

$result = $connection->get($item->url(), []);
$collection = $item->collectionFromResult($result);

$this->assertIsArray($collection);
$this->assertInstanceOf(Item::class, $collection[0]);
$this->assertCount(2, $collection);
}

public function testCanGetCollectionFromResultAsGenerator()
{
$handler = $this->createMockHandlerUsingFixture('items.json');
$connection = $this->createMockConnection($handler);
$item = new Item($connection);

$result = $connection->get($item->url(), []);
$collection = $item->collectionFromResultAsGenerator($result);

$this->assertInstanceOf(Generator::class, $collection);
$this->assertCount(2, $collection);
}

public function testCanGetResultSet()
{
$handler = $this->createMockHandler();
$connection = $this->createMockConnection($handler);
$item = new Item($connection);

$resultset = (new Item($connection))->getResultSet();

$this->assertInstanceOf(Resultset::class, $resultset);
}
}
29 changes: 4 additions & 25 deletions tests/QueryParamTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,22 +4,21 @@

namespace Picqer\Tests;

use GuzzleHttp\Client;
use GuzzleHttp\Handler\MockHandler;
use GuzzleHttp\HandlerStack;
use GuzzleHttp\Psr7\Response;
use PHPUnit\Framework\TestCase;
use Picqer\Financials\Exact;
use Picqer\Financials\Exact\Connection;
use Picqer\Tests\Support\MocksExactConnection;

class QueryParamTest extends TestCase
{
use MocksExactConnection;

/**
* @dataProvider ModelsWithSupportQueryParams
*/
public function testGetRequestHasCorrectlyFormattedQueryParam(string $classString, array $supportedParams): void
{
$mockHandler = $this->createMockHandler();
$mockHandler = $this->createMockHandler(new Response(200, [], json_encode((object) [])));
$connection = $this->createMockConnection($mockHandler);
$sut = new $classString($connection);
$params = array_fill_keys($supportedParams, '00000000-0000-0000-0000-000000000000');
Expand All @@ -29,26 +28,6 @@ public function testGetRequestHasCorrectlyFormattedQueryParam(string $classStrin
$this->assertEquals(http_build_query($params), $mockHandler->getLastRequest()->getUri()->getQuery());
}

private function createMockConnection(callable $mockHandler): Connection
{
$handlerStack = HandlerStack::create($mockHandler);
$client = new Client(['handler' => $handlerStack]);
$connection = new Connection();
$connection->setClient($client);
$connection->setDivision('1234567890');
$connection->setAccessToken('1234567890');
$connection->setTokenExpires(time() + 60);

return $connection;
}

private function createMockHandler(): MockHandler
{
return new MockHandler([
new Response(200, [], json_encode((object) [])),
]);
}

public function ModelsWithSupportQueryParams(): array
{
return [
Expand Down
46 changes: 46 additions & 0 deletions tests/ResultsetTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
<?php

namespace Picqer\Tests;

use PHPUnit\Framework\TestCase;
use Picqer\Financials\Exact\Item;
use Picqer\Financials\Exact\Query\Resultset;
use Picqer\Tests\Support\MocksExactConnection;

class ResultsetTest extends TestCase
{
use MocksExactConnection;

public function testCanGetNext()
{
$handler = $this->createMockHandlerUsingFixture('items.json');
$connection = $this->createMockConnection($handler);

$response = (new Resultset(
$connection,
'logistics/Items',
Item::class,
[]
))->next();

$this->assertIsArray($response);
$this->assertInstanceOf(Item::class, $response[0]);
$this->assertCount(2, $response);
}

public function testCanGetNextAsGenerator()
{
$handler = $this->createMockHandlerUsingFixture('items.json');
$connection = $this->createMockConnection($handler);

$response = (new Resultset(
$connection,
'logistics/Items',
Item::class,
[]
))->nextAsGenerator();

$this->assertIsIterable($response);
$this->assertCount(2, $response);
}
}
Loading

0 comments on commit 0c39ba2

Please sign in to comment.