Skip to content

Commit

Permalink
add doc + persistent connection
Browse files Browse the repository at this point in the history
  • Loading branch information
clementtalleu committed Jun 6, 2024
1 parent 05602c3 commit 65c2cc3
Show file tree
Hide file tree
Showing 17 changed files with 109 additions and 45 deletions.
4 changes: 4 additions & 0 deletions .github/workflows/tests.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -58,3 +58,7 @@ jobs:
run: vendor/bin/phpstan analyse src --level=3
- name: Run tests
run: vendor/bin/phpunit
- name: composer audit
run: composer audit
- name: composer validate
run: composer validate
37 changes: 18 additions & 19 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,20 @@ with Redis.

- PHP 8.2 or higher
- Redis 4.0 or higher
- Redisearch module ([installation](https://redis.io/docs/latest/operate/oss_and_stack/install/install-stack/))
- php-redis extension (or your favorite Redis client)
- Redis JSON module (optional)
- Composer

## Supported types

- scalar (string, int, float, bool)
- DateTimeImmutable
- DateTime
- array and nested arrays
- object and nested objects
- stdClass

## Installation

Install the library via Composer:
Expand Down Expand Up @@ -57,10 +67,10 @@ class User
```

After add the RedisOm attribute to your class,
you have to run the following command to create the Redis schema for the given source directory:
you have to run the following command to create the Redis schema for your classes (default path is `./src`):

```console
vendor/bin/redisMigration src
vendor/bin/redisMigration <YOUR DIRECTORY PATH>
```

Then you can use the ObjectManager to persist your objects from Redis:
Expand All @@ -75,7 +85,7 @@ $user->id = 1;
$user->name = 'John Doe';

// Persist the object in redis
$objectManager = new ObjectManager();
$objectManager = new RedisObjectManager();
$objectManager->persist($user);
$objectManager->flush();
```
Expand All @@ -96,20 +106,9 @@ $users = $objectManager->getRepository(User::class)->findAll();
$users = $objectManager->getRepository(User::class)->findBy(['name' => 'John Doe'], ['createdAt' => 'DESC'], 10);
```

### Docker

The package provide a Docker Compose configuration to run a Redis
server with the required modules (RedisJSON and Redisearch) for testing purposes.

```console
docker compose up -d
```

### Running tests

```console
docker compose exec php vendor/bin/phpunit tests
```


## Advanced mapping configuration
## Advanced documentation
- [Installation](https://github.com/clementtalleu/php-redis-om/blob/main/docs/installation.md)
- [Configuration](https://github.com/clementtalleu/php-redis-om/blob/main/docs/configuration.md)
- [Docker integration](https://github.com/clementtalleu/php-redis-om/blob/main/docs/docker_integration.md)
- [Mapping ](https://github.com/clementtalleu/php-redis-om/blob/main/docs/mapping.md)
Empty file added docs/advanced_usage.md
Empty file.
22 changes: 22 additions & 0 deletions docs/configuration.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# Configuration

### Persistent connections
> *A Redis server is designed to handle a lot of operations per second making it a very good candidate as a data cache storing engine.
But connecting to the server is an expensive operation and thus connections rate should be kept as low as possible.*.

For more information about persistent connections, read this [article](https://medium.com/assoconnect/how-to-use-persistent-connections-with-redis-for-symfony-cache-with-php-fpm-3e7bd1100736)

php-redis-om provide a way to use persistent connections with the `RedisClient` class.

```php
// Set the persistent connection to true
$objectManager = new RedisObjectManager(createPersistentConnection: true);

// Then you can use the ObjectManager normally
$objectManager->persist($user);
$objectManager->flush();
```

Warning: if you set this parameter to true, the client will connect to the redis server as soon as the object manager is instantiated. Even if the object manager is not actually used.


29 changes: 29 additions & 0 deletions docs/docker_integation.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
### Docker

The package provide a Docker Compose configuration to run a Redis
server with the required modules (RedisJSON and Redisearch) for testing purposes.

You could retrieve an example in compose.yaml file and a Dockerfile for the PHP container configuration.

```yaml
redis:
image: redis/redis-stack-server:7.2.0-v6
ports:
- 6379:6379
healthcheck:
test: [ "CMD", "redis-cli", "--raw", "incr", "ping" ]
volumes:
- redis_data:/data
```
To run the Redis server locally, you can use the following command:
```console
docker compose up -d
```

### Running tests

```console
docker compose exec php vendor/bin/phpunit tests
```
10 changes: 9 additions & 1 deletion docs/advanced_mapping_configuration.md → docs/mapping.md
Original file line number Diff line number Diff line change
Expand Up @@ -110,4 +110,12 @@ Each of these parameters are optional and can be omitted. Here is a description
- The name of the setter method to use to set the value of the property **if the property is not public**. If not set, a default setter as : `setName()` will be used.
- Example: `withName`
- Default: `null`
- Type: `string`
- Type: `string`

## Update the schema
After each modification of your classes, you have to update the schema in Redis. You can do it by running the following command:

```console
vendor/bin/redisMigration <YOUR DIRECTORY PATH>
```
the default path is `./src`.
15 changes: 12 additions & 3 deletions src/Client/RedisClient.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,15 @@ public function __construct(protected ?\Redis $redis = null)
$this->redis = $redis ?? new \Redis(array_key_exists('REDIS_HOST', $_SERVER) ? ['host' => $_SERVER['REDIS_HOST']] : null);
}

public function createPersistentConnection(?string $host = null, ?int $port = null, ?int $timeout = 0): void
{
$this->redis->pconnect(
$host ?? $this->redis->getHost(),
$port ?? $this->redis->getPort(),
$timeout ?? $this->redis->getTimeout(),
);
}

public function hMSet(string $key, array $data): void
{
$result = $this->redis->hMSet(RedisClient::convertPrefix($key), $data);
Expand Down Expand Up @@ -147,16 +156,16 @@ public function count(string $prefixKey, array $criterias = []): int

$rawResult = call_user_func_array([$this->redis, 'rawCommand'], $arguments);

return (int) $rawResult[0];
return (int)$rawResult[0];
}

public function scanKeys(string $prefixKey): array
{
$keys = [];
$iterator = null;
while($iterator !== 0) {
while ($iterator !== 0) {
$scans = $this->redis->scan($iterator, sprintf("%s*", static::convertPrefix($prefixKey)));
foreach($scans as $scan) {
foreach ($scans as $scan) {
$keys[] = $scan;
}
}
Expand Down
1 change: 1 addition & 0 deletions src/Client/RedisClientInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

interface RedisClientInterface
{
public function createPersistentConnection(?string $host = null, ?int $port = null, ?int $timeout = 0): void;
public function hMSet(string $key, array $data): void;
public function hGetAll(string $key): array;
public function del(string $key): void;
Expand Down
10 changes: 2 additions & 8 deletions src/Om/Converters/AbstractArrayConverter.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@

namespace Talleu\RedisOm\Om\Converters;

use Talleu\RedisOm\Om\Converters\ConverterInterface;

abstract class AbstractArrayConverter implements ConverterInterface
{
/**
Expand All @@ -15,17 +13,13 @@ abstract public function convert($data);

public function supportsConversion(string $type, mixed $data): bool
{
return ($type === 'array' || $type === 'iterable') && $data !== null;
return ($type === 'array' || $type === 'iterable' || is_iterable($data)) && $data !== null;
}

abstract public function revert($data, string $type): mixed;

public function supportsReversion(string $type, mixed $value): bool
{
// if (is_array($value) && array_key_exists('date', $value) && array_key_exists('timezone', $value)) {
// return false;
// }

return $type === 'array' || $type === 'iterable' && $value !== null;
return ($type === 'array' || $type === 'iterable') && $value !== null;
}
}
5 changes: 0 additions & 5 deletions src/Om/Converters/JsonModel/ArrayConverter.php
Original file line number Diff line number Diff line change
Expand Up @@ -57,9 +57,4 @@ public function revert($data, string $type): mixed

return $revertedArray;
}

public function supportsReversion(string $type, mixed $value): bool
{
return ($type === 'array' || $type === 'iterable') && $value !== null;
}
}
1 change: 0 additions & 1 deletion src/Om/Converters/JsonModel/JsonObjectConverter.php
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,6 @@ public function convert($data): array
*/
public function revert($data, string $type): mixed
{

$object = new $type();
foreach ($data as $key => $value) {

Expand Down
10 changes: 7 additions & 3 deletions src/Om/RedisObjectManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
use Talleu\RedisOm\Om\Mapping\Entity;
use Talleu\RedisOm\Om\Persister\ObjectToPersist;
use Talleu\RedisOm\Om\Persister\PersisterInterface;
use Talleu\RedisOm\Om\Persister\PersisterOperations;
use Talleu\RedisOm\Om\Repository\RepositoryInterface;

final class RedisObjectManager implements RedisObjectManagerInterface
Expand All @@ -24,7 +23,7 @@ final class RedisObjectManager implements RedisObjectManagerInterface
protected array $objectsToFlush = [];
protected ?KeyGenerator $keyGenerator = null;

public function __construct()
public function __construct(private readonly ?bool $createPersistentConnection = null)
{
$this->keyGenerator = new KeyGenerator();
}
Expand Down Expand Up @@ -136,7 +135,12 @@ protected function getEntityMapper(string|object $object): Entity
$redisEntity->repository->setPrefix($redisEntity->prefix ?? $reflectionClass->getName());
$redisEntity->repository->setClassName($reflectionClass->getName());
$redisEntity->repository->setConverter($redisEntity->converter ?? ($redisEntity->format === RedisFormat::HASH->value ? new HashObjectConverter() : new JsonObjectConverter()));
$redisEntity->repository->setRedisClient($redisEntity->redisClient ?? (new RedisClient()));

$redisClient = $redisEntity->redisClient ?? new RedisClient();
if ($this->createPersistentConnection === true) {
$redisClient->createPersistentConnection();
}
$redisEntity->repository->setRedisClient($redisClient);
$redisEntity->repository->setFormat($redisEntity->format);

return $redisEntity;
Expand Down
2 changes: 1 addition & 1 deletion src/Om/Repository/AbstractObjectRepository.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ public function __construct(public ?string $format = null)
{
}

abstract public function find(string $identifier): ?object;
abstract public function find($identifier): ?object;

public function findBy(array $criteria, ?array $orderBy = null, ?int $limit = null, ?int $offset = null): array
{
Expand Down
2 changes: 1 addition & 1 deletion src/Om/Repository/HashModel/HashRepository.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ final class HashRepository extends AbstractObjectRepository
{
public ?string $format = RedisFormat::HASH->value;

public function find(string $identifier): ?object
public function find($identifier): ?object
{
$data = $this->redisClient->hGetAll("$this->prefix:$identifier");
if (!$data) {
Expand Down
2 changes: 1 addition & 1 deletion src/Om/Repository/JsonModel/JsonRepository.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ final class JsonRepository extends AbstractObjectRepository
{
public ?string $format = RedisFormat::JSON->value;

public function find(string $identifier): ?object
public function find($identifier): ?object
{
$data = $this->redisClient->jsonget("$this->prefix:$identifier");
if (!$data) {
Expand Down
2 changes: 1 addition & 1 deletion src/Om/Repository/RepositoryInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

interface RepositoryInterface
{
public function find(string $identifier): ?object;
public function find($identifier): ?object;
public function findBy(array $criteria, ?array $orderBy = null, ?int $limit = null, ?int $offset = null): array;
public function findAll(): array;
public function findOneBy(array $criteria, ?array $orderBy = null): ?object;
Expand Down
2 changes: 1 addition & 1 deletion tests/Client/Client.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,6 @@ class Client extends RedisClient
public function __construct()
{
parent::__construct();
$this->redis->pconnect($_SERVER['REDIS_HOST']);
$this->createPersistentConnection($_SERVER['REDIS_HOST']);
}
}

0 comments on commit 65c2cc3

Please sign in to comment.