Skip to content

Commit

Permalink
v6.48.0
Browse files Browse the repository at this point in the history
  • Loading branch information
spawnia committed Jan 9, 2025
1 parent fe23e87 commit 2f0cd99
Show file tree
Hide file tree
Showing 8 changed files with 309 additions and 115 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ You can find and compare releases at the [GitHub release page](https://github.co

## Unreleased

## v6.48.0

### Added

- Add `@bind` directive as a GraphQL analogue for Laravel's Route Model Binding https://github.com/nuwave/lighthouse/pull/2645
Expand Down
151 changes: 151 additions & 0 deletions docs/6/api-reference/directives.md
Original file line number Diff line number Diff line change
Expand Up @@ -460,6 +460,157 @@ type RoleEdge {
}
```

## @bind

```graphql
"""
Replace argument values with the corresponding model (or some other value) before passing them to the resolver.
For example, instead of injecting a user's ID, you can inject the entire User model instance that matches the given ID.
This eliminates the need to manually query for the instance inside the resolver.
This works analogues to [Laravel's Route Model Binding](https://laravel.com/docs/routing#route-model-binding).
"""
directive @bind(
"""
Specify the fully qualified class name of the binding to use.
This can be either an Eloquent model, or a class that defines a method `__invoke` that resolves the value.
"""
class: String!

"""
Specify the column name of a unique identifier to use when binding Eloquent models.
By default, "id" is used as the primary key column.
"""
column: String! = "id"

"""
Specify the relations to eager-load when binding Eloquent models.
"""
with: [String!]! = []

"""
Specify whether the binding should be considered required.
When set to `true`, a validation error will be thrown if the value (or any of the list values) can not be resolved.
The field resolver will not be invoked in this case.
When set to `false`, argument values that can not be resolved will be passed to the resolver as `null`.
When the argument is a list, individual values that can not be resolved will be filtered out.
"""
required: Boolean! = true
) on ARGUMENT_DEFINITION | INPUT_FIELD_DEFINITION
```

### Basic usage

```graphql
type Mutation {
addUserToCompany(
user: ID! @bind(class: "App\\Models\\User")
company: ID! @bind(class: "App\\Models\\Company")
): Boolean!
}
```

```php
namespace App\GraphQL\Mutations;

final class AddUserToCompany
{
/**
* @param array{
* user: \App\Models\User,
* company: \App\Models\Company,
* } $args
*/
public function __invoke(mixed $root, array $args): bool
{
$user = $args['user'];
$user->associate($args['company']);

return $user->save();
}
}
```

### Binding instances that are not Eloquent models

To bind instances that are not Eloquent models, callable classes can be used instead:

```graphql
type Mutation {
updateCompanyInfo(
company: ID! @bind(class: "App\\Http\\GraphQL\\Bindings\\CompanyBinding")
): Boolean!
}
```

```php
namespace App\GraphQL\Bindings;

use App\External\Company;
use App\External\CompanyRepository;
use Nuwave\Lighthouse\Bind\BindDefinition;

final class CompanyBinding
{
public function __construct(
private CompanyRepository $companyRepository,
) {}

public function __invoke(string $value, BindDefinition $definition): ?Company
{
if ($definition->required) {
return $this->companyRepository->findOrFail($value);
}

return $this->companyRepository->find($value);
}
}
```

### Binding a collection of instances

When the `@bind` directive is defined on an argument or input field with an array value,
it can be used to resolve a collection of instances.

```graphql
type Mutation {
addUsersToCompany(
users: [ID!]! @bind(class: "App\\Models\\User")
company: ID! @bind(class: "App\\Models\\Company")
): [User!]!
}
```

```php
namespace App\GraphQL\Mutations;

use App\Models\User;

final class AddUsersToCompany
{
/**
* @param array{
* users: \Illuminate\Database\Eloquent\Collection<\App\Models\User>,
* company: \App\Models\Company,
* } $args
*
* @return \Illuminate\Database\Eloquent\Collection<\App\Models\User>
*/
public function __invoke(mixed $root, array $args): Collection
{
return $args['users']
->map(function (User $user) use ($args): ?User {
$user->associate($args['company']);

return $user->save()
? $user
: null;
})
->filter();
}
}
```

## @broadcast

```graphql
Expand Down
69 changes: 34 additions & 35 deletions docs/master/api-reference/directives.md
Original file line number Diff line number Diff line change
Expand Up @@ -464,51 +464,49 @@ type RoleEdge {

```graphql
"""
Automatically inject (model) instances directly into a resolver's arguments. For example, instead of
injecting a user's ID, you can inject the entire User model instance that matches the given ID.
Replace argument values with the corresponding model (or some other value) before passing them to the resolver.
For example, instead of injecting a user's ID, you can inject the entire User model instance that matches the given ID.
This eliminates the need to manually query for the instance inside the resolver.
This is a GraphQL analogue for Laravel's Route Binding.
This works analogues to [Laravel's Route Model Binding](https://laravel.com/docs/routing#route-model-binding).
"""
directive @bind(
"""
Specify the class name of the binding to use. This can be either an Eloquent
model or callable class to bind any other instance than a model.
Specify the fully qualified class name of the binding to use.
This can be either an Eloquent model, or a class that defines a method `__invoke` that resolves the value.
"""
class: String!

"""
Specify the column name of a unique identifier to use when binding Eloquent models.
By default, "id" is used the the primary key column.
By default, "id" is used as the primary key column.
"""
column: String! = "id"

"""
Specify the relations to eager-load when binding Eloquent models.
"""
with: [String!]! = []

"""
Specify whether the binding should be considered required. When required, a validation error will be thrown for
the argument or any item in the argument (when the argument is an array) for which a binding instance could not
be resolved. The field resolver will not be invoked in this case. When optional, the argument value will resolve
as null or, when the argument is an array, any item in the argument value will be filtered out of the collection.
Specify whether the binding should be considered required.
When set to `true`, a validation error will be thrown if the value (or any of the list values) can not be resolved.
The field resolver will not be invoked in this case.
When set to `false`, argument values that can not be resolved will be passed to the resolver as `null`.
When the argument is a list, individual values that can not be resolved will be filtered out.
"""
required: Boolean! = true
) on ARGUMENT_DEFINITION | INPUT_FIELD_DEFINITION
```

Resolver model binding (like [Route Model Binding](https://laravel.com/docs/routing#route-model-binding)) allows to
inject instances directly into a resolver's arguments instead of the provided scalar identifier, eliminating the need to
manually query for the instance inside the resolver.

### Basic usage

```graphql
type Mutation {
addUserToCompany(
user: ID! @bind(class: "App\\Models\\User")
company: ID! @bind(class: "App\\Models\\Company")
): Boolean!
addUserToCompany(
user: ID! @bind(class: "App\\Models\\User")
company: ID! @bind(class: "App\\Models\\Company")
): Boolean!
}
```

Expand All @@ -519,29 +517,29 @@ final class AddUserToCompany
{
/**
* @param array{
* user: \App\Models\User,
* user: \App\Models\User,
* company: \App\Models\Company,
* } $args
*/
public function __invoke(mixed $root, array $args): bool
{
$user = $args['user'];
$user->associate($args['company']);

return $user->save();
}
}
```

### Binding instances that are not Eloquent models
### Binding instances that are not Eloquent models

To bind instances that are not Eloquent models, callable classes can be used instead:

```graphql
type Mutation {
updateCompanyInfo(
company: ID! @bind(class: "App\\Http\\GraphQL\\Bindings\\CompanyBinding")
): Boolean!
updateCompanyInfo(
company: ID! @bind(class: "App\\Http\\GraphQL\\Bindings\\CompanyBinding")
): Boolean!
}
```

Expand All @@ -557,29 +555,29 @@ final class CompanyBinding
public function __construct(
private CompanyRepository $companyRepository,
) {}

public function __invoke(string $value, BindDefinition $definition): ?Company
{
if ($definition->required) {
return $this->companyRepository->findOrFail($value);
}

return $this->companyRepository->find($value);
}
}
```

### Binding a collection of instances

When the `@bind` directive is defined on an argument or input field with an array value, it can be used to resolve a
collection of instances.
When the `@bind` directive is defined on an argument or input field with an array value,
it can be used to resolve a collection of instances.

```graphql
type Mutation {
addUsersToCompany(
users: [ID!]! @bind(class: "App\\Models\\User")
company: ID! @bind(class: "App\\Models\\Company")
): [User!]!
addUsersToCompany(
users: [ID!]! @bind(class: "App\\Models\\User")
company: ID! @bind(class: "App\\Models\\Company")
): [User!]!
}
```

Expand All @@ -595,6 +593,7 @@ final class AddUsersToCompany
* users: \Illuminate\Database\Eloquent\Collection<\App\Models\User>,
* company: \App\Models\Company,
* } $args
*
* @return \Illuminate\Database\Eloquent\Collection<\App\Models\User>
*/
public function __invoke(mixed $root, array $args): Collection
Expand Down
28 changes: 15 additions & 13 deletions src/Bind/BindDirective.php
Original file line number Diff line number Diff line change
Expand Up @@ -34,34 +34,36 @@ public static function definition(): string
{
return /** @lang GraphQL */ <<<'GRAPHQL'
"""
Automatically inject (model) instances directly into a resolver's arguments. For example, instead of
injecting a user's ID, you can inject the entire User model instance that matches the given ID.
Replace argument values with the corresponding model (or some other value) before passing them to the resolver.
For example, instead of injecting a user's ID, you can inject the entire User model instance that matches the given ID.
This eliminates the need to manually query for the instance inside the resolver.
This is a GraphQL analogue for Laravel's Route Binding.
This works analogues to [Laravel's Route Model Binding](https://laravel.com/docs/routing#route-model-binding).
"""
directive @bind(
"""
Specify the class name of the binding to use. This can be either an Eloquent
model or callable class to bind any other instance than a model.
Specify the fully qualified class name of the binding to use.
This can be either an Eloquent model, or a class that defines a method `__invoke` that resolves the value.
"""
class: String!
"""
Specify the column name of a unique identifier to use when binding Eloquent models.
By default, "id" is used the the primary key column.
By default, "id" is used as the primary key column.
"""
column: String! = "id"
"""
Specify the relations to eager-load when binding Eloquent models.
"""
with: [String!]! = []
"""
Specify whether the binding should be considered required. When required, a validation error will be thrown for
the argument or any item in the argument (when the argument is an array) for which a binding instance could not
be resolved. The field resolver will not be invoked in this case. When optional, the argument value will resolve
as null or, when the argument is an array, any item in the argument value will be filtered out of the collection.
Specify whether the binding should be considered required.
When set to `true`, a validation error will be thrown if the value (or any of the list values) can not be resolved.
The field resolver will not be invoked in this case.
When set to `false`, argument values that can not be resolved will be passed to the resolver as `null`.
When the argument is a list, individual values that can not be resolved will be filtered out.
"""
required: Boolean! = true
) on ARGUMENT_DEFINITION | INPUT_FIELD_DEFINITION
Expand Down
Loading

0 comments on commit 2f0cd99

Please sign in to comment.