hyvor/laravel-json-meta

为 Eloquent 模型提供严格的元数据,存储在 JSON 列中

1.0.1 2024-05-02 16:55 UTC

This package is auto-updated.

Last update: 2024-09-02 17:54:38 UTC


README

一个用于在 JSON 列中保存元数据的库,使用 PHPStan 进行严格类型检查。

  • 最适合保存配置选项和其他元数据
  • 不支持嵌套对象

安装

通过 composer 安装此包。

composer require hyvor/laravel-json-meta

如果您使用 PHPStan(强烈推荐),请将以下内容添加到您的 phpstan.neon 文件中。这可以提高库的类型检查。

includes:
    - vendor/hyvor/laravel-json-meta/extension.neon

元数据还是列?

在保存数据时,您必须在元数据和列之间做出选择。一般规则是,如果这些数据需要用于 WHEREORDER BY,则将其保存到列中。如果不是,您可能将其保存到元数据中。在大多数情况下,配置选项是保存到元数据中的最佳选择。

(如果您的数据库引擎支持 JSON 操作,您仍然可以在 WHERE 查询中使用元数据。)

使用方法

假设您想将 blogs 的元数据保存到名为 meta 的 JSON 列中。

首先,将 JSON meta 列添加到表中。

Schema::create('blogs', function (Blueprint $table) {
    // other columns
    
    $table->json('meta')->nullable();
});

如果您的数据库不支持原生 JSON 列,Laravel 将创建一个 TEXT 列,该列与这个库配合工作得很好。

更新模型

  • 添加 HasMeta 特性
  • 声明 defineMeta 方法
use Hyvor\JsonMeta\HasMeta;
use Hyvor\JsonMeta\MetaDefinition;

class Blog extends Model
{
    
    use HasMeta;
    
    protected function defineMeta(MetaDefinition $meta) 
    {
        // definition goes here
    }

}

定义

"定义"是指您允许在 meta 字段中保存哪些数据类型。通过定义它们,您可以确保不会通过无效的用户输入或代码中的错误输入插入错误数据。在 MetaDefinition 类中提供了以下方法。

  • string(string $name)
  • integer(string $name)
  • float(string $name)
  • boolean(string $name)
  • enum(string $name, string[]|class-string $values)
protected function defineMeta(MetaDefinition $meta)
{

    // string
    $meta->string('seo_robots')->nullable();
    
    // integer
    $meta->integer('max_comments')->default(100);
    
    // float
    $meta->float('comment_delay')->default(0.5);
    
    // boolean
    $meta->boolean('seo_indexing')->default(true);
    
    // enum (string)
    $meta->enum('comments_type', ['hyvor', 'other'])->default('hyvor');
    
    // enum (PHP Enum)
    $meta->enum('comments_type', CommentType::class)->default(CommentType::HYVOR);
        
}

默认值

您应该为每个元数据设置一个默认值。

使用 default 方法设置默认值。值应与您定义的类型相同。

$meta->string('seo_robots')->default('index, follow');

或者,您可以使用 nullable 将默认值设置为 null

$meta->string('seo_robots')->nullable();

在上面的示例中,seo_robots 的类型现在是 string|null

方法

HasMeta 特性为模型添加了以下方法。

metaGet(string $name) : mixed

通过名称获取元值。参数 $name 应为在 metaDefinition 函数中定义的名称之一。如果未设置值,则返回默认值。

$seoIndexingOn = $blog->metaGet('seo_indexing');

metaGetAll() : array

获取所有元值。在 metaDefinition 函数中定义的所有键都将设置在返回的数组中,如果 meta 字段中缺失,则填充默认值。

$meta = $blog->metaGetAll();

if ($meta['seo_indexing']) {
    echo "Hey Google, Please index me!";
}

metaSet(string|array $name, mixed $value) : void

将元 $name 设置为 $value。除了通过 PHPStan 进行静态类型检查外,这里还进行了运行时类型检查,以防止保存无效值。

$blog->metaSet('seo_indexing', false);
// throws an error (wrong type)
$blog->metaSet('seo_indexing', 'no');

您还可以将数组作为第一个参数发送以更新多个值。

$blog->metaSet([
    'seo_indexing' => false,
    'comments_type' => CommentType::OTHER 
]);

类型

包含 PHPStan 扩展以获得更好的类型检查。

includes:
    - vendor/hyvor/laravel-json-meta/extension.neon

这注册了泛型实用类型 meta-of<Model>。您可以使用此类型轻松地获取元字段的类型作为常量数组。

/**
*  @param key-of<meta-of<Blog>> $key
*/
function handleMeta($key) {
    // $key is a key of the meta definition of Blog
}

为什么?

为什么要在 JSON 列中保存元数据?

让我们看看其他选项

情况 1:您可以将元数据保存到单独的列中。

  • 维护大量列是很困难的。您需要迁移来添加新的列。
  • 大多数情况下,用户不会更改默认值。因此,大多数列将是空的。最好只在它们更改时保存它们(这正是本库所做的事情)。请参阅下面的“数据如何保存”部分。
  • 数据库有最大行大小限制。在MYSQL中,大约是65KB。在Hyvor Talk上,我们接近这个限制,这也是我们开发这个库的主要原因之一。请注意,在这个库中,我们使用TEXT、BLOB和JSON列来保存元数据,这些列不计入行大小限制。

情况2:您可以将元数据保存在单独的表中。

这实际上是一个不错的选择。请查看laravel-meta库,它与本库有类似的概念,但将数据保存在单独的表中。

数据如何保存

当创建新的模型时,metaNULL。元数据列只包含已更改的字段。例如,如果您将seo_indexing设置为false,您将在meta列中看到以下JSON

{
  "seo_indexing": false
}

这样,您将在数据库中节省很多空间。

添加新的元数据

假设您想要添加一个名为seo_follow_external_links的新元数据。这项任务相当简单。您只需将其添加到定义中即可。

use Hyvor\JsonMeta\MetaDefinition;

public function defineMeta(MetaDefinition $meta) 
{
    // old definitions
    
    // the new one
    $meta->boolean('seo_follow_external_links')->default(false);
   
}

更新元数据

更新稍微有些复杂。假设您想要将seo_indexing重命名为seo_indexing_on。您可以更新定义,但问题在于数据库中已保存的值。

您如何更新这个

{
  "seo_indexing": false
}

到这个

{
  "seo_indexing_on": false
}

一个选项是使用数据库中的JSON操作。或者,使用自定义脚本。目前,这个库不提供这个功能。但如果未来需要,我们将添加一个命令来完成这项任务。(欢迎贡献/想法)

删除元数据

如果您需要从您的应用程序中删除seo_indexing选项,您应该首先在元数据定义中删除它。

您仍将在数据库的元数据字段中保存旧数据,但这应该不会成问题,因为这些数据即使存在也不会被使用。

然而,如果您需要删除这些数据(例如,出于法律原因),您将需要一个类似于“更新元数据”部分的选项。再次,欢迎贡献。