Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add @hasOneThrough directive #2585

Merged
merged 26 commits into from
Jul 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
a8e25ba
ADD HasOneThroughDirective
saeed-rostami Jul 16, 2024
94650bb
ADD HasOneThroughDirective
saeed-rostami Jul 16, 2024
037aba8
ADD relations property on models
saeed-rostami Jul 16, 2024
d61a7e3
ADD models use namespace
saeed-rostami Jul 16, 2024
44e8f75
ADD |null to @property-read on models for phpstan error
saeed-rostami Jul 16, 2024
f369c54
ADD |null to @property-read on models for phpstan error
saeed-rostami Jul 16, 2024
b4faa61
ADD |null to @property-read on models for phpstan error
saeed-rostami Jul 16, 2024
80536cf
ADD |null to @property-read on models for phpstan error
saeed-rostami Jul 16, 2024
24afb8c
ADD |null to @property-read on models for phpstan error
saeed-rostami Jul 16, 2024
5762a21
ADD |null to @property-read on models for phpstan error
saeed-rostami Jul 16, 2024
f139a85
ADD |null to @property-read on models for phpstan error
saeed-rostami Jul 16, 2024
6a6daa5
CHANGE TEST WITH EXISTING MODELS
saeed-rostami Jul 18, 2024
f7187fc
CHANGE TEST WITH EXISTING MODELS
saeed-rostami Jul 18, 2024
9b7c7a0
CHANGE TEST WITH EXISTING MODELS
saeed-rostami Jul 18, 2024
d25dd41
FIX ISSUES...
saeed-rostami Jul 18, 2024
8cae703
FIX ISSUES...
saeed-rostami Jul 18, 2024
501a7a9
FIX ISSUES...
saeed-rostami Jul 18, 2024
bc5a1d3
FIX ISSUES...
saeed-rostami Jul 18, 2024
27fb0d0
clean up migrations
spawnia Jul 19, 2024
09827c3
changelog
spawnia Jul 19, 2024
7435602
docs
spawnia Jul 19, 2024
27bcb33
avoid useless directive
spawnia Jul 19, 2024
5e5fd54
clean up test setup
spawnia Jul 19, 2024
b29412f
clean up test setup
spawnia Jul 19, 2024
c60810c
fix tests
spawnia Jul 19, 2024
0f67441
format
spawnia Jul 19, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,12 @@ You can find and compare releases at the [GitHub release page](https://github.co

## Unreleased

## v6.42.0

### Added

- Add `@hasOneThrough` directive https://github.com/nuwave/lighthouse/pull/2585

## v6.41.1

### Fixed
Expand Down
39 changes: 37 additions & 2 deletions docs/6/api-reference/directives.md
Original file line number Diff line number Diff line change
Expand Up @@ -1738,7 +1738,7 @@ type User {
}
```

If the name of the relationship on the Eloquent model is different than the field name,
If the name of the relationship on the Eloquent model differs from the field name,
you can override it by setting `relation`.

```graphql
Expand Down Expand Up @@ -1841,7 +1841,7 @@ type User {
}
```

If the name of the relationship on the Eloquent model is different than the field name,
If the name of the relationship on the Eloquent model differs from the field name,
you can override it by setting `relation`.

```graphql
Expand All @@ -1850,6 +1850,41 @@ type User {
}
```

## @hasOneThrough

```graphql
"""
Corresponds to [the Eloquent relationship HasOneThrough](https://laravel.com/docs/eloquent-relationships#has-one-through).
"""
directive @hasOneThrough(
"""
Specify the relationship method name in the model class,
if it is named different from the field in the schema.
"""
relation: String

"""
Apply scopes to the underlying query.
"""
scopes: [String!]
) on FIELD_DEFINITION
```

```graphql
type Mechanic {
carOwner: Owner! @hasOneThrough
}
```

If the name of the relationship on the Eloquent model differs from the field name,
you can override it by setting `relation`.

```graphql
type Mechanic {
carOwner: Owner! @hasOneThrough(relation: "owner")
}
```

## @in

```graphql
Expand Down
10 changes: 10 additions & 0 deletions docs/6/eloquent/relationships.md
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,16 @@ type Role {
}
```

## Has One Through

Use the [@hasOneThrough](../api-reference/directives.md#hasonethrough) directive to define a [has-one-through relationship](https://laravel.com/docs/eloquent-relationships#has-one-through).

```graphql
type Mechanic {
carOwner: Owner! @hasOneThrough
}
```

## Has Many Through

Use the [@hasManyThrough](../api-reference/directives.md#hasmanythrough) directive to define a [has-many-through relationship](https://laravel.com/docs/eloquent-relationships#has-many-through).
Expand Down
39 changes: 37 additions & 2 deletions docs/master/api-reference/directives.md
Original file line number Diff line number Diff line change
Expand Up @@ -1738,7 +1738,7 @@ type User {
}
```

If the name of the relationship on the Eloquent model is different than the field name,
If the name of the relationship on the Eloquent model differs from the field name,
you can override it by setting `relation`.

```graphql
Expand Down Expand Up @@ -1841,7 +1841,7 @@ type User {
}
```

If the name of the relationship on the Eloquent model is different than the field name,
If the name of the relationship on the Eloquent model differs from the field name,
you can override it by setting `relation`.

```graphql
Expand All @@ -1850,6 +1850,41 @@ type User {
}
```

## @hasOneThrough

```graphql
"""
Corresponds to [the Eloquent relationship HasOneThrough](https://laravel.com/docs/eloquent-relationships#has-one-through).
"""
directive @hasOneThrough(
"""
Specify the relationship method name in the model class,
if it is named different from the field in the schema.
"""
relation: String

"""
Apply scopes to the underlying query.
"""
scopes: [String!]
) on FIELD_DEFINITION
```

```graphql
type Mechanic {
carOwner: Owner! @hasOneThrough
}
```

If the name of the relationship on the Eloquent model differs from the field name,
you can override it by setting `relation`.

```graphql
type Mechanic {
carOwner: Owner! @hasOneThrough(relation: "owner")
}
```

## @in

```graphql
Expand Down
10 changes: 10 additions & 0 deletions docs/master/eloquent/relationships.md
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,16 @@ type Role {
}
```

## Has One Through

Use the [@hasOneThrough](../api-reference/directives.md#hasonethrough) directive to define a [has-one-through relationship](https://laravel.com/docs/eloquent-relationships#has-one-through).

```graphql
type Mechanic {
carOwner: Owner! @hasOneThrough
}
```

## Has Many Through

Use the [@hasManyThrough](../api-reference/directives.md#hasmanythrough) directive to define a [has-many-through relationship](https://laravel.com/docs/eloquent-relationships#has-many-through).
Expand Down
27 changes: 27 additions & 0 deletions src/Schema/Directives/HasOneThroughDirective.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<?php declare(strict_types=1);

namespace Nuwave\Lighthouse\Schema\Directives;

class HasOneThroughDirective extends RelationDirective
{
public static function definition(): string
{
return /** @lang GraphQL */ <<<'GRAPHQL'
"""
Corresponds to [the Eloquent relationship HasOneThrough](https://laravel.com/docs/eloquent-relationships#has-one-through).
"""
directive @hasOneThrough(
"""
Specify the relationship method name in the model class,
if it is named different from the field in the schema.
"""
relation: String

"""
Apply scopes to the underlying query.
"""
scopes: [String!]
) on FIELD_DEFINITION
GRAPHQL;
}
}
61 changes: 61 additions & 0 deletions tests/Integration/Schema/Directives/HasOneThroughDirectiveTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
<?php declare(strict_types=1);

namespace Tests\Integration\Schema\Directives;

use Tests\DBTestCase;
use Tests\Utils\Models\Post;
use Tests\Utils\Models\PostStatus;

final class HasOneThroughDirectiveTest extends DBTestCase
{
public function testQueryHasOneThroughRelationship(): void
{
$this->schema = /** @lang GraphQL */ '
type Query {
tasks: [Task!]! @all
}

type Task {
id: ID!
postStatus: PostStatus @hasOneThrough
}

type PostStatus {
id: ID!
status: String
}
';

$post = factory(Post::class)->create();
assert($post instanceof Post);

$postStatus = factory(PostStatus::class)->make();
assert($postStatus instanceof PostStatus);
$postStatus->post()->associate($post);
$postStatus->save();

$this->graphQL(/** @lang GraphQL */ '
{
tasks {
id
postStatus {
id
status
}
}
}
')->assertExactJson([
'data' => [
'tasks' => [
[
'id' => (string) $post->task->id,
'postStatus' => [
'id' => (string) $postStatus->id,
'status' => $postStatus->status,
],
],
],
],
]);
}
}
69 changes: 40 additions & 29 deletions tests/Utils/Models/Post.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\Relations\HasOne;
use Illuminate\Database\Eloquent\Relations\MorphMany;
use Illuminate\Database\Eloquent\Relations\MorphToMany;
use Illuminate\Database\Eloquent\SoftDeletes;
Expand All @@ -28,43 +29,53 @@
* @property int|null $user_id
* @property int $task_id
* @property int|null $parent_id
* @property-read \Tests\Utils\Models\User $user
*
* Relations
* @property-read \Illuminate\Database\Eloquent\Collection<\Tests\Utils\Models\Activity> $activity
* @property-read \Illuminate\Database\Eloquent\Collection<\Tests\Utils\Models\Comment> $comments
* @property-read \Tests\Utils\Models\Task $task
* @property-read \Tests\Utils\Models\Post $parent
* @property-read \Illuminate\Database\Eloquent\Collection<\Tests\Utils\Models\Category> $categories
* @property-read \Illuminate\Database\Eloquent\Collection<\Tests\Utils\Models\Post> $children
* @property-read \Illuminate\Database\Eloquent\Collection<\Tests\Utils\Models\Tag> $tags
* @property-read \Illuminate\Database\Eloquent\Collection<\Tests\Utils\Models\Comment> $comments
* @property-read \Illuminate\Database\Eloquent\Collection<\Tests\Utils\Models\Image> $images
* @property-read \Tests\Utils\Models\Post|null $parent
* @property-read \Illuminate\Database\Eloquent\Collection<\Tests\Utils\Models\RoleUserPivot> $roles
* @property-read \Tests\Utils\Models\PostStatus|null $status
* @property-read \Illuminate\Database\Eloquent\Collection<\Tests\Utils\Models\Tag> $tags
* @property-read \Tests\Utils\Models\Task $task
* @property-read \Tests\Utils\Models\User|null $user
*/
final class Post extends Model
{
use Searchable;
use SoftDeletes;

/** @return \Illuminate\Database\Eloquent\Relations\BelongsTo<\Tests\Utils\Models\User, self> */
public function user(): BelongsTo
{
return $this->belongsTo(User::class);
}

/** @return \Illuminate\Database\Eloquent\Relations\MorphMany<\Tests\Utils\Models\Activity> */
public function activity(): MorphMany
{
return $this->morphMany(Activity::class, 'content');
}

/** @return \Illuminate\Database\Eloquent\Relations\BelongsToMany<\Tests\Utils\Models\Category> */
public function categories(): BelongsToMany
{
return $this->belongsToMany(Category::class, 'category_post', 'category_id', 'post_id');
}

/** @return \Illuminate\Database\Eloquent\Relations\HasMany<self> */
public function children(): HasMany
{
return $this->hasMany(self::class);
}

/** @return \Illuminate\Database\Eloquent\Relations\HasMany<\Tests\Utils\Models\Comment> */
public function comments(): HasMany
{
return $this->hasMany(Comment::class);
}

/** @return \Illuminate\Database\Eloquent\Relations\BelongsTo<\Tests\Utils\Models\Task, self> */
public function task(): BelongsTo
/** @return \Illuminate\Database\Eloquent\Relations\MorphMany<\Tests\Utils\Models\Image> */
public function images(): MorphMany
{
return $this->belongsTo(Task::class);
return $this->morphMany(Image::class, 'imageable');
}

/** @return \Illuminate\Database\Eloquent\Relations\BelongsTo<self, self> */
Expand All @@ -73,33 +84,33 @@ public function parent(): BelongsTo
return $this->belongsTo(self::class);
}

/** @return \Illuminate\Database\Eloquent\Relations\HasMany<self> */
public function children(): HasMany
/** @return \Illuminate\Database\Eloquent\Relations\HasMany<\Tests\Utils\Models\RoleUserPivot> */
public function roles(): HasMany
{
return $this->hasMany(self::class);
return $this->hasMany(RoleUserPivot::class, 'user_id', 'user_id');
}

/** @return \Illuminate\Database\Eloquent\Relations\MorphToMany<\Tests\Utils\Models\Tag> */
public function tags(): MorphToMany
/** @return \Illuminate\Database\Eloquent\Relations\HasOne<\Tests\Utils\Models\PostStatus> */
public function status(): HasOne
{
return $this->morphToMany(Tag::class, 'taggable');
return $this->hasOne(PostStatus::class);
}

/** @return \Illuminate\Database\Eloquent\Relations\MorphMany<\Tests\Utils\Models\Image> */
public function images(): MorphMany
/** @return \Illuminate\Database\Eloquent\Relations\BelongsTo<\Tests\Utils\Models\Task, self> */
public function task(): BelongsTo
{
return $this->morphMany(Image::class, 'imageable');
return $this->belongsTo(Task::class);
}

/** @return \Illuminate\Database\Eloquent\Relations\BelongsToMany<\Tests\Utils\Models\Category> */
public function categories(): BelongsToMany
/** @return \Illuminate\Database\Eloquent\Relations\MorphToMany<\Tests\Utils\Models\Tag> */
public function tags(): MorphToMany
{
return $this->belongsToMany(Category::class, 'category_post', 'category_id', 'post_id');
return $this->morphToMany(Tag::class, 'taggable');
}

/** @return \Illuminate\Database\Eloquent\Relations\HasMany<\Tests\Utils\Models\RoleUserPivot> */
public function roles(): HasMany
/** @return \Illuminate\Database\Eloquent\Relations\BelongsTo<\Tests\Utils\Models\User, self> */
public function user(): BelongsTo
{
return $this->hasMany(RoleUserPivot::class, 'user_id', 'user_id');
return $this->belongsTo(User::class);
}
}
Loading
Loading