From 8b7c5b74d7b2bf5e93050a6b9a1f7e697ce9c50e Mon Sep 17 00:00:00 2001 From: Joshua Estes Date: Tue, 21 Nov 2023 12:08:19 -0500 Subject: [PATCH 1/5] [Pager] Doctrine Bridge --- .../Tests/ArrayCollectionAdapterTest.php | 48 ++++++++++++ .../DBAL/Pager/QueryBuilderAdapter.php | 31 +++++++- .../Pager/Tests/QueryBuilderAdapterTest.php | 78 +++++++++++++++++++ 3 files changed, 153 insertions(+), 4 deletions(-) create mode 100644 src/SonsOfPHP/Bridge/Doctrine/Collections/Pager/Tests/ArrayCollectionAdapterTest.php create mode 100644 src/SonsOfPHP/Bridge/Doctrine/DBAL/Pager/Tests/QueryBuilderAdapterTest.php diff --git a/src/SonsOfPHP/Bridge/Doctrine/Collections/Pager/Tests/ArrayCollectionAdapterTest.php b/src/SonsOfPHP/Bridge/Doctrine/Collections/Pager/Tests/ArrayCollectionAdapterTest.php new file mode 100644 index 00000000..81dc9d45 --- /dev/null +++ b/src/SonsOfPHP/Bridge/Doctrine/Collections/Pager/Tests/ArrayCollectionAdapterTest.php @@ -0,0 +1,48 @@ +assertInstanceOf(AdapterInterface::class, $adapter); + } + + /** + * @covers ::count + */ + public function testCount(): void + { + $adapter = new ArrayCollectionAdapter(new ArrayCollection(range(0, 9))); + + $this->assertCount(10, $adapter); + } + + /** + * @covers ::getSlice + */ + public function testGetSlice(): void + { + $adapter = new ArrayCollectionAdapter(new ArrayCollection(range(0, 9))); + + $this->assertSame([0], $adapter->getSlice(0, 1)); + } +} diff --git a/src/SonsOfPHP/Bridge/Doctrine/DBAL/Pager/QueryBuilderAdapter.php b/src/SonsOfPHP/Bridge/Doctrine/DBAL/Pager/QueryBuilderAdapter.php index 66ecfd23..7f7eefbb 100644 --- a/src/SonsOfPHP/Bridge/Doctrine/DBAL/Pager/QueryBuilderAdapter.php +++ b/src/SonsOfPHP/Bridge/Doctrine/DBAL/Pager/QueryBuilderAdapter.php @@ -8,20 +8,36 @@ use SonsOfPHP\Contract\Pager\AdapterInterface; /** + * Usage: + * $adapter = new QueryBuilderAdapter($queryBuilder, function (QueryBuilder $builder): void { + * $builder->select('COUNT(DISTINCT e.id) AS cnt'); + * }); + * * @author Joshua Estes */ class QueryBuilderAdapter implements AdapterInterface { + private $countQuery; + public function __construct( - private QueryBuilder $builder, - ) {} + private readonly QueryBuilder $builder, + callable $countQuery, + ) { + $this->countQuery = $countQuery; + } /** * {@inheritdoc} */ public function count(): int { - throw new \Exception('@todo'); + $builder = clone $this->builder; + $callable = $this->countQuery; + + $callable($builder); + $builder->setMaxResults(1); + + return (int) $builder->executeQuery()->fetchOne(); } /** @@ -29,6 +45,13 @@ public function count(): int */ public function getSlice(int $offset, ?int $length): iterable { - throw new \Exception('@todo'); + $builder = clone $this->builder; + + return $builder + ->setFirstResult($offset) + ->setMaxResults($length) + ->executeQuery() + ->fetchAllAssociative() + ; } } diff --git a/src/SonsOfPHP/Bridge/Doctrine/DBAL/Pager/Tests/QueryBuilderAdapterTest.php b/src/SonsOfPHP/Bridge/Doctrine/DBAL/Pager/Tests/QueryBuilderAdapterTest.php new file mode 100644 index 00000000..d03065c4 --- /dev/null +++ b/src/SonsOfPHP/Bridge/Doctrine/DBAL/Pager/Tests/QueryBuilderAdapterTest.php @@ -0,0 +1,78 @@ +builder = $this->createMock(QueryBuilder::class); + $this->result = $this->createMock(Result::class); + } + + /** + * @covers ::__construct + */ + public function testItHasTheRightInterface(): void + { + $adapter = new QueryBuilderAdapter($this->builder, function (QueryBuilder $builder): void {}); + + $this->assertInstanceOf(AdapterInterface::class, $adapter); + } + + /** + * @covers ::count + */ + public function testCount(): void + { + $this->builder->method('executeQuery')->willReturn($this->result); + $this->builder->expects($this->once())->method('select'); + + $this->result->method('fetchOne')->willReturn(123); + + $adapter = new QueryBuilderAdapter($this->builder, function (QueryBuilder $builder): void { + $builder->select('COUNT()'); + }); + + $this->assertCount(123, $adapter); + } + + /** + * @covers ::getSlice + */ + public function testSlice(): void + { + $this->builder + ->expects($this->once()) + ->method('setFirstResult') + ->with($this->identicalTo(0)) + ->willReturn($this->builder) + ; + $this->builder + ->expects($this->once()) + ->method('setMaxResults') + ->with($this->identicalTo(100)) + ->willReturn($this->builder) + ; + + $adapter = new QueryBuilderAdapter($this->builder, function (QueryBuilder $builder): void {}); + + $adapter->getSlice(0, 100); + } +} From 53fd5887b174e014e980cfe4372a4f899c1a147c Mon Sep 17 00:00:00 2001 From: Joshua Estes Date: Tue, 21 Nov 2023 12:25:34 -0500 Subject: [PATCH 2/5] docs --- docs/components/pager/index.md | 34 ++++++++++++++++--- docs/contracts/pager/index.md | 34 +++++++++++++++++++ .../Contract/Pager/AdapterInterface.php | 2 +- 3 files changed, 65 insertions(+), 5 deletions(-) diff --git a/docs/components/pager/index.md b/docs/components/pager/index.md index c23393aa..0c37775f 100644 --- a/docs/components/pager/index.md +++ b/docs/components/pager/index.md @@ -66,7 +66,14 @@ if ($pager->hasNextPage()) { } ``` -## Adapters +## Custom Adapters + +Creating Custom Adapters is easy. You can take a look at the available adapters +to see how easy it is. + +Please see the [Pager Contract](../../contracts/pager/index.md) to learn more. + +## Available Adapters ### ArrayAdapter @@ -97,10 +104,9 @@ $adapter = new CallableAdapter( ); ``` -### ArrayCollectionAdapter - +### ArrayCollectionAdapter (doctrine/collections) -!!! warning +!!! warning "Requires `sonsofphp/pager-doctrine-collections`" ```shell composer require sonsofphp/pager-doctrine-collections ``` @@ -115,3 +121,23 @@ $collection = new ArrayCollection(); $adapter = new ArrayCollectionAdapter($collection); ``` + +### QueryBuilderAdapter (doctrine/dbal) + +!!! warning "Requires `sonsofphp/pager-doctrine-dbal`" + ```shell + composer require sonsofphp/pager-doctrine-dbal + ``` + +```php +select('COUNT(e.id) as total'); +}); +``` diff --git a/docs/contracts/pager/index.md b/docs/contracts/pager/index.md index 550b6de1..d2333a30 100644 --- a/docs/contracts/pager/index.md +++ b/docs/contracts/pager/index.md @@ -10,3 +10,37 @@ components. ```shell composer require sonsofphp/pager-contract ``` + +## AdapterInterface + +```php + Date: Tue, 21 Nov 2023 12:41:17 -0500 Subject: [PATCH 3/5] updates --- .../ORM/Pager/QueryBuilderAdapter.php | 45 +++++++++++++++++++ .../Pager/Tests/QueryBuilderAdapterTest.php | 35 +++++++++++++++ 2 files changed, 80 insertions(+) create mode 100644 src/SonsOfPHP/Bridge/Doctrine/ORM/Pager/QueryBuilderAdapter.php create mode 100644 src/SonsOfPHP/Bridge/Doctrine/ORM/Pager/Tests/QueryBuilderAdapterTest.php diff --git a/src/SonsOfPHP/Bridge/Doctrine/ORM/Pager/QueryBuilderAdapter.php b/src/SonsOfPHP/Bridge/Doctrine/ORM/Pager/QueryBuilderAdapter.php new file mode 100644 index 00000000..1abafc11 --- /dev/null +++ b/src/SonsOfPHP/Bridge/Doctrine/ORM/Pager/QueryBuilderAdapter.php @@ -0,0 +1,45 @@ + + */ +class QueryBuilderAdapter implements AdapterInterface +{ + private readonly Paginator $paginator; + + public function __construct( + QueryBuilder $builder, + ) { + $this->paginator = new Paginator($builder, fetchJoinCollection: true); + } + + /** + * {@inheritdoc} + */ + public function count(): int + { + return count($this->paginator); + } + + /** + * {@inheritdoc} + */ + public function getSlice(int $offset, ?int $length): iterable + { + $query = $this->paginator->getQuery(); + $query + ->setFirstResult($offset) + ->setMaxResults($length) + ; + + return $this->paginator->getIterator(); + } +} diff --git a/src/SonsOfPHP/Bridge/Doctrine/ORM/Pager/Tests/QueryBuilderAdapterTest.php b/src/SonsOfPHP/Bridge/Doctrine/ORM/Pager/Tests/QueryBuilderAdapterTest.php new file mode 100644 index 00000000..19441717 --- /dev/null +++ b/src/SonsOfPHP/Bridge/Doctrine/ORM/Pager/Tests/QueryBuilderAdapterTest.php @@ -0,0 +1,35 @@ +builder = $this->createMock(QueryBuilder::class); + } + + /** + * @covers ::__construct + */ + public function testItHasTheRightInterface(): void + { + $adapter = new QueryBuilderAdapter($this->builder); + + $this->assertInstanceOf(AdapterInterface::class, $adapter); + } +} From 24856990ed971da5d458008e431c157f2ab3ffa7 Mon Sep 17 00:00:00 2001 From: Joshua Estes Date: Tue, 21 Nov 2023 12:45:51 -0500 Subject: [PATCH 4/5] docs --- docs/components/pager/index.md | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/docs/components/pager/index.md b/docs/components/pager/index.md index 0c37775f..c9a23af6 100644 --- a/docs/components/pager/index.md +++ b/docs/components/pager/index.md @@ -141,3 +141,21 @@ $adapter = new QueryBuilderAdapter($builder, function (QueryBuilder $builder): v $builder->select('COUNT(e.id) as total'); }); ``` + +### QueryBuilderAdapter (doctrine/orm) + +!!! warning "Requires `sonsofphp/pager-doctrine-orm`" + ```shell + composer require sonsofphp/pager-doctrine-orm + ``` + +```php +createQueryBuilder('e'); + +$adapter = new QueryBuilderAdapter($builder); +``` From 697f7d79786442bd6856eee5c820e25932c86db9 Mon Sep 17 00:00:00 2001 From: Joshua Estes Date: Tue, 21 Nov 2023 12:48:22 -0500 Subject: [PATCH 5/5] cs fixes --- .../Collections/Pager/Tests/ArrayCollectionAdapterTest.php | 2 +- .../Doctrine/DBAL/Pager/Tests/QueryBuilderAdapterTest.php | 4 ++-- .../Bridge/Doctrine/ORM/Pager/QueryBuilderAdapter.php | 2 +- .../Doctrine/ORM/Pager/Tests/QueryBuilderAdapterTest.php | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/SonsOfPHP/Bridge/Doctrine/Collections/Pager/Tests/ArrayCollectionAdapterTest.php b/src/SonsOfPHP/Bridge/Doctrine/Collections/Pager/Tests/ArrayCollectionAdapterTest.php index 81dc9d45..ede1c857 100644 --- a/src/SonsOfPHP/Bridge/Doctrine/Collections/Pager/Tests/ArrayCollectionAdapterTest.php +++ b/src/SonsOfPHP/Bridge/Doctrine/Collections/Pager/Tests/ArrayCollectionAdapterTest.php @@ -5,9 +5,9 @@ namespace SonsOfPHP\Bridge\Doctrine\Collections\Pager\Tests; use Doctrine\Common\Collections\ArrayCollection; -use SonsOfPHP\Contract\Pager\AdapterInterface; use PHPUnit\Framework\TestCase; use SonsOfPHP\Bridge\Doctrine\Collections\Pager\ArrayCollectionAdapter; +use SonsOfPHP\Contract\Pager\AdapterInterface; /** * @coversDefaultClass \SonsOfPHP\Bridge\Doctrine\Collections\Pager\ArrayCollectionAdapter diff --git a/src/SonsOfPHP/Bridge/Doctrine/DBAL/Pager/Tests/QueryBuilderAdapterTest.php b/src/SonsOfPHP/Bridge/Doctrine/DBAL/Pager/Tests/QueryBuilderAdapterTest.php index d03065c4..fb2050d0 100644 --- a/src/SonsOfPHP/Bridge/Doctrine/DBAL/Pager/Tests/QueryBuilderAdapterTest.php +++ b/src/SonsOfPHP/Bridge/Doctrine/DBAL/Pager/Tests/QueryBuilderAdapterTest.php @@ -5,10 +5,10 @@ namespace SonsOfPHP\Bridge\Doctrine\DBAL\Pager\Tests; use Doctrine\DBAL\Query\QueryBuilder; -use SonsOfPHP\Contract\Pager\AdapterInterface; +use Doctrine\DBAL\Result; use PHPUnit\Framework\TestCase; use SonsOfPHP\Bridge\Doctrine\DBAL\Pager\QueryBuilderAdapter; -use Doctrine\DBAL\Result; +use SonsOfPHP\Contract\Pager\AdapterInterface; /** * @coversDefaultClass \SonsOfPHP\Bridge\Doctrine\DBAL\Pager\QueryBuilderAdapter diff --git a/src/SonsOfPHP/Bridge/Doctrine/ORM/Pager/QueryBuilderAdapter.php b/src/SonsOfPHP/Bridge/Doctrine/ORM/Pager/QueryBuilderAdapter.php index 1abafc11..2f7cb6e6 100644 --- a/src/SonsOfPHP/Bridge/Doctrine/ORM/Pager/QueryBuilderAdapter.php +++ b/src/SonsOfPHP/Bridge/Doctrine/ORM/Pager/QueryBuilderAdapter.php @@ -5,8 +5,8 @@ namespace SonsOfPHP\Bridge\Doctrine\ORM\Pager; use Doctrine\ORM\QueryBuilder; -use SonsOfPHP\Contract\Pager\AdapterInterface; use Doctrine\ORM\Tools\Pagination\Paginator; +use SonsOfPHP\Contract\Pager\AdapterInterface; /** * @author Joshua Estes diff --git a/src/SonsOfPHP/Bridge/Doctrine/ORM/Pager/Tests/QueryBuilderAdapterTest.php b/src/SonsOfPHP/Bridge/Doctrine/ORM/Pager/Tests/QueryBuilderAdapterTest.php index 19441717..8536ffec 100644 --- a/src/SonsOfPHP/Bridge/Doctrine/ORM/Pager/Tests/QueryBuilderAdapterTest.php +++ b/src/SonsOfPHP/Bridge/Doctrine/ORM/Pager/Tests/QueryBuilderAdapterTest.php @@ -5,9 +5,9 @@ namespace SonsOfPHP\Bridge\Doctrine\ORM\Pager\Tests; use Doctrine\ORM\QueryBuilder; -use SonsOfPHP\Contract\Pager\AdapterInterface; use PHPUnit\Framework\TestCase; use SonsOfPHP\Bridge\Doctrine\ORM\Pager\QueryBuilderAdapter; +use SonsOfPHP\Contract\Pager\AdapterInterface; /** * @coversDefaultClass \SonsOfPHP\Bridge\Doctrine\ORM\Pager\QueryBuilderAdapter