Skip to content

Commit

Permalink
Merge pull request #21 from byjg/5.0
Browse files Browse the repository at this point in the history
Enable Cache Query results
  • Loading branch information
byjg authored Nov 11, 2024
2 parents a1d5263 + 0815a84 commit 19db0f9
Show file tree
Hide file tree
Showing 9 changed files with 161 additions and 10 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,7 @@ $result = $repository->getByQuery($query);
## Advanced Topics

* [The Literal Object](docs/the-literal-object.md)
* [Caching the Results](docs/cache.md)
* [Observing the Database](docs/observers.md)
* [Controlling the data queried/updated](docs/controlling-the-data.md)
* [Using FieldAlias](docs/using-fieldalias.md)
Expand Down
3 changes: 2 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,11 @@
"require": {
"php": ">=8.1 <8.4",
"ext-json": "*",
"byjg/anydataset-db": "^5.0"
"byjg/anydataset-db": "5.0.x-dev"
},
"require-dev": {
"phpunit/phpunit": "^9.6",
"byjg/cache-engine": "^5.0",
"vimeo/psalm": "^5.9"
},
"suggest": {
Expand Down
31 changes: 31 additions & 0 deletions docs/cache.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# Caching the Results

The `AnyDatasetDb` library has a built-in cache mechanism that can be used to cache the results of a query.
This is useful when you have a query that is expensive to run and you want to cache the results for a
certain amount of time.

To enable caching, you need to pass a `CacheQueryResult` object when you´ll query the database.

Here is an example of how to use the cache mechanism:

```php
<?php

$query = Query::getInstance()
->where('id = :id1', ['id1'=>3]);

$cacheEngine = /* any SimpleCache implementation */;

// Get the result and save to cache
$result = $repository->getByQuery($query, cache: new CacheQueryResult($cacheEngine, 120));
```

In this example, the result of the query will be saved in the cache for 120 seconds.

The `CacheQueryResult` object has the following parameters:

- `cacheEngine`: The cache engine to use. It must implement the `Psr\SimpleCache\CacheInterface` interface.
- `ttl`: The time-to-live of the cache in seconds. If the cache is older than this value, it will be considered expired.



35 changes: 35 additions & 0 deletions src/CacheQueryResult.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<?php

namespace ByJG\MicroOrm;

use DateInterval;
use Psr\SimpleCache\CacheInterface;

class CacheQueryResult
{
protected CacheInterface $cache;
protected string $cacheKey;
protected int|DateInterval $ttl;

public function __construct(CacheInterface $cache, string $cacheKey, int|DateInterval $ttl)
{
$this->cache = $cache;
$this->cacheKey = $cacheKey;
$this->ttl = $ttl;
}

public function getCache(): CacheInterface
{
return $this->cache;
}

public function getCacheKey(): string
{
return $this->cacheKey;
}

public function getTtl(): DateInterval|int
{
return $this->ttl;
}
}
3 changes: 2 additions & 1 deletion src/Interface/QueryBuilderInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,12 @@

use ByJG\AnyDataset\Core\GenericIterator;
use ByJG\AnyDataset\Db\DbDriverInterface;
use ByJG\MicroOrm\CacheQueryResult;
use ByJG\MicroOrm\SqlObject;

interface QueryBuilderInterface
{
public function build(?DbDriverInterface $dbDriver = null): SqlObject;

public function buildAndGetIterator(?DbDriverInterface $dbDriver = null): GenericIterator;
public function buildAndGetIterator(?DbDriverInterface $dbDriver = null, ?CacheQueryResult $cache = null): GenericIterator;
}
9 changes: 7 additions & 2 deletions src/QueryBasic.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

use ByJG\AnyDataset\Core\GenericIterator;
use ByJG\AnyDataset\Db\DbDriverInterface;
use ByJG\AnyDataset\Db\SqlStatement;
use ByJG\MicroOrm\Exception\InvalidArgumentException;
use ByJG\MicroOrm\Interface\QueryBuilderInterface;
use ByJG\Serializer\Serialize;
Expand Down Expand Up @@ -272,9 +273,13 @@ public function build(?DbDriverInterface $dbDriver = null): SqlObject
return new SqlObject($sql, $params);
}

