elkadrey/eloquent-json-relations

Laravel Eloquent 关系与 JSON 键

1.7.1 2024-09-29 07:34 UTC

This package is auto-updated.

Last update: 2024-09-29 07:34:45 UTC


README

CI Code Coverage Scrutinizer Code Quality Latest Stable Version Total Downloads License

简介

此 Laravel Eloquent 扩展为 BelongsToHasOneHasManyHasOneThroughHasManyThroughMorphToMorphOneMorphMany 关系添加了对 JSON 外键的支持。
它还提供了具有 JSON 数组的 多对多 关系。

兼容性

安装

composer require "elkadrey/eloquent-json-relations:^1.1"

如果您在 Windows 的 PowerShell 中(例如在 VS Code 中),请使用此命令

composer require "elkadrey/eloquent-json-relations:^^^^1.1"

用法

一对一关系

在此示例中,UserLocale 有一个 BelongsTo 关系。没有专用列,但外键(locale_id)作为属性存储在 JSON 字段(users.options)中

class User extends Model
{
    use \Staudenmeir\EloquentJsonRelations\HasJsonRelationships;

    protected $casts = [
        'options' => 'json',
    ];

    public function locale()
    {
        return $this->belongsTo(Locale::class, 'options->locale_id');
    }
}

class Locale extends Model
{
    use \Staudenmeir\EloquentJsonRelations\HasJsonRelationships;

    public function users()
    {
        return $this->hasMany(User::class, 'options->locale_id');
    }
}

请记住在父模型和相关模型中都使用 HasJsonRelationships 特性。

参照完整性

MySQLMariaDBSQL Server 上,您仍然可以通过在生成/计算列上使用外键来确保参照完整性。

Laravel 迁移在 MySQL/MariaDB 上支持此功能

Schema::create('users', function (Blueprint $table) {
    $table->bigIncrements('id');
    $table->json('options');
    $locale_id = DB::connection()->getQueryGrammar()->wrap('options->locale_id');
    $table->unsignedBigInteger('locale_id')->storedAs($locale_id);
    $table->foreign('locale_id')->references('id')->on('locales');
});

Laravel 迁移(5.7.25+)也在 SQL Server 上支持此功能

Schema::create('users', function (Blueprint $table) {
    $table->bigIncrements('id');
    $table->json('options');
    $locale_id = DB::connection()->getQueryGrammar()->wrap('options->locale_id');
    $locale_id = 'CAST('.$locale_id.' AS INT)';
    $table->computed('locale_id', $locale_id)->persisted();
    $table->foreign('locale_id')->references('id')->on('locales');
});

对于较旧版本的 Laravel,有一个 解决方案

多对多关系

此包还引入了两种新的关系类型:BelongsToJsonHasManyJson

在 Laravel 5.6.25+ 上,您可以使用它们通过 JSON 数组实现多对多关系。

在此示例中,UserRole 有一个 BelongsToMany 关系。没有中间表,但外键作为数组存储在 JSON 字段(users.options)中

ID 数组

默认情况下,关系将中继记录作为 ID 数组存储

class User extends Model
{
    use \Staudenmeir\EloquentJsonRelations\HasJsonRelationships;

    protected $casts = [
       'options' => 'json',
    ];
    
    public function roles()
    {
        return $this->belongsToJson(Role::class, 'options->role_ids');
    }
}

class Role extends Model
{
    use \Staudenmeir\EloquentJsonRelations\HasJsonRelationships;

    public function users()
    {
       return $this->hasManyJson(User::class, 'options->role_ids');
    }
}

BelongsToJson 关系的侧面,您可以使用 attach()detach()sync()toggle()

$user = new User;
$user->roles()->attach([1, 2])->save(); // Now: [1, 2]

$user->roles()->detach([2])->save();    // Now: [1]

$user->roles()->sync([1, 3])->save();   // Now: [1, 3]

$user->roles()->toggle([2, 3])->save(); // Now: [1, 2]

对象数组

您还可以将中继记录作为具有附加属性的对象存储

class User extends Model
{
    use \Staudenmeir\EloquentJsonRelations\HasJsonRelationships;

    protected $casts = [
       'options' => 'json',
    ];
    
    public function roles()
    {
        return $this->belongsToJson(Role::class, 'options->roles[]->role_id');
    }
}

class Role extends Model
{
    use \Staudenmeir\EloquentJsonRelations\HasJsonRelationships;

    public function users()
    {
       return $this->hasManyJson(User::class, 'options->roles[]->role_id');
    }
}

在这里,options->roles 是 JSON 数组的路径。 role_id 是记录对象内部外键属性名称

$user = new User;
$user->roles()->attach([1 => ['active' => true], 2 => ['active' => false]])->save();
// Now: [{"role_id":1,"active":true},{"role_id":2,"active":false}]

$user->roles()->detach([2])->save();
// Now: [{"role_id":1,"active":true}]

$user->roles()->sync([1 => ['active' => false], 3 => ['active' => true]])->save();
// Now: [{"role_id":1,"active":false},{"role_id":3,"active":true}]

$user->roles()->toggle([2 => ['active' => true], 3])->save();
// Now: [{"role_id":1,"active":false},{"role_id":2,"active":true}]

限制:在 SQLite 和 SQL Server 上,这些关系仅部分工作。

查询性能

在 PostgreSQL 上,您可以通过 jsonb 列和 GIN 索引来提高查询性能。

当 ID/对象数组本身是列时(例如 users.role_ids),请使用此迁移

Schema::create('users', function (Blueprint $table) {
    $table->bigIncrements('id');
    $table->jsonb('role_ids');
    $table->index('role_ids')->algorithm('gin');
});

当数组嵌套在对象内部时(例如 users.options->role_ids),请使用此迁移

Schema::create('users', function (Blueprint $table) {
    $table->bigIncrements('id');
    $table->jsonb('options');
    $table->rawIndex('("options"->\'role_ids\')', 'users_options_index')->algorithm('gin'); // Laravel 7.10.3+
    //$table->index([DB::raw('("options"->\'role_ids\')')], 'users_options_index', 'gin');  // Laravel < 7.10.3
});

贡献

请参阅 CONTRIBUTINGCODE OF CONDUCT 获取详细信息。