目录树/activeredis

Redis的ActiveRecord实现。

dev-master 2024-09-23 14:37 UTC

This package is auto-updated.

Last update: 2024-09-23 14:37:50 UTC


README

使用散列在Laravel中为Redis实现Active Record。

ActiveRedis使用Redis散列来存储和检索模型数据,为Laravel中与Redis的交互提供了一种简单高效的方式。

索引

需求

  • PHP >= 8.1
  • Predis >= 2.0
  • Laravel >= 9.0

安装

您可以通过composer安装此包

composer require directorytree/activeredis

使用

创建模型

要开始使用,定义一个新的ActiveRedis模型

namespace App\Redis;

use DirectoryTree\ActiveRedis\Model;

class Visit extends Model {}

然后,创建具有所需数据的模型

重要

分配给模型属性的值始终以字符串的形式存储在Redis中。

use App\Redis\Visit;

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

这将创建一个新的Redis散列,其格式如下

{plural_model_name}:{key_attribute}:{key_value}

例如

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

按照您在模型实例上预期的方式访问属性

重要

属性值始终以字符串的形式存储在Redis中。如果您想保留值的类型,请使用模型转换

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

模型标识符

在创建模型时,ActiveRedis将为每个模型自动生成一个UUID,分配给id属性

$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"

如果您愿意,也可以提供自己的ID

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

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

尝试创建具有现有ID的模型将导致DuplicateKeyException异常

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

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

同样,尝试创建具有空ID的模型将抛出InvalidKeyException异常

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

要更改存储模型键的字段名称,可以重写key属性

namespace App\Redis;

use DirectoryTree\ActiveRedis\Model;

class Visit extends Model
{
    protected string $key = 'custom_key';
}

如果您不提供,ActiveRedis将始终在键的属性中生成一个新的UUID。

要更改此行为或生成自己的唯一键,可以重写getNewKey()方法

重要

不要使用冒号(:)或星号(*)生成键。它们是Redis中的保留字符。

这也适用于定义为可查询的属性。

namespace App\Redis;

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

class Visit extends Model
{
    // ...
    
    protected function getNewKey(): string
    {
        return Str::orderedUuid();
    }
}

模型时间戳

模型还将维护created_atupdated_at属性

重要

当访问时,时间戳属性将以Carbon实例的形式返回。

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

要仅更新模型的updated_at时间戳,可以调用touch()方法

$visit->touch();

您还可以提供要触摸的时间戳属性

$visit->touch('created_at');

要禁用时间戳,可以重写timestamps属性并将其设置为false

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

如果您需要自定义用于存储时间戳的列的名称,可以在模型上定义CREATED_ATUPDATED_AT常量

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

模型转换

要将模型属性转换为特定类型,可以在模型上定义casts属性

class Metric extends Model
{
    protected array $casts = [
        'user_id' => 'integer',
        'authenticated' => 'boolean',
    ];
}

当您访问属性时,它将转换为指定的类型

$metric = new Metric([
    'user_id' => '1',
    'authenticated' => '1',
]);

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

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

以下是所有支持的转换列表

  • json
  • date
  • array
  • string
  • object
  • decimal
  • timestamp
  • collection
  • int|integer
  • bool|boolean
  • real|float|double
  • datetime|custom_datetime
  • immutable_date|immutable_custom_datetime|immutable_datetime

还有可用的枚举转换

use App\Enums\MetricCategory;

class Metric extends Model
{
    protected array $casts = [
        'category' => MetricCategory::class,
    ];
}

更新模型

您可以使用update()方法更新模型

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

或者通过设置模型属性并调用save()方法

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

$visit->save();

删除模型

要删除模型,请在模型实例上使用delete()方法

$visit->delete();

或者,您也可以通过它们的ID来删除模型

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

echo $deleted; // 1

您可以通过提供ID数组来删除多个模型

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

echo $deleted; // 2

过期模型

要使模型在一段时间后过期,请使用setExpiry()方法

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

5分钟后,该模型将自动从Redis中删除。

查询模型

查询模型使用Redis的SCAN命令来遍历模型哈希集中的所有键。

例如,当查询Visit模型时,使用模式visits:id:*

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

要开始查询模型,您可以在模型上调用query()方法

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

这将遍历模型哈希集中的所有键,并返回匹配该模式的模型集合。

缺少的模型方法将被转发到查询构建器,因此如果您喜欢,可以在模型上动态调用查询方法。

$visits = Visit::get();

分块

您可以使用chunk()方法分块查询结果

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

或调用each方法

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

在回调中返回false可以停止分块查询

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

过滤

在尝试过滤模型之前,您必须定义您想要在模型上查询的属性

class Visit extends Model
{
    protected array $queryable = ['ip'];
}

当您定义这些属性时,它们将以以下格式作为哈希键的一部分进行存储

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

例如

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

当定义多个可查询属性时,它们将按字母顺序存储。例如

class Metric extends Model
{
    protected array $queryable = ['user_id', 'company_id'];
}

// ...

$metric = Metric::create([
    'user_id' => 1,
    'company_id' => 1,
]);

$metric->getHashKey(); // "metrics:id:{uuid}:company_id:1:user_id:1"

一旦定义了可查询属性,您就可以使用where()方法开始查询它们

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

您还可以在where子句中使用通配符进行通配搜索

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

检索模型

要检索模型,请使用find方法

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

如果您希望在找不到模型时抛出异常,请使用findOrFail方法

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