Skip to content

Commit

Permalink
refactor: refactor counting, change default strategy to `ConditionalD…
Browse files Browse the repository at this point in the history
…elegatedCountStrategy` (#90)
  • Loading branch information
priyadi authored Jul 2, 2024
1 parent 0e4af49 commit 2942f8b
Show file tree
Hide file tree
Showing 17 changed files with 128 additions and 68 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
* refactor: remove `RefreshableCount` interface
* refactor: `MinimalReadableRecollection` now extends `Countable`
* refactor: cleanup count traits
* refactor: refactor counting, change default strategy to
`ConditionalDelegatedCountStrategy`

## 0.6.2

Expand Down
26 changes: 26 additions & 0 deletions packages/collections-common/src/Configuration.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
namespace Rekalogika\Domain\Collections\Common;

use Doctrine\Common\Collections\Order;
use Rekalogika\Domain\Collections\Common\Count\ConditionalDelegatedCountStrategy;
use Rekalogika\Domain\Collections\Common\Count\CountStrategy;

final class Configuration
{
Expand All @@ -39,4 +41,28 @@ final class Configuration
* @var non-empty-array<string,Order>
*/
public static array $defaultOrderBy = ['id' => Order::Descending];

/**
* @var null|\Closure(): CountStrategy
*/
private static ?\Closure $defaultCountStrategy = null;

/**
* @param \Closure(): CountStrategy $defaultCountStrategy
*/
public static function setDefaultCountStrategy(\Closure $defaultCountStrategy): void
{
self::$defaultCountStrategy = $defaultCountStrategy;
}

public static function getDefaultCountStrategy(): CountStrategy
{
if (self::$defaultCountStrategy === null) {
$countStrategy = fn (): CountStrategy => new ConditionalDelegatedCountStrategy();
} else {
$countStrategy = self::$defaultCountStrategy;
}

return $countStrategy();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
<?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\Common\Count;

use Rekalogika\Domain\Collections\Common\Exception\GettingCountUnsupportedException;
use Rekalogika\Domain\Collections\Common\Exception\SettingCountUnsupportedException;

class ConditionalDelegatedCountStrategy implements CountStrategy
{
public function __construct(
private ?int $softLimit = 5000,
private ?int $hardLimit = null,
private ?float $durationThreshold = 1,
) {
}

public function getCount(?\Countable $underlyingObject): int
{
if ($underlyingObject === null) {
throw new GettingCountUnsupportedException('The underlying object is not provided');
}

$start = microtime(true);
$result = \count($underlyingObject);
$duration = microtime(true) - $start;

if ($this->hardLimit !== null && $result > $this->hardLimit) {
throw new GettingCountUnsupportedException(sprintf('The count exceeds the threshold of %d. You should refactor and use other counting strategy. Count duration: %d s', $this->hardLimit, $duration));
} elseif ($this->softLimit !== null && $result > $this->softLimit) {
@trigger_error(sprintf('The count exceeds the warning threshold of %d. As it might impact performance, you should refactor and use other counting strategy. Count duration: %d s.', $this->softLimit, $duration), \E_USER_DEPRECATED);
} elseif ($duration > $this->durationThreshold) {
@trigger_error(sprintf('The count duration is %d s. You should consider refactoring and using other counting strategy.', $duration), \E_USER_DEPRECATED);
}

return $result;
}

public function setCount(?\Countable $underlyingObject, int $count): void
{
throw new SettingCountUnsupportedException('Setting count is disabled');
}
}
11 changes: 8 additions & 3 deletions packages/collections-common/src/Trait/CountableTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,20 +13,23 @@

namespace Rekalogika\Domain\Collections\Common\Trait;

use Rekalogika\Domain\Collections\Common\Configuration;
use Rekalogika\Domain\Collections\Common\Count\CountStrategy;
use Rekalogika\Domain\Collections\Common\Exception\InvalidCountException;

trait CountableTrait
{
abstract private function getCountStrategy(): CountStrategy;
abstract private function getCountStrategy(): ?CountStrategy;
abstract private function getUnderlyingCountable(): ?\Countable;

/**
* @return int<0,max>
*/
final public function count(): int
{
$result = $this->getCountStrategy()->getCount($this->getUnderlyingCountable());
$countStrategy = $this->getCountStrategy() ?? Configuration::getDefaultCountStrategy();

$result = $countStrategy->getCount($this->getUnderlyingCountable());

if ($result >= 0) {
return $result;
Expand All @@ -37,7 +40,9 @@ final public function count(): int

final public function refreshCount(): void
{
$countStrategy = $this->getCountStrategy() ?? Configuration::getDefaultCountStrategy();

$realCount = \count($this->getUnderlyingCountable());
$this->getCountStrategy()->setCount($this->getUnderlyingCountable(), $realCount);
$countStrategy->setCount($this->getUnderlyingCountable(), $realCount);
}
}
18 changes: 10 additions & 8 deletions packages/collections-domain/src/CriteriaPageable.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
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\CountableTrait;
use Rekalogika\Domain\Collections\Common\Trait\PageableTrait;
use Rekalogika\Domain\Collections\Trait\RecollectionPageableTrait;

Expand All @@ -29,14 +29,16 @@
* @template T
* @implements PageableInterface<TKey,T>
*/
class CriteriaPageable implements PageableInterface
class CriteriaPageable implements PageableInterface, \Countable
{
/** @use RecollectionPageableTrait<TKey,T> */
use RecollectionPageableTrait;

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

use CountableTrait;

/**
* @var null|\WeakMap<object,array<string,self<array-key,mixed>>>
*/
Expand All @@ -48,7 +50,6 @@ class CriteriaPageable implements PageableInterface
private readonly Selectable $collection;

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

/**
* @param ReadableCollection<TKey,T>|Selectable<TKey,T> $collection
Expand All @@ -59,7 +60,7 @@ final private function __construct(
?Criteria $criteria = null,
private readonly ?string $indexBy = null,
private readonly int $itemsPerPage = 50,
?CountStrategy $count = null,
private readonly ?CountStrategy $count = null,
) {
// save collection

Expand All @@ -78,10 +79,6 @@ final private function __construct(
}

$this->criteria = $criteria;

// save count strategy

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

/**
Expand Down Expand Up @@ -158,4 +155,9 @@ private function getUnderlyingCountable(): \Countable
{
return $this->collection->matching($this->criteria);
}

private function getCountStrategy(): ?CountStrategy
{
return $this->count;
}
}
10 changes: 2 additions & 8 deletions packages/collections-domain/src/CriteriaRecollection.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@
use Rekalogika\Contracts\Collections\Exception\UnexpectedValueException;
use Rekalogika\Contracts\Collections\ReadableRecollection;
use Rekalogika\Domain\Collections\Common\Count\CountStrategy;
use Rekalogika\Domain\Collections\Common\Count\RestrictedCountStrategy;
use Rekalogika\Domain\Collections\Common\Trait\ReadableRecollectionTrait;
use Rekalogika\Domain\Collections\Common\Trait\SafeCollectionTrait;
use Rekalogika\Domain\Collections\Trait\CriteriaReadableTrait;
Expand Down Expand Up @@ -65,7 +64,6 @@ class CriteriaRecollection implements ReadableRecollection
private readonly Selectable $collection;

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

/**
* @param ReadableCollection<TKey,T>|Selectable<TKey,T> $collection
Expand All @@ -78,7 +76,7 @@ final private function __construct(
?Criteria $criteria = null,
private readonly ?string $indexBy = null,
private readonly int $itemsPerPage = 50,
?CountStrategy $count = null,
private readonly ?CountStrategy $count = null,
private readonly ?int $softLimit = null,
private readonly ?int $hardLimit = null,
) {
Expand All @@ -99,10 +97,6 @@ final private function __construct(
}

$this->criteria = $criteria;

// save count strategy

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

/**
Expand Down Expand Up @@ -168,7 +162,7 @@ final public static function create(
return $newInstance;
}

private function getCountStrategy(): CountStrategy
private function getCountStrategy(): ?CountStrategy
{
return $this->count;
}
Expand Down
10 changes: 2 additions & 8 deletions packages/collections-domain/src/MinimalCriteriaRecollection.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@
use Rekalogika\Contracts\Collections\Exception\UnexpectedValueException;
use Rekalogika\Contracts\Collections\MinimalReadableRecollection;
use Rekalogika\Domain\Collections\Common\Count\CountStrategy;
use Rekalogika\Domain\Collections\Common\Count\RestrictedCountStrategy;
use Rekalogika\Domain\Collections\Common\Trait\MinimalReadableRecollectionTrait;
use Rekalogika\Domain\Collections\Common\Trait\SafeCollectionTrait;
use Rekalogika\Domain\Collections\Trait\RecollectionPageableTrait;
Expand Down Expand Up @@ -52,7 +51,6 @@ class MinimalCriteriaRecollection implements MinimalReadableRecollection, \Count
private readonly Selectable $collection;

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

/**
* @param ReadableCollection<TKey,T>|Selectable<TKey,T> $collection
Expand All @@ -63,7 +61,7 @@ final private function __construct(
?Criteria $criteria = null,
private readonly ?string $indexBy = null,
private readonly int $itemsPerPage = 50,
?CountStrategy $count = null,
private readonly ?CountStrategy $count = null,
) {
// save collection

Expand All @@ -82,10 +80,6 @@ final private function __construct(
}

$this->criteria = $criteria;

// save count strategy

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

/**
Expand Down Expand Up @@ -163,7 +157,7 @@ final public static function create(
return $newInstance;
}

private function getCountStrategy(): CountStrategy
private function getCountStrategy(): ?CountStrategy
{
return $this->count;
}
Expand Down
10 changes: 2 additions & 8 deletions packages/collections-domain/src/MinimalRecollectionDecorator.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@
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;
use Rekalogika\Domain\Collections\Common\Internal\OrderByUtil;
use Rekalogika\Domain\Collections\Common\Trait\MinimalRecollectionTrait;
use Rekalogika\Domain\Collections\Trait\RecollectionPageableTrait;
Expand Down Expand Up @@ -56,7 +55,6 @@ class MinimalRecollectionDecorator implements MinimalRecollection
private readonly array $orderBy;

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

/**
* @param Collection<TKey,T> $collection
Expand All @@ -68,7 +66,7 @@ final private function __construct(
array|string|null $orderBy = null,
private readonly ?string $indexBy = null,
private readonly int $itemsPerPage = 50,
?CountStrategy $count = null,
private readonly ?CountStrategy $count = null,
) {
// handle collection

Expand All @@ -86,10 +84,6 @@ final private function __construct(
);

$this->criteria = Criteria::create()->orderBy($this->orderBy);

// handle count strategy

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

/**
Expand Down Expand Up @@ -149,7 +143,7 @@ final public static function create(
return $newInstance;
}

private function getCountStrategy(): CountStrategy
private function getCountStrategy(): ?CountStrategy
{
return $this->count;
}
Expand Down
10 changes: 2 additions & 8 deletions packages/collections-domain/src/RecollectionDecorator.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@
use Rekalogika\Contracts\Collections\Recollection;
use Rekalogika\Domain\Collections\Common\Configuration;
use Rekalogika\Domain\Collections\Common\Count\CountStrategy;
use Rekalogika\Domain\Collections\Common\Count\RestrictedCountStrategy;
use Rekalogika\Domain\Collections\Common\Internal\OrderByUtil;
use Rekalogika\Domain\Collections\Common\Trait\RecollectionTrait;
use Rekalogika\Domain\Collections\Common\Trait\SafeCollectionTrait;
Expand Down Expand Up @@ -76,7 +75,6 @@ class RecollectionDecorator implements Recollection
private readonly array $orderBy;

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

/**
* @param Collection<TKey,T> $collection
Expand All @@ -90,7 +88,7 @@ final private function __construct(
array|string|null $orderBy = null,
private readonly ?string $indexBy = null,
private readonly int $itemsPerPage = 50,
?CountStrategy $count = null,
private readonly ?CountStrategy $count = null,
private readonly ?int $softLimit = null,
private readonly ?int $hardLimit = null,
) {
Expand All @@ -110,10 +108,6 @@ final private function __construct(
);

$this->criteria = Criteria::create()->orderBy($this->orderBy);

// handle count strategy

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

/**
Expand Down Expand Up @@ -179,7 +173,7 @@ final public static function create(
return $newInstance;
}

private function getCountStrategy(): CountStrategy
private function getCountStrategy(): ?CountStrategy
{
return $this->count;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ private function getPageable(): PageableInterface

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

0 comments on commit 2942f8b

Please sign in to comment.