directorytree/activeredis
Active Record-style Redis hash models for Laravel. Create, update, delete, expire, and query Redis-backed records with an Eloquent-like API, including model identifiers, timestamps, casts, events, connections, chunking, searching, and testing support.
Installation:
composer require directorytree/activeredis
Ensure your config/database.php has a dedicated Redis connection for ActiveRedis (recommended).
Define a Model:
namespace App\Redis;
use DirectoryTree\ActiveRedis\Model;
class Visit extends Model {}
Create a Record:
$visit = Visit::create([
'ip' => '192.168.1.1',
'url' => 'https://example.com',
]);
Query a Record:
$visit = Visit::find('f195637b-7d48-43ab-abab-86e93dfc9410');
Track User Visits:
// Create a visit record
$visit = Visit::create([
'ip' => request()->ip(),
'url' => request()->url(),
'user_agent' => request()->userAgent(),
]);
// Later, retrieve all visits
$visits = Visit::query()->get();
app/Redis/Visit.php (or your custom model file).config/database.php (Redis connection).$searchable).booted() method.$model = ModelName::create(['key' => 'value']);
$model = ModelName::find('id');
// Or query all
$models = ModelName::all();
$model->update(['key' => 'new_value']);
// Or
$model->key = 'new_value';
$model->save();
$model->delete();
// Or bulk delete
ModelName::destroy(['id1', 'id2']);
$model = ModelName::create(['key' => 'value']);
$model->setExpiry(now()->addHours(1));
if ($model->getExpiry()) {
// Model will expire at $model->getExpiry()
}
class Visit extends Model {
protected array $searchable = ['ip', 'url'];
}
$results = ModelName::search('ip', '192.168.1.1')->get();
ModelName::chunk(100, function ($models) {
foreach ($models as $model) {
// Process each model
}
});
getNewKey() for non-UUID keys:
protected function getNewKey(): string {
return Str::random(20);
}
config/database.php for ActiveRedis to avoid scanning unrelated keys (e.g., cache, sessions).'activeredis' => [
'url' => env('REDIS_URL'),
'database' => 10, // Isolated database
],
// app/Observers/VisitObserver.php
class VisitObserver {
public function created(Visit $visit) {
Log::info("New visit recorded: {$visit->ip}");
}
}
AppServiceProvider:
Visit::observe(VisitObserver::class);
class Visit extends Model {
protected array $casts = [
'user_id' => 'integer',
'is_bot' => 'boolean',
'metadata' => 'json',
];
}
$recentVisits = Visit::where('created_at', '>', now()->subHours(1))->get();
ActiveRedis facade to mock Redis in tests:
use DirectoryTree\ActiveRedis\Facades\ActiveRedis;
public function test_model_creation() {
ActiveRedis::shouldReceive('hSet')->once();
$model = ModelName::create(['key' => 'value']);
$this->assertEquals('value', $model->key);
}
id vs ID).// Fails to find the model if case doesn't match
$model = ModelName::find('CUSTOM-ID'); // Returns null if stored as 'custom-id'
$searchable after records exist will break searches. Plan schema early.SCAN does not guarantee exact batch sizes. Use each() with a callback to handle variability:
ModelName::each(function ($model) {
// Process model
}, 100); // Batch size hint
force: true in create() or save() deletes existing records silently. Document this behavior in your code.// Deletes existing record if it exists
ModelName::create(['id' => '123'], force: true);
null unexpectedly. Handle gracefully:
$model = ModelName::find('id');
if (!$model) {
// Handle expired/deleted model
}
:) and asterisks (*) in custom keys or searchable values. Redis uses these for patterns.redis-cli to inspect keys:
redis-cli KEYS "visits:id:*"
redis-cli TTL "visits:id:f195637b-7d48-43ab-abab-86e93dfc9410"
AppServiceProvider:
use DirectoryTree\ActiveRedis\Facades\ActiveRedis;
public function boot() {
ActiveRedis::setLogger(function ($query) {
Log::debug('Redis Query:', ['query' => $query]);
});
}
findOrFail() for API responses to return 404:
$model = ModelName::findOrFail($id);
return response()->json($model);
getNewKey() for testing to avoid UUID collisions:
protected function getNewKey(): string {
return 'test-key-' . time();
}
class Visit extends Model {
public function scopeRecent($query, $hours = 24) {
return $query->where('created_at', '>', now()->subHours($hours));
}
}
$recent = Visit::recent()->get();
DirectoryTree\ActiveRedis\Connection class to add custom commands:
namespace App\Redis;
use DirectoryTree\ActiveRedis\Connection;
class CustomConnection extends Connection {
public function
How can I help you explore Laravel packages today?