hyvor / laravel-json-meta
为 Eloquent 模型提供严格的元数据,存储在 JSON 列中
Requires
- php: ^8.3|^8.2
Requires (Dev)
- orchestra/testbench: ^9.0
- pestphp/pest: ^2
- phpstan/phpstan: ^1.10
- phpunit/phpunit: ^10
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
元数据还是列?
在保存数据时,您必须在元数据和列之间做出选择。一般规则是,如果这些数据需要用于 WHERE
或 ORDER 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库,它与本库有类似的概念,但将数据保存在单独的表中。
数据如何保存
当创建新的模型时,meta
是NULL
。元数据列只包含已更改的字段。例如,如果您将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
选项,您应该首先在元数据定义中删除它。
您仍将在数据库的元数据字段中保存旧数据,但这应该不会成问题,因为这些数据即使存在也不会被使用。
然而,如果您需要删除这些数据(例如,出于法律原因),您将需要一个类似于“更新元数据”部分的选项。再次,欢迎贡献。