Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

0.4.0 setup #6

Merged
merged 12 commits into from
Nov 19, 2023
1 change: 1 addition & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
/.gitattributes export-ignore
/.gitignore export-ignore
/.github export-ignore
/.scrutinizer.yml export-ignore
/doc export-ignore
/phpunit.xml export-ignore
/tests export-ignore
11 changes: 4 additions & 7 deletions doc/cache.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ To start using auto-cache feature you need:
to `Composite\DB\AbstractCachedTable`
3. Implement method `getFlushCacheKeys()`
4. Change all internal select methods to their cached versions (example: `findByPkInternal()`
to `findByPkCachedInternal()` etc.)
to `_findByPkCached()` etc.)

You can also generate cached version of your table with console command:

Expand Down Expand Up @@ -46,23 +46,20 @@ class PostsTable extends AbstractCachedTable

public function findByPk(int $id): ?Post
{
return $this->createEntity($this->findByPkInternalCached($id));
return $this->_findByPkCached($id);
}

/**
* @return Post[]
*/
public function findAllFeatured(): array
{
return $this->createEntities($this->findAllInternal(
'is_featured = :is_featured',
['is_featured' => true],
));
return $this->_findAll(['is_featured' => true]);
}

public function countAllFeatured(): int
{
return $this->countAllCachedInternal(
return $this->_countAllCached(
'is_featured = :is_featured',
['is_featured' => true],
);
Expand Down
12 changes: 4 additions & 8 deletions doc/example.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,25 +43,21 @@ class UsersTable extends \Composite\DB\AbstractTable

public function findByPk(int $id): ?User
{
return $this->createEntity($this->findByPkInternal($id));
return $this->_findByPk($id);
}

/**
* @return User[]
*/
public function findAllActive(): array
{
return $this->createEntities($this->findAllInternal(
'status = :status',
['status' => Status::ACTIVE->name],
));
return $this->_findAll(['status' => Status::ACTIVE]);
}

public function countAllActive(): int
{
return $this->countAllInternal(
'status = :status',
['status' => Status::ACTIVE->name],
return $this->_countAll(
['status' => Status::ACTIVE],
);
}

Expand Down
31 changes: 23 additions & 8 deletions doc/table.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,20 +38,20 @@ class UsersTable extends AbstractTable

public function findOne(int $id): ?User
{
return $this->createEntity($this->findOneInternal($id));
return $this->_findByPk($id);
}

/**
* @return User[]
*/
public function findAll(): array
{
return $this->createEntities($this->findAllInternal());
return $this->_findAll();
}

public function countAll(): int
{
return $this->countAllInternal();
return $this->_countAll();
}
}
```
Expand All @@ -67,15 +67,30 @@ Example with internal helper:
*/
public function findAllActiveAdults(): array
{
$rows = $this->findAllInternal(
'age > :age AND status = :status',
['age' => 18, 'status' => Status::ACTIVE->name],
return $this->_findAll(
new Where(
'age > :age AND status = :status',
['age' => 18, 'status' => Status::ACTIVE->name],
)
);
return $this->createEntities($rows);
}
```

Example with pure query builder
Or it might be simplified to:
```php
/**
* @return User[]
*/
public function findAllActiveAdults(): array
{
return $this->_findAll([
'age' => ['>', 18],
'status' => Status:ACTIVE,
]);
}
```

Or you can use standard Doctrine QueryBuilder
```php
/**
* @return User[]
Expand Down
1 change: 1 addition & 0 deletions phpunit.xml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/10.1/phpunit.xsd" bootstrap="tests/bootstrap.php"
executionOrder="depends,defects" beStrictAboutOutputDuringTests="true" failOnRisky="true" failOnWarning="true"
colors="true" cacheDirectory=".phpunit.cache" requireCoverageMetadata="false"
displayDetailsOnTestsThatTriggerWarnings="true"
beStrictAboutCoverageMetadata="true">
<testsuites>
<testsuite name="default">
Expand Down
98 changes: 48 additions & 50 deletions src/AbstractCachedTable.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,14 @@

namespace Composite\DB;

use Composite\DB\Exceptions\DbException;
use Composite\Entity\AbstractEntity;
use Psr\SimpleCache\CacheInterface;
use Ramsey\Uuid\UuidInterface;

abstract class AbstractCachedTable extends AbstractTable
{
use SelectRawTrait;

protected const CACHE_VERSION = 1;

public function __construct(
Expand Down Expand Up @@ -93,59 +94,60 @@ private function collectCacheKeysByEntity(AbstractEntity $entity): array
}

/**
* @return array<string, mixed>|null
* @return AbstractEntity|null
*/
protected function findByPkCachedInternal(mixed $pk, null|int|\DateInterval $ttl = null): ?array
protected function _findByPkCached(mixed $pk, null|int|\DateInterval $ttl = null): mixed
{
return $this->findOneCachedInternal($this->getPkCondition($pk), $ttl);
return $this->_findOneCached($this->getPkCondition($pk), $ttl);
}

/**
* @param array<string, mixed> $condition
* @param array<string, mixed> $where
* @param int|\DateInterval|null $ttl
* @return array<string, mixed>|null
* @return AbstractEntity|null
*/
protected function findOneCachedInternal(array $condition, null|int|\DateInterval $ttl = null): ?array
protected function _findOneCached(array $where, null|int|\DateInterval $ttl = null): mixed
{
return $this->getCached(
$this->getOneCacheKey($condition),
fn() => $this->findOneInternal($condition),
$row = $this->getCached(
$this->getOneCacheKey($where),
fn() => $this->_findOneRaw($where),
$ttl,
) ?: null;
);
return $this->createEntity($row);
}

/**
* @param array<string, mixed> $whereParams
* @param array<string, mixed>|Where $where
* @param array<string, string>|string $orderBy
* @return array<string, mixed>[]
* @return array<AbstractEntity>|array<array-key, AbstractEntity>
*/
protected function findAllCachedInternal(
string $whereString = '',
array $whereParams = [],
protected function _findAllCached(
array|Where $where = [],
array|string $orderBy = [],
?int $limit = null,
null|int|\DateInterval $ttl = null,
?string $keyColumnName = null,
): array
{
return $this->getCached(
$this->getListCacheKey($whereString, $whereParams, $orderBy, $limit),
fn() => $this->findAllInternal(whereString: $whereString, whereParams: $whereParams, orderBy: $orderBy, limit: $limit),
$rows = $this->getCached(
$this->getListCacheKey($where, $orderBy, $limit),
fn() => $this->_findAllRaw(where: $where, orderBy: $orderBy, limit: $limit),
$ttl,
);
return $this->createEntities($rows, $keyColumnName);
}

/**
* @param array<string, mixed> $whereParams
* @param array<string, mixed>|Where $where
*/
protected function countAllCachedInternal(
string $whereString = '',
array $whereParams = [],
protected function _countByAllCached(
array|Where $where = [],
null|int|\DateInterval $ttl = null,
): int
{
return (int)$this->getCached(
$this->getCountCacheKey($whereString, $whereParams),
fn() => $this->countAllInternal(whereString: $whereString, whereParams: $whereParams),
$this->getCountCacheKey($where),
fn() => $this->_countAll(where: $where),
$ttl,
);
}
Expand All @@ -166,10 +168,14 @@ protected function getCached(string $cacheKey, callable $dataCallback, null|int|
/**
* @param mixed[] $ids
* @param int|\DateInterval|null $ttl
* @return array<array<string, mixed>>
* @return array<AbstractEntity>|array<array-key, AbstractEntity>
* @throws \Psr\SimpleCache\InvalidArgumentException
*/
protected function findMultiCachedInternal(array $ids, null|int|\DateInterval $ttl = null): array
protected function _findMultiCached(
array $ids,
null|int|\DateInterval $ttl = null,
?string $keyColumnName = null,
): array
{
$result = $cacheKeys = $foundIds = [];
foreach ($ids as $id) {
Expand All @@ -188,11 +194,11 @@ protected function findMultiCachedInternal(array $ids, null|int|\DateInterval $t
}
$ids = array_diff($ids, $foundIds);
foreach ($ids as $id) {
if ($row = $this->findByPkCachedInternal($id, $ttl)) {
if ($row = $this->_findByPkCached($id, $ttl)) {
$result[] = $row;
}
}
return $result;
return $this->createEntities($result, $keyColumnName);
}

/**
Expand All @@ -209,37 +215,35 @@ protected function getOneCacheKey(string|int|array|AbstractEntity|UuidInterface
}

/**
* @param array<string, mixed> $whereParams
* @param array<string, mixed>|Where $where
* @param array<string, string>|string $orderBy
*/
protected function getListCacheKey(
string $whereString = '',
array $whereParams = [],
array|Where $where = [],
array|string $orderBy = [],
?int $limit = null
): string
{
$wherePart = $this->prepareWhereKey($whereString, $whereParams);
$wherePart = is_array($where) ? $where : $this->prepareWhereKey($where);
return $this->buildCacheKey(
'l',
$wherePart ?? 'all',
$wherePart ?: 'all',
$orderBy ? ['ob' => $orderBy] : null,
$limit ? ['limit' => $limit] : null,
);
}

/**
* @param array<string, mixed> $whereParams
* @param array<string, mixed>|Where $where
*/
protected function getCountCacheKey(
string $whereString = '',
array $whereParams = [],
array|Where $where = [],
): string
{
$wherePart = $this->prepareWhereKey($whereString, $whereParams);
$wherePart = is_array($where) ? $where : $this->prepareWhereKey($where);
return $this->buildCacheKey(
'c',
$wherePart ?? 'all',
$wherePart ?: 'all',
);
}

Expand Down Expand Up @@ -274,24 +278,18 @@ protected function buildCacheKey(mixed ...$parts): string

private function formatStringForCacheKey(string $string): string
{
$string = mb_strtolower($string);
$string = strtolower($string);
$string = str_replace(['!=', '<>', '>', '<', '='], ['_not_', '_not_', '_gt_', '_lt_', '_eq_'], $string);
$string = (string)preg_replace('/\W/', '_', $string);
return trim((string)preg_replace('/_+/', '_', $string), '_');
}

/**
* @param array<string, mixed> $whereParams
*/
private function prepareWhereKey(string $whereString, array $whereParams): ?string
private function prepareWhereKey(Where $where): string
{
if (!$whereString) {
return null;
}
return str_replace(
array_map(fn (string $key): string => ':' . $key, array_keys($whereParams)),
array_values($whereParams),
$whereString,
array_map(fn (string $key): string => ':' . $key, array_keys($where->params)),
array_values($where->params),
$where->condition,
);
}
}
Loading