PHPackages                             raphaelcangucu/activeredis - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Database &amp; ORM](/categories/database)
4. /
5. raphaelcangucu/activeredis

ActiveLibrary[Database &amp; ORM](/categories/database)

raphaelcangucu/activeredis
==========================

An ActiveRecord implementation for Redis.

v1.7.1(8mo ago)09.9k↓46.7%MITPHPPHP &gt;=8.1

Since Sep 24Pushed 8mo agoCompare

[ Source](https://github.com/raphaelcangucu/ActiveRedis)[ Packagist](https://packagist.org/packages/raphaelcangucu/activeredis)[ RSS](/packages/raphaelcangucu-activeredis/feed)WikiDiscussions master Synced 1mo ago

READMEChangelogDependencies (7)Versions (27)Used By (0)

[![](https://github.com/DirectoryTree/ActiveRedis/raw/master/art/logo.svg)](https://github.com/DirectoryTree/ActiveRedis/blob/master/art/logo.svg)

An Active Record implementation for Redis hashes in Laravel.

[![](https://camo.githubusercontent.com/a4e571303e2948807abc8a98366424a45f59b5b07cba01b43a724c26cfaa063d/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f616374696f6e732f776f726b666c6f772f7374617475732f6469726563746f7279747265652f61637469766572656469732f72756e2d74657374732e796d6c3f6272616e63683d6d6173746572267374796c653d666c61742d737175617265)](https://github.com/directorytree/activeredis/actions)[![](https://camo.githubusercontent.com/f2d4ac724f75761fdc6a99f500a53eafd2e746961c7b2d192c75f11a847e15d6/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f762f6469726563746f7279747265652f61637469766572656469732e7376673f7374796c653d666c61742d737175617265)](https://packagist.org/packages/directorytree/activeredis)[![](https://camo.githubusercontent.com/43842b2f70970b4600789ce8f0e9b5cd0146c3141d6e26b440fa0ea9f5ea97cc/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f64742f6469726563746f7279747265652f61637469766572656469732e7376673f7374796c653d666c61742d737175617265)](https://packagist.org/packages/directorytree/activeredis)[![](https://camo.githubusercontent.com/f27f17d750c49df69a97a9938a744a503d1b558d8b7e9c0788cf457b5fac4e24/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f6c2f6469726563746f7279747265652f61637469766572656469732e7376673f7374796c653d666c61742d737175617265)](https://packagist.org/packages/directorytree/activeredis)

---

ActiveRedis provides you simple and efficient way to interact with Redis hashes using an Eloquent-like API.

Index
-----

[](#index)

- [Requirements](#requirements)
- [Installation](#installation)
- [Redis Cluster Support](#redis-cluster-support)
- [Usage](#usage)
    - [Creating Models](#creating-models)
        - [Model Identifiers](#model-identifiers)
        - [Model Timestamps](#model-timestamps)
        - [Model Casts](#model-casts)
        - [Model Events](#model-events)
        - [Model Connection](#model-connection)
    - [Updating Models](#updating-models)
    - [Deleting Models](#deleting-models)
    - [Expiring Models](#expiring-models)
    - [Querying Models](#querying-models)
        - [Chunking](#chunking)
        - [Searching](#searching)
    - [Testing](#testing)
- [Credits](#credits)

Requirements
------------

[](#requirements)

- PHP &gt;= 8.1
- Redis &gt;= 3.0
- Laravel &gt;= 9.0

Installation
------------

[](#installation)

You can install the package via composer:

```
composer require raphaelcangucu/activeredis
```

Redis Cluster Support
---------------------

[](#redis-cluster-support)

ActiveRedis now supports **Redis Cluster mode** for horizontal scaling and high availability! 🚀

### Quick Start with Cluster

[](#quick-start-with-cluster)

```
use DirectoryTree\ActiveRedis\Model;

// Enable cluster support globally
Model::setRepository('indexed'); // Recommended for best performance
// Or
Model::setRepository('cluster');  // Basic cluster support

// Configure per model
class Visit extends Model
{
    protected static string $repository = 'indexed';
    protected ?string $connection = 'redis_cluster';
}
```

### Cluster Configuration

[](#cluster-configuration)

Add to your `config/database.php`:

```
'redis' => [
    'redis_cluster' => [
        'client' => 'predis',
        'cluster' => 'redis',
        'clusters' => [
            ['host' => '127.0.0.1', 'port' => 7001],
            ['host' => '127.0.0.1', 'port' => 7002],
            ['host' => '127.0.0.1', 'port' => 7003],
        ],
        'options' => [
            'cluster' => 'redis',
        ],
    ],
],
```

### Key Features

[](#key-features)

- ✅ **Horizontal Scaling** - Distribute data across multiple Redis nodes
- ✅ **High Availability** - Automatic failover with replica nodes
- ✅ **AWS ElastiCache Compatible** - Production-ready for AWS environments
- ✅ **Secondary Index System** - Efficient queries without SCAN operations
- ✅ **Hash Tag Support** - Co-locate related keys for transactions
- ✅ **Transparent Operation** - Existing code works with minimal changes

### Usage

[](#usage)

```
// Works exactly like single Redis
$visit = Visit::create([
    'ip' => '192.168.1.100',
    'user_agent' => 'Mozilla/5.0',
]);

// Queries work across all cluster nodes
$visits = Visit::where('ip', '192.168.1.*')->get();

// All operations are cluster-aware
$visit->update(['path' => '/updated']);
$visit->delete();
```

For comprehensive cluster documentation, see [CLUSTER.md](CLUSTER.md).

Usage
-----

[](#usage-1)

### Creating Models

[](#creating-models)

To get started, define a new ActiveRedis model:

```
namespace App\Redis;

use DirectoryTree\ActiveRedis\Model;

class Visit extends Model {}
```

Then, create models with whatever data you'd like:

Important

Without [model casts](#model-casts) defined, all values you assign to model attributes will be cast to strings, as that is their true storage type in Redis.

```
use App\Redis\Visit;

$visit = Visit::create([
    'ip' => request()->ip(),
    'url' => request()->url(),
    'user_agent' => request()->userAgent(),
]);
```

This will create a new Redis hash in below format:

```
{plural_model_name}:{key_name}:{key_value}

```

For example:

```
visits:id:f195637b-7d48-43ab-abab-86e93dfc9410

```

Access attributes as you would expect on the model instance:

```
$visit->ip; // xxx.xxx.xxx.xxx
$visit->url; // https://example.com
$visit->user_agent; // Mozilla/5.0 ...
```

Important

Before you begin using your model in production, consider possible [searchable attributes](#searching) that you may like to be part of your model schema, as these should not be modified once you have existing records.

#### Model Identifiers

[](#model-identifiers)

When creating models, ActiveRedis will automatically generate a UUID for each model, assigned to the `id` attribute:

```
$visit->id; // "f195637b-7d48-43ab-abab-86e93dfc9410"
$visit->getKey(); // "f195637b-7d48-43ab-abab-86e93dfc9410"
$visit->getHashKey(); // "visits:id:f195637b-7d48-43ab-abab-86e93dfc9410"

$visit->getKeyName(); // "id"
$visit->getBaseHash(); // "visits:id"
$visit->getHashPrefix(): // "visits"
```

You may provide your own ID if you'd prefer:

Important

Redis keys are **case-sensitive**. Be mindful of this, as it impacts queries, discussed below.

```
$visit = Visit::create([
    'id' => 'custom-id',
    // ...
]);

$visit->id; // "custom-id"
$visit->getHashKey(); // "visits:id:custom-id"
```

Attempting to create a model with an ID that already exists will result in a `DuplicateKeyException`:

```
Visit::create(['id' => 'custom-id']);

// DuplicateKeyException: A model with the key 'custom-id' already exists.
Visit::create(['id' => 'custom-id']);
```

Similarly, attempting to create a model with an empty ID will throw a `InvalidKeyException`:

```
// InvalidKeyException: A key is required to create a model.
Visit::create(['id' => '']);
```

If you're running a high traffic application that may encounter a race condition, you may pass in `true`in the second argument to `create` to ignore the `DuplicateKeyException` and delete the existing record:

```
Visit::create(['id' => 'custom-id']);

// The existing model will be deleted.
Visit::create(['id' => 'custom-id'], force: true);
```

Similarly, you may pass in `true` into the `save` method to ignore the exception and delete the existing record:

```
(new Visit(['id' => 'custom-id'])->save();

(new Visit(['id' => 'custom-id']))->save(force: true);
```

To change the name of the field in which the model key is stored, override the `key` property:

```
namespace App\Redis;

use DirectoryTree\ActiveRedis\Model;

class Visit extends Model
{
    /**
     * The key name for the model.
     */
    protected string $key = 'custom_key';
}
```

ActiveRedis will always generate a new UUID in the key's attribute if you do not provide one.

To change this behaviour or generate your own unique keys, you may override the `getNewKey()` method:

Important

**Do not** generate keys with colons (:) or asterisks (\*). They are reserved characters in Redis.

This also applies to values of [searchable](#searching) attributes.

```
namespace App\Redis;

use Illuminate\Support\Str;
use DirectoryTree\ActiveRedis\Model;

class Visit extends Model
{
    /**
     * Generate a new key for the model.
     */
    protected function getNewKey(): string
    {
        return Str::uuid();
    }
}
```

#### Model Timestamps

[](#model-timestamps)

Models will also maintain `created_at` and `updated_at` attributes:

Important

Timestamp attributes will be returned as `Carbon` instances when accessed.

```
$visit->created_at; // \Carbon\Carbon('2024-01-01 00:00:00')
$visit->updated_at; // \Carbon\Carbon('2024-01-01 00:00:00')
```

To only update a models `updated_at` timestamp, you may call the `touch()` method:

```
$visit->touch();
```

You may provide a timestamp attribute to touch as well:

```
$visit->touch('created_at');
```

To disable timestamps, you may override the `timestamps` property and set it to `false`:

```
class Visit extends Model
{
    /**
     * Indicates if the model should be timestamped.
     */
    public bool $timestamps = false;
}
```

If you need to customize the names of the columns used to store the timestamps, you may define `CREATED_AT` and `UPDATED_AT` constants on your model:

```
class Visit extends Model
{
    const CREATED_AT = 'creation_date';
    const UPDATED_AT = 'updated_date';
}
```

#### Model Casts

[](#model-casts)

To cast model attributes to a specific type, you may define a `casts` property on the model:

```
class Visit extends Model
{
    /**
     * The attributes that should be cast to native types.
     */
    protected array $casts = [
        'user_id' => 'integer',
        'authenticated' => 'boolean',
    ];
}
```

When you access the attribute, it will be cast to the specified type:

```
$visit = new Visit([
    'user_id' => '1',
    'authenticated' => '1',
    // ...
]);

$visit->user_id; // (int) 1
$visit->authenticated; // (bool) true

$visit->getAttributes(); // ['user_id' => '1', 'authenticated' => '1'],
```

Here is a list of all supported casts:

- `json`
- `date`
- `real`
- `array`
- `float`
- `string`
- `object`
- `double`
- `integer`
- `boolean`
- `datetime`
- `timestamp`
- `collection`
- `immutable_date`
- `immutable_datetime`
- `decimal:`

Enum casts are also available:

```
use App\Enums\VisitType;

class Visit extends Model
{
    /**
     * The attributes that should be cast to native types.
     */
    protected array $casts = [
        'type' => VisitType::class,
    ];
}
```

```
$visit = Visit::create(['type' => VisitType::Unique]);
// Or:
$visit = Visit::create(['type' => 'unique']);

$visit->type; // (enum) VisitType::Unique
```

#### Model Events

[](#model-events)

ActiveRedis models dispatch several events, allowing you to hook into the following moments in a model's lifecycle: `retrieved`, `creating`, `created`, `updating`, `updated`, `saving`, `saved`, `deleting`, and `deleted`.

You may register listeners for these methods inside your model's `booted` method:

> You may return `false` from an event listener on the `creating`, `updating`, or `deleting` events to cancel the operation.

```
class Visit extends Model
{
    /**
     * The "booted" method of the model.
     */
    protected static function booted(): void
    {
        static::creating(function (Visit $visit) {
            // ...
        });

        // ...
    }
}
```

If you prefer, you may also create a model observer:

```
class VisitObserver
{
    /**
     * Handle the "creating" event.
     */
    public function creating(Visit $visit): void
    {
        // ...
    }
}
```

And register it using the `observe` method:

```
use App\Redis\Visit;

class AppServiceProvider extends ServiceProvider
{
    /**
     * Bootstrap any application services.
     */
    public function boot(): void
    {
        Visit::observe(VisitObserver::class);
    }
}
```

#### Model Connection

[](#model-connection)

By default, models will use the default Redis connection defined in your Laravel configuration.

It is recommended to define a separate connection for your ActiveRedis models in your `config/database.php`file, as your default Redis connection will contain other data, such as jobs, cache, and sessions.

This will make queries more efficient, as it will only need to scan over the keys owned by your models:

```
return [
    // ...

    'redis' => [
        'activeredis' => [
            'url' => env('REDIS_URL'),
            'host' => env('REDIS_HOST', '127.0.0.1'),
            'password' => env('REDIS_PASSWORD', null),
            'port' => env('REDIS_PORT', '6379'),
            'database' => '10',
        ],
    ],
];
```

To use this connection, you may override the `connection` property on the model:

```
class Visit extends Model
{
    /**
     * The Redis connection to use.
     */
    protected ?string $connection = 'activeredis';
}
```

### Updating Models

[](#updating-models)

You may update models using the `update()` method:

```
$visit->update([
    'ip' => 'xxx.xxx.xxx.xxx',
    'url' => 'https://example.com',
    'user_agent' => 'Mozilla/5.0 ...',
]);
```

Or by setting model attributes and calling the `save()` method:

```
$visit->ip = 'xxx.xxx.xxx.xxx';

$visit->save();
```

### Deleting Models

[](#deleting-models)

To delete models, use the `delete()` method on the model instance:

```
$visit->delete();
```

Or, you may also delete models by their ID:

```
$deleted = Visit::destroy('f195637b-7d48-43ab-abab-86e93dfc9410');

echo $deleted; // 1
```

You may delete multiple models by providing an array of IDs:

```
$deleted = Visit::destroy(['f195637b...', 'a195637b...']);

echo $deleted; // 2
```

### Expiring Models

[](#expiring-models)

To expire models after a certain amount of time, use the `setExpiry()` method:

```
$visit->setExpiry(now()->addMinutes(5));
```

After 5 minutes have elapsed, the model will be automatically deleted from Redis.

To retrieve a model's expiry time, use the `getExpiry()` method:

Important

The value returned will be a `Carbon` instance, or `null` if the model does not expire.

```
$visit->getExpiry(); // \Carbon\Carbon|null
```

### Querying Models

[](#querying-models)

Querying models uses Redis' `SCAN` command to iterate over all keys in the model's hash set.

For example, when the Visit model is queried, the pattern `visits:id:*` is used:

```
SCAN {cursor} MATCH visits:id:* COUNT {count}

```

To begin querying models, you may call the `query()` method on the model:

```
$visits = Visit::query()->get();
```

This will iterate over all keys in the model's hash set and return a collection of models matching the pattern.

Missing model methods will be forwarded to the query builder, so you may call query methods dynamically on the model if you prefer.

```
$visits = Visit::get();
```

#### Finding

[](#finding)

To retrieve specific models, you may use the `find` method:

```
$visit = Visit::find('f195637b-7d48-43ab-abab-86e93dfc9410');
```

If you would like to throw an exception when the model is not found, you may use the `findOrFail` method:

```
Visit::findOrFail('missing'); // ModelNotFoundException
```

#### Chunking

[](#chunking)

You may chunk query results using the `chunk()` method:

Important

Redis does not guarantee the exact number of records returned in each SCAN iteration. See  for more information.

```
use App\Redis\Visit;
use Illuminate\Support\Collection;

Visit::chunk(100, function (Collection $visits) {
    $visits->each(function ($visit) {
        // ...
    });
});
```

Or call the `each` method:

```
use App\Redis\Visit;

Visit::each(function (Visit $visit) {
    // ...
}, 100);
```

You may return `false` in the callback to stop the chunking query:

```
use App\Redis\Visit;

Visit::each(function (Visit $visit) {
    if ($visit->ip === 'xxx.xxx.xxx.xxx') {
        return false;
    }
});
```

#### Searching

[](#searching)

Before attempting to search models, you must define which attributes you would like to be searchable on the model:

```
namespace App\Redis;

class Visit extends Model
{
    /**
     * The attributes that are searchable.
     */
    protected array $searchable = ['ip'];
}
```

Important

Consider searchable attributes to be part of your model schema. They should be defined before you begin using your model. **Do not change these while you have existing records**. Doing so will lead to models that cannot be retrieved without interacting with Redis manually.

When you define these attributes, they will be stored as a part of the hash key in the below format:

```
visits:id:{id}:ip:{ip}

```

For example:

```
visits:id:f195637b-7d48-43ab-abab-86e93dfc9410:ip:127.0.0.1

```

If you do not provide a value for a searchable attribute, literal string `null` will be used as the value in the key:

```
visits:id:f195637b-7d48-43ab-abab-86e93dfc9410:ip:null

```

When multiple searchable attributes are defined, they will be stored in alphabetical order from left to right in the hash key:

```
class Visit extends Model
{
    /**
     * The attributes that are searchable.
     */
    protected array $searchable = ['user_id', 'ip'];
}
```

For example:

```
visits:id:{id}:ip:{ip}:user_id:{user_id}

```

Tip

Because searchable attributes should not be modified while you have existing records, you may find it useful name your models in a way that references the searchable attributes. For example `UserVisit`.

```
$visit = UserVisit::create([
    'user_id' => 1,
    'ip' => request()->ip(),
]);

$visit->getHashKey(); // "user_visits:id:f195637b-7d48-43ab-abab-86e93dfc9410:ip:127.0.0.1:user_id:1"
```

Once the searchable attributes have been defined, you may begin querying for them using the `where()` method:

```
// SCAN ... MATCH visits:id:*:ip:127.0.0.1:user_id:1
$visits = Visit::query()
    ->where('user_id', 1)
    ->where('ip', '127.0.0.1')
    ->get();
```

You may omit searchable attributes from the query and an asterisk will be inserted automatically:

```
// SCAN ... MATCH visits:id:*:ip:127.0.0.1:user_id:*
$visit = Visit::where('ip', '127.0.0.1')->first();
```

You may also use asterisks in your where clauses to perform wildcard searches:

```
// SCAN ... MATCH visits:id:*:ip:127.0.*:user_id:*
$visit = Visit::where('ip', '127.0.*')->first();
```

You may also use a string literal `'null'` as a search value to query for models where the attribute is `null`:

```
// SCAN ... MATCH visits:id:*:ip:null:user_id:*
$visit = Visit::where('ip', 'null')->first();
```

Searchable attribute values may be updated at any time on models. If they have been changed, the existing model instance is deleted in Redis, and a new one is saved automatically:

```
$visit = Visit::create(['user_id' => 1]);

// HDEL visits:id:f195637b-7d48-43ab-abab-86e93dfc9410:ip:127.0.0.1:user_id:1
// HSET visits:id:f195637b-7d48-43ab-abab-86e93dfc9410:ip:127.0.0.1:user_id:2
$visit->update(['user_id' => 2]);
```

Testing
-------

[](#testing)

To run your application tests without a real Redis server, you may swap the underlying repository to an array.

This will allow you to test with your models without needing to interact with Redis:

```
use DirectoryTree\ActiveRedis\Model;

// Pest
beforeAll(function () {
    Model::setRepository('array');
});

// PHPUnit
protected function setUp(): void
{
    parent::setUp();

    Model::setRepository('array');
}
```

Otherwise, you will need to run your tests with a Redis server running, and flush your Redis database after each test:

```
use Illuminate\Support\Facades\Redis;

// Pest
beforeEach(function () {
    Redis::flushdb();
});

// PHPUnit
protected function setUp(): void
{
    parent::setUp();

    Redis::flushdb();
}
```

Credits
-------

[](#credits)

This package is directly inspired from [Laravel's Eloquent](https://laravel.com/docs/eloquent), and most features are direct ports to a Redis equivalent.

I am forever grateful for the work [Taylor Otwell](https://github.com/taylorotwell) has produced.

If you can, support his work by purchasing a [sponsorship](https://github.com/sponsors/taylorotwell), or one of his many Laravel based services.

###  Health Score

41

—

FairBetter than 89% of packages

Maintenance59

Moderate activity, may be stable

Popularity25

Limited adoption so far

Community11

Small or concentrated contributor base

Maturity58

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 88% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~15 days

Recently: every ~69 days

Total

23

Last Release

263d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/475c93b20e68d9c282c8a8f2aeac74f936cc79d53a27bb654a3e689506013a42?d=identicon)[raphaelcangucu](/maintainers/raphaelcangucu)

---

Top Contributors

[![stevebauman](https://avatars.githubusercontent.com/u/6421846?v=4)](https://github.com/stevebauman "stevebauman (146 commits)")[![luizantunescods](https://avatars.githubusercontent.com/u/105310009?v=4)](https://github.com/luizantunescods "luizantunescods (9 commits)")[![raphaelcangucu](https://avatars.githubusercontent.com/u/5266252?v=4)](https://github.com/raphaelcangucu "raphaelcangucu (6 commits)")[![iksaku](https://avatars.githubusercontent.com/u/4632429?v=4)](https://github.com/iksaku "iksaku (4 commits)")[![J0sh0nat0r](https://avatars.githubusercontent.com/u/16763483?v=4)](https://github.com/J0sh0nat0r "J0sh0nat0r (1 commits)")

###  Code Quality

TestsPest

Code StyleLaravel Pint

### Embed Badge

![Health badge](/badges/raphaelcangucu-activeredis/health.svg)

```
[![Health](https://phpackages.com/badges/raphaelcangucu-activeredis/health.svg)](https://phpackages.com/packages/raphaelcangucu-activeredis)
```

###  Alternatives

[illuminate/database

The Illuminate Database package.

2.8k52.4M9.4k](/packages/illuminate-database)[spatie/laravel-backup

A Laravel package to backup your application

6.0k21.8M191](/packages/spatie-laravel-backup)[kreait/laravel-firebase

A Laravel package for the Firebase PHP Admin SDK

1.3k16.5M42](/packages/kreait-laravel-firebase)[laravel-doctrine/orm

An integration library for Laravel and Doctrine ORM

8425.3M87](/packages/laravel-doctrine-orm)[watson/validating

Eloquent model validating trait.

9723.3M47](/packages/watson-validating)[dyrynda/laravel-model-uuid

This package allows you to easily work with UUIDs in your Laravel models.

4802.8M8](/packages/dyrynda-laravel-model-uuid)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
