elkadrey / eloquent-json-relations
Laravel Eloquent 关系与 JSON 键
资助包维护!
paypal.me/JonasStaudenmeir
Requires
- php: ^8.3|^8.2|^8.1
- illuminate/database: ^9.0|^10|^11
Requires (Dev)
README
简介
此 Laravel Eloquent 扩展为 BelongsTo
、HasOne
、HasMany
、HasOneThrough
、HasManyThrough
、MorphTo
、MorphOne
和 MorphMany
关系添加了对 JSON 外键的支持。
它还提供了具有 JSON 数组的 多对多 关系。
兼容性
安装
composer require "elkadrey/eloquent-json-relations:^1.1"
如果您在 Windows 的 PowerShell 中(例如在 VS Code 中),请使用此命令
composer require "elkadrey/eloquent-json-relations:^^^^1.1"
用法
一对一关系
在此示例中,User
与 Locale
有一个 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
特性。
参照完整性
在 MySQL、MariaDB 和 SQL 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,有一个 解决方案。
多对多关系
此包还引入了两种新的关系类型:BelongsToJson
和 HasManyJson
在 Laravel 5.6.25+ 上,您可以使用它们通过 JSON 数组实现多对多关系。
在此示例中,User
与 Role
有一个 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 });
贡献
请参阅 CONTRIBUTING 和 CODE OF CONDUCT 获取详细信息。