Eloquent directives¶
Build a CRUD API over Eloquent with SDL directives — no resolver code, and the
model stays the single source of truth. Directives are auto-registered; the
model is taken from a model: argument or the type name (convention
config('graphql.models.namespace'), default App\Models).
type Query {
users: [User!]! @all
user(id: ID!): User @find
posts: [Post!]! @paginate @whereConditions(columns: ["title"]) @orderBy(columns: ["id", "title"])
}
type Mutation {
createUser(name: String!, email: String!): User @create
updateUser(id: ID!, name: String): User @update
deleteUser(id: ID!): User @delete
}
type User {
id: ID!
name: String
posts: [Post!]! @hasMany
postsCount: Int @count(relation: "posts")
}
type Post {
id: ID!
title: String
author: User @belongsTo(relation: "user")
}
Reading¶
| Directive | Effect |
|---|---|
@all |
all records of the model |
@find |
one record constrained by the field arguments |
@first |
first record matching the arguments |
@paginate(type: PAGINATOR\|CONNECTION) |
paginated list; generates the paginator/connection type and adds first/page (or first/after) arguments |
Relations¶
@hasMany, @hasOne, @belongsTo, @belongsToMany, @morphMany/@morphOne/@morphTo
resolve the Eloquent relation named after the field (override with relation:).
@count(relation:) resolves a relation count.
Filtering & sorting¶
@whereConditions(columns: [...])adds awhere: [...]argument. Each condition is{ column, operator, value }with operatorsEQ/NEQ/GT/GTE/LT/LTE/LIKE/IN/NOT_IN. Columns are restricted to the declared allow-list (a generated enum).@orderBy(columns: [...])adds anorderBy: [{ column, order }]argument (ASC/DESC).
Both compose with @all and @paginate, which apply them to the query.
Single-argument filters¶
For simple filters, put a directive directly on an argument — the column defaults to
the argument name (override with key:). These apply to @all/@paginate queries:
type Query {
posts(
title: String @like(key: "title")
authorId: ID @eq
tags: [String!] @in(key: "tag")
minViews: Int @scope(name: "popular")
limit: Int @limit
): [Post!]! @all
}
Available: @eq, @neq, @in, @notIn, @like, @whereBetween, @whereNull,
@scope(name:) (applies an Eloquent scope with the value) and @limit.
Writing¶
@create, @update, @delete, @upsert — resolved from the field arguments, each in
a database transaction. @update/@delete/@upsert locate the record by id (override
with key:). For soft-delete models, @forceDelete permanently deletes and @restore
brings back a trashed record.
Configuration¶
'models' => ['namespace' => 'App\\Models'],
'pagination' => ['default_count' => 15, 'max_count' => 100],
Search, auth & utilities¶
@search(by:)— full-text search via Laravel Scout (requireslaravel/scout).@guard— require an authenticated user;@inject(context:, name:)— inject a context value (e.g. the user id) into an argument.@field(resolver:)— bind a custom resolver class;@rename(attribute:)— read a differently-named source attribute.
The graphql:print Artisan command prints the built schema as SDL
(Hmennen90\GraphQL\Engine\Schema\SchemaPrinter).
Validation & sanitisers¶
Argument directives — @rules, @validator, @trim, @hash, @globalId — validate
or sanitise input before the resolver runs and compose with the mutation directives.
See Validation & argument sanitisers.
Code-first attributes¶
Every field directive has a PHP attribute equivalent under Hmennen90\GraphQL\Attributes
that dispatches to the same implementation — no duplicated logic:
use Hmennen90\GraphQL\Attributes\{All, Find, First, Paginate, Guard, Count, Rename, Relation};
#[GraphQLType(name: 'Query')]
final class QueryType
{
#[GraphQLField(type: '[User!]!')]
#[All(model: User::class)]
public function users(): array { return []; }
#[GraphQLField(type: '[Post!]!')]
#[Paginate(type: 'CONNECTION')]
public function posts(): array { return []; }
}
Build with the directive registry so the attributes resolve:
$directives = app(\Hmennen90\GraphQL\Directives\DirectiveRegistry::class)->all();
$types = (new AttributeSchemaBuilder($directives))->build([QueryType::class, UserType::class]);
#[Relation(type: "hasMany", relation: "posts")] covers the relation directives.
Apollo Federation¶
Any schema can be exposed as a federated subgraph via Federation::subgraph(). See
Apollo Federation.