Skip to content

Commit

Permalink
Document relationship concatenation and HasManyThroughJson relationships
Browse files Browse the repository at this point in the history
  • Loading branch information
staudenmeir committed Nov 19, 2022
1 parent 2b2ec91 commit 0cc3a99
Showing 1 changed file with 112 additions and 12 deletions.
124 changes: 112 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@
[![License](https://poser.pugx.org/staudenmeir/eloquent-json-relations/license)](https://packagist.org/packages/staudenmeir/eloquent-json-relations)

## Introduction
This Laravel Eloquent extension adds support for JSON foreign keys to `BelongsTo`, `HasOne`, `HasMany`, `HasOneThrough`, `HasManyThrough`, `MorphTo`, `MorphOne` and `MorphMany` relationships.

This Laravel Eloquent extension adds support for JSON foreign keys to `BelongsTo`, `HasOne`, `HasMany`, `HasOneThrough`
, `HasManyThrough`, `MorphTo`, `MorphOne` and `MorphMany` relationships.
It also provides [many-to-many](#many-to-many-relationships) relationships with JSON arrays.

## Compatibility
Expand All @@ -30,15 +32,18 @@ Use this command if you are in PowerShell on Windows (e.g. in VS Code):
## Usage

- [One-To-Many Relationships](#one-to-many-relationships)
- [Referential Integrity](#referential-integrity)
- [Referential Integrity](#referential-integrity)
- [Many-To-Many Relationships](#many-to-many-relationships)
- [Array of IDs](#array-of-ids)
- [Array of Objects](#array-of-objects)
- [Query Performance](#query-performance)
- [Array of IDs](#array-of-ids)
- [Array of Objects](#array-of-objects)
- [Query Performance](#query-performance)
- [Has-Many-Through Relationships](#has-many-through-relationships)
- [Concatenation](#concatenation)

### One-To-Many Relationships

In this example, `User` has a `BelongsTo` relationship with `Locale`. There is no dedicated column, but the foreign key (`locale_id`) is stored as a property in a JSON field (`users.options`):
In this example, `User` has a `BelongsTo` relationship with `Locale`. There is no dedicated column, but the foreign
key (`locale_id`) is stored as a property in a JSON field (`users.options`):

```php
class User extends Model
Expand Down Expand Up @@ -70,7 +75,10 @@ Remember to use the `HasJsonRelationships` trait in both the parent and the rela

#### Referential Integrity

On [MySQL](https://dev.mysql.com/doc/refman/en/create-table-foreign-keys.html), [MariaDB](https://mariadb.com/kb/en/library/foreign-keys/) and [SQL Server](https://docs.microsoft.com/en-us/sql/relational-databases/tables/specify-computed-columns-in-a-table) you can still ensure referential integrity with foreign keys on generated/computed columns.
On [MySQL](https://dev.mysql.com/doc/refman/en/create-table-foreign-keys.html)
, [MariaDB](https://mariadb.com/kb/en/library/foreign-keys/)
and [SQL Server](https://docs.microsoft.com/en-us/sql/relational-databases/tables/specify-computed-columns-in-a-table)
you can still ensure referential integrity with foreign keys on generated/computed columns.

Laravel migrations support this feature on MySQL/MariaDB:

Expand All @@ -84,7 +92,7 @@ Schema::create('users', function (Blueprint $table) {
});
```

Laravel migrations (5.7.25+) also support this feature on SQL Server:
Laravel migrations (5.7.25+) also support this feature on SQL Server:

```php
Schema::create('users', function (Blueprint $table) {
Expand All @@ -97,15 +105,17 @@ Schema::create('users', function (Blueprint $table) {
});
```

There is a [workaround](https://github.com/staudenmeir/eloquent-json-relations/tree/1.1#referential-integrity) for older versions of Laravel.
There is a [workaround](https://github.com/staudenmeir/eloquent-json-relations/tree/1.1#referential-integrity) for older
versions of Laravel.

### Many-To-Many Relationships

The package also introduces two new relationship types: `BelongsToJson` and `HasManyJson`

On Laravel 5.6.25+, you can use them to implement many-to-many relationships with JSON arrays.

In this example, `User` has a `BelongsToMany` relationship with `Role`. There is no pivot table, but the foreign keys are stored as an array in a JSON field (`users.options`):
In this example, `User` has a `BelongsToMany` relationship with `Role`. There is no pivot table, but the foreign keys
are stored as an array in a JSON field (`users.options`):

#### Array of IDs

Expand Down Expand Up @@ -180,7 +190,8 @@ class Role extends Model
}
```

Here, `options->roles` is the path to the JSON array. `role_id` is the name of the foreign key property inside the record object:
Here, `options->roles` is the path to the JSON array. `role_id` is the name of the foreign key property inside the
record object:

```php
$user = new User;
Expand All @@ -201,7 +212,8 @@ $user->roles()->toggle([2 => ['active' => true], 3])->save();

#### Query Performance

On PostgreSQL, you can improve the query performance with `jsonb` columns and [`GIN` indexes](https://www.postgresql.org/docs/current/datatype-json.html#JSON-INDEXING).
On PostgreSQL, you can improve the query performance with `jsonb` columns
and [`GIN` indexes](https://www.postgresql.org/docs/current/datatype-json.html#JSON-INDEXING).

Use this migration when the array of IDs/objects is the column itself (e.g. `users.role_ids`):

Expand All @@ -224,6 +236,94 @@ Schema::create('users', function (Blueprint $table) {
});
```

### Has-Many-Through Relationships

Similar to Laravel's [`HasManyThrough`](https://laravel.com/docs/9.x/eloquent-relationships#has-many-through), you can
define `HasManyThroughJson` relationship when the JSON column is in the intermediate table (Laravel 9+). This
requires [staudenmeir/eloquent-has-many-deep](https://github.com/staudenmeir/eloquent-has-many-deep).

Consider a relationship between `Role` and `Project` through `User`:

`Role` → has many JSON → `User` → has many `Project`

[Install](https://github.com/staudenmeir/eloquent-has-many-deep/#installation) the additional package, add the
`HasRelationships` trait to the parent (first) model and pass the JSON column as a `JsonKey` object:

```php
class Role extends Model
{
use \Staudenmeir\EloquentHasManyDeep\HasRelationships;

public function projects()
{
return $this->hasManyThroughJson(
Project::class, User::class, new JsonKey('options->role_ids')
);
}
}
```

The reverse relationship would look like this:

```php
class Project extends Model
{
use \Staudenmeir\EloquentHasManyDeep\HasRelationships;

public function roles()
{
return $this->hasManyThroughJson(
Role::class, User::class, 'id', 'id', 'user_id', new JsonKey('options->role_ids')
);
}
}
```

### Concatenation

You can include JSON relationships into deep relationships by concatenating them with other relationships
using [staudenmeir/eloquent-has-many-deep](https://github.com/staudenmeir/eloquent-has-many-deep) (Laravel 9+).

Consider a relationship between `User` and `Permission` through `Role`:

`User` → belongs to JSON → `Role` → has many → `Permission`

[Install](https://github.com/staudenmeir/eloquent-has-many-deep/#installation) the additional package, add the
`HasRelationships` trait to the parent (first) model
and [define](https://github.com/staudenmeir/eloquent-has-many-deep/#concatenating-existing-relationships) a
deep relationship:

```php
class User extends Model
{
use \Staudenmeir\EloquentHasManyDeep\HasRelationships;
use \Staudenmeir\EloquentJsonRelations\HasJsonRelationships;

public function permissions()
{
return $this->hasManyDeepFromRelations(
$this->roles(),
(new Role)->permissions()
);
}

public function roles()
{
return $this->belongsToJson(Role::class, 'options->role_ids');
}
}

class Role extends Model
{
public function permissions()
{
return $this->hasMany(Permission::class);
}
}

$permissions = User::find($id)->permissions;
```

## Contributing

Please see [CONTRIBUTING](.github/CONTRIBUTING.md) and [CODE OF CONDUCT](.github/CODE_OF_CONDUCT.md) for details.

0 comments on commit 0cc3a99

Please sign in to comment.