public function buildAndGetIterator(?DbDriverInterface $dbDriver = null): GenericIterator
public function buildAndGetIterator(?DbDriverInterface $dbDriver = null, ?CacheQueryResult $cache = null): GenericIterator
{
$sqlObject = $this->build($dbDriver);
return $dbDriver->getIterator($sqlObject->getSql(), $sqlObject->getParameters());
$sqlStatement = new SqlStatement($sqlObject->getSql());
if (!empty($cache)) {
$sqlStatement->withCache($cache->getCache(), $cache->getCacheKey(), $cache->getTtl());
}
return $sqlStatement->getIterator($dbDriver, $sqlObject->getParameters());
}
}
10 changes: 7 additions & 3 deletions src/Repository.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
use ByJG\AnyDataset\Core\IteratorFilter;
use ByJG\AnyDataset\Db\DbDriverInterface;
use ByJG\AnyDataset\Db\IteratorFilterSqlFormatter;
use ByJG\AnyDataset\Db\SqlStatement;
use ByJG\MicroOrm\Exception\InvalidArgumentException;
use ByJG\MicroOrm\Exception\OrmBeforeInvalidException;
use ByJG\MicroOrm\Exception\OrmInvalidFieldsException;
Expand Down Expand Up @@ -252,15 +253,18 @@ public function getScalar(QueryBuilderInterface $query): mixed
* @param Mapper[] $mapper
* @return array
*/
public function getByQuery(QueryBuilderInterface $query, array $mapper = []): array
public function getByQuery(QueryBuilderInterface $query, array $mapper = [], ?CacheQueryResult $cache = null): array
{
$mapper = array_merge([$this->mapper], $mapper);
$sqlBuild = $query->build($this->getDbDriver());

$params = $sqlBuild->getParameters();
$sql = $sqlBuild->getSql();
$sql = new SqlStatement($sqlBuild->getSql());
if (!empty($cache)) {
$sql->withCache($cache->getCache(), $cache->getCacheKey(), $cache->getTtl());
}
$result = [];
$iterator = $this->getDbDriver()->getIterator($sql, $params);
$iterator = $sql->getIterator($this->getDbDriver(), $params);

foreach ($iterator as $row) {
$collection = [];
Expand Down
9 changes: 7 additions & 2 deletions src/Union.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

use ByJG\AnyDataset\Core\GenericIterator;
use ByJG\AnyDataset\Db\DbDriverInterface;
use ByJG\AnyDataset\Db\SqlStatement;
use ByJG\MicroOrm\Exception\InvalidArgumentException;
use ByJG\MicroOrm\Interface\QueryBuilderInterface;

Expand Down Expand Up @@ -103,9 +104,13 @@ public function build(?DbDriverInterface $dbDriver = null): SqlObject
}


public function buildAndGetIterator(?DbDriverInterface $dbDriver = null): GenericIterator
public function buildAndGetIterator(?DbDriverInterface $dbDriver = null, ?CacheQueryResult $cache = null): GenericIterator
{
$sqlObject = $this->build($dbDriver);
return $dbDriver->getIterator($sqlObject->getSql(), $sqlObject->getParameters());
$sqlStatement = new SqlStatement($sqlObject->getSql());
if (!empty($cache)) {
$sqlStatement->withCache($cache->getCache(), $cache->getCacheKey(), $cache->getTtl());
}
return $sqlStatement->getIterator($dbDriver, $sqlObject->getParameters());
}
}
70 changes: 69 additions & 1 deletion tests/RepositoryTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
use ByJG\AnyDataset\Core\IteratorFilter;
use ByJG\AnyDataset\Db\DbDriverInterface;
use ByJG\AnyDataset\Db\Factory;
use ByJG\Cache\Psr16\ArrayCacheEngine;
use ByJG\MicroOrm\CacheQueryResult;
use ByJG\MicroOrm\DeleteQuery;
use ByJG\MicroOrm\Exception\AllowOnlyNewValuesConstraintException;
use ByJG\MicroOrm\Exception\InvalidArgumentException;
Expand Down Expand Up @@ -169,10 +171,44 @@ public function testBuildAndGetIterator()
->table('users')
->where('id = :id', ['id' => 1]);

$iterator = $query->buildAndGetIterator($this->repository->getDbDriver())->toArray();
$iterator = $query->buildAndGetIterator($this->repository->getDbDriver(), null)->toArray();
$this->assertEquals(1, count($iterator));
}

public function testBuildAndGetIteratorWithCache()
{
$query = Query::getInstance()
->table('users')
->where('id = :id', ['id' => 1]);

$cacheEngine = new ArrayCacheEngine();

// Sanity test
$result = $query->buildAndGetIterator($this->repository->getDbDriver())->toArray();
$this->assertCount(1, $result);

$cacheObject = new CacheQueryResult($cacheEngine, "mykey", 120);

// Get the result and save to cache
$result = $query->buildAndGetIterator($this->repository->getDbDriver(), cache: $cacheObject)->toArray();
$this->assertCount(1, $result);

// Delete the record
$deleteQuery = DeleteQuery::getInstance()
->table('users')
->where('id = :id', ['id' => 1]);
$deleteQuery->buildAndExecute($this->repository->getDbDriver());

// Check if query with no cache the record is not found
$result = $query->buildAndGetIterator($this->repository->getDbDriver())->toArray();
$this->assertCount(0, $result);

// Check if query with cache the record is found
$result = $query->buildAndGetIterator($this->repository->getDbDriver(), cache: $cacheObject)->toArray();
$this->assertCount(1, $result);
}


public function testInsert()
{
$users = new Users();
Expand Down Expand Up @@ -1242,5 +1278,37 @@ public function testQueryInstanceWithWrongModel()
$query = $this->repository->queryInstance($infoModel);
}

public function testQueryWithCache()
{
$query = $this->infoMapper->getQueryBasic()
->where('id = :id1', ['id1'=>3]);

$infoRepository = new Repository($this->dbDriver, $this->infoMapper);
$cacheEngine = new ArrayCacheEngine();

// Sanity test
$result = $infoRepository->getByQuery($query);
$this->assertCount(1, $result);

$cacheObject = new CacheQueryResult($cacheEngine, "qry", 120);

// Get the result and save to cache
$result = $infoRepository->getByQuery($query, cache: $cacheObject);
$this->assertCount(1, $result);

// Delete the record
$deleteQuery = DeleteQuery::getInstance()
->table($this->infoMapper->getTable())
->where('id = :id', ['id' => 3]);
$deleteQuery->buildAndExecute($this->repository->getDbDriver());

// Check if query with no cache the record is not found
$result = $infoRepository->getByQuery($query);
$this->assertCount(0, $result);

// Check if query with cache the record is found
$result = $infoRepository->getByQuery($query, cache: $cacheObject);
$this->assertCount(1, $result);
}

}

0 comments on commit 19db0f9

Please sign in to comment.