Skip to content

Commit

Permalink
feat: add createCriteriaPageable DX method (#79)
Browse files Browse the repository at this point in the history
  • Loading branch information
priyadi authored Jun 29, 2024
1 parent 7f09f3e commit a083c75
Show file tree
Hide file tree
Showing 5 changed files with 213 additions and 19 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
* refactor: refresh count
* fix: fix various problems with criteria recollections
* feat: add several DX methods
* feat: add `createCriteriaPageable` DX method

## 0.4.0

Expand Down
161 changes: 161 additions & 0 deletions packages/collections-domain/src/CriteriaPageable.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
<?php

declare(strict_types=1);

/*
* This file is part of rekalogika/collections package.
*
* (c) Priyadi Iman Nurcahyo <https://rekalogika.dev>
*
* For the full copyright and license information, please view the LICENSE file
* that was distributed with this source code.
*/

namespace Rekalogika\Domain\Collections;

use Doctrine\Common\Collections\Criteria;
use Doctrine\Common\Collections\Order;
use Doctrine\Common\Collections\ReadableCollection;
use Doctrine\Common\Collections\Selectable;
use Rekalogika\Contracts\Collections\Exception\UnexpectedValueException;
use Rekalogika\Contracts\Rekapager\PageableInterface;
use Rekalogika\Domain\Collections\Common\Count\CountStrategy;
use Rekalogika\Domain\Collections\Common\Count\RestrictedCountStrategy;
use Rekalogika\Domain\Collections\Common\Trait\PageableTrait;
use Rekalogika\Domain\Collections\Trait\RecollectionPageableTrait;

/**
* @template TKey of array-key
* @template T
* @implements PageableInterface<TKey,T>
*/
class CriteriaPageable implements PageableInterface
{
/** @use RecollectionPageableTrait<TKey,T> */
use RecollectionPageableTrait;

/** @use PageableTrait<TKey,T> */
use PageableTrait;

/**
* @var null|\WeakMap<object,array<string,self<array-key,mixed>>>
*/
private static ?\WeakMap $instances = null;

/**
* @var Selectable<TKey,T>
*/
private readonly Selectable $collection;

private readonly Criteria $criteria;
private readonly CountStrategy $count;

/**
* @param ReadableCollection<TKey,T>|Selectable<TKey,T> $collection
* @param int<1,max> $itemsPerPage
*/
final private function __construct(
ReadableCollection|Selectable $collection,
?Criteria $criteria = null,
private readonly ?string $indexBy = null,
private readonly int $itemsPerPage = 50,
?CountStrategy $count = null,
) {
// save collection

if (!$collection instanceof Selectable) {
throw new UnexpectedValueException('The wrapped collection must implement the Selectable interface.');
}

$this->collection = $collection;

// save criteria

$criteria = clone ($criteria ?? Criteria::create());

if (\count($criteria->orderings()) === 0) {
$criteria->orderBy(['id' => Order::Descending]);
}

$this->criteria = $criteria;

// save count strategy

$this->count = $count ?? new RestrictedCountStrategy();
}

/**
* @template STKey of array-key
* @template ST
* @param ReadableCollection<STKey,ST>|Selectable<STKey,ST> $collection
* @param int<1,max> $itemsPerPage
* @return static
*/
final public static function create(
ReadableCollection|Selectable $collection,
?Criteria $criteria = null,
?string $instanceId = null,
?string $indexBy = null,
int $itemsPerPage = 50,
?CountStrategy $count = null,
): PageableInterface {
if (self::$instances === null) {
/** @var \WeakMap<object,array<string,self<array-key,mixed>>> */
$weakmap = new \WeakMap();
// @phpstan-ignore-next-line
self::$instances = $weakmap;
}

$cacheKey = hash('xxh128', serialize([
$instanceId ?? $criteria,
$indexBy,
$itemsPerPage,
]));

if (isset(self::$instances[$collection][$cacheKey])) {
/** @var static */
return self::$instances[$collection][$cacheKey];
}

/** @psalm-suppress UnsafeGenericInstantiation */
$newInstance = new static(
collection: $collection,
criteria: $criteria,
indexBy: $indexBy,
itemsPerPage: $itemsPerPage,
count: $count,
);

if (!isset(self::$instances[$collection])) {
// @phpstan-ignore-next-line
self::$instances[$collection] = [];
}

/**
* @psalm-suppress InvalidArgument
* @phpstan-ignore-next-line
*/
self::$instances[$collection][$cacheKey] = $newInstance;

/** @var static */
return $newInstance;
}

/**
* @param int<1,max> $itemsPerPage
*/
public function withItemsPerPage(int $itemsPerPage): static
{
return self::create(
collection: $this->collection,
criteria: $this->criteria,
itemsPerPage: $itemsPerPage,
count: $this->count,
);
}

private function getUnderlyingCountable(): \Countable
{
return $this->collection->matching($this->criteria);
}
}
24 changes: 24 additions & 0 deletions packages/collections-domain/src/MinimalRecollectionDecorator.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
use Doctrine\Common\Collections\Selectable;
use Rekalogika\Contracts\Collections\Exception\UnexpectedValueException;
use Rekalogika\Contracts\Collections\MinimalRecollection;
use Rekalogika\Contracts\Rekapager\PageableInterface;
use Rekalogika\Domain\Collections\Common\Configuration;
use Rekalogika\Domain\Collections\Common\Count\CountStrategy;
use Rekalogika\Domain\Collections\Common\Count\RestrictedCountStrategy;
Expand Down Expand Up @@ -215,4 +216,27 @@ final protected function createCriteriaCollection(
count: $count,
);
}

/**
* @return PageableInterface<TKey,T>
*/
final protected function createCriteriaPageable(
Criteria $criteria,
?string $instanceId = null,
?CountStrategy $count = null,
): PageableInterface {
// if $criteria has no orderings, add the current ordering
if (\count($criteria->orderings()) === 0) {
$criteria = $criteria->orderBy($this->orderBy);
}

/** @var PageableInterface<TKey,T> */
return CriteriaPageable::create(
collection: $this->collection,
criteria: $criteria,
instanceId: $instanceId,
itemsPerPage: $this->itemsPerPage,
count: $count,
);
}
}
24 changes: 24 additions & 0 deletions packages/collections-domain/src/RecollectionDecorator.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
use Doctrine\Common\Collections\Selectable;
use Rekalogika\Contracts\Collections\Exception\UnexpectedValueException;
use Rekalogika\Contracts\Collections\Recollection;
use Rekalogika\Contracts\Rekapager\PageableInterface;
use Rekalogika\Domain\Collections\Common\Configuration;
use Rekalogika\Domain\Collections\Common\Count\CountStrategy;
use Rekalogika\Domain\Collections\Common\Count\RestrictedCountStrategy;
Expand Down Expand Up @@ -259,4 +260,27 @@ final protected function createCriteriaCollection(
hardLimit: $this->hardLimit,
);
}

/**
* @return PageableInterface<TKey,T>
*/
final protected function createCriteriaPageable(
Criteria $criteria,
?string $instanceId = null,
?CountStrategy $count = null,
): PageableInterface {
// if $criteria has no orderings, add the current ordering
if (\count($criteria->orderings()) === 0) {
$criteria = $criteria->orderBy($this->orderBy);
}

/** @var PageableInterface<TKey,T> */
return CriteriaPageable::create(
collection: $this->collection,
criteria: $criteria,
instanceId: $instanceId,
itemsPerPage: $this->itemsPerPage,
count: $count,
);
}
}
22 changes: 3 additions & 19 deletions packages/collections-orm/src/Trait/RepositoryDxTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,8 @@
use Doctrine\Persistence\ObjectRepository;
use Rekalogika\Contracts\Rekapager\PageableInterface;
use Rekalogika\Domain\Collections\Common\Count\CountStrategy;
use Rekalogika\Domain\Collections\Common\Exception\GettingCountUnsupportedException;
use Rekalogika\Domain\Collections\CriteriaPageable;
use Rekalogika\Domain\Collections\CriteriaRecollection;
use Rekalogika\Rekapager\Doctrine\Collections\SelectableAdapter;
use Rekalogika\Rekapager\Keyset\KeysetPageable;

/**
* @template TKey of array-key
Expand Down Expand Up @@ -93,26 +91,12 @@ final protected function createCriteriaPageable(
?string $indexBy = null,
?CountStrategy $count = null,
): PageableInterface {
$adapter = new SelectableAdapter(
return CriteriaPageable::create(
collection: $this->getDoctrineRepository(),
criteria: $criteria,
indexBy: $indexBy ?? $this->indexBy
);

$count = function (): int|bool {
try {
return $this->count->getCount($this->getUnderlyingCountable());
} catch (GettingCountUnsupportedException) {
return false;
}
};

$pageable = new KeysetPageable(
adapter: $adapter,
indexBy: $indexBy ?? $this->indexBy,
itemsPerPage: $this->itemsPerPage,
count: $count,
);

return $pageable;
}
}

0 comments on commit a083c75

Please sign in to comment.