Fields
fields() lists the columns a request may select with fields[<key>]:
public function fields(): array
{
return ['id', 'name', 'email', 'profile.bio', Field::virtual('display_name', dependsOn: ['name', 'nickname'])];
}GET /users?fields[users]=id,nameWithout fields[...] all columns are selected.
Keys
fields[<key>] takes a comma-separated list. A key is one of:
- the resource type of the root (
fields[users]), or no key at all (fields=name); - an include path (
fields[posts],fields[posts.comments]); - the type of a nested definition (
fields[posts]reaches every pathPostQueryis attached to).
A key that is an include path and a type at once (the relation posts described by PostQuery of type posts) is checked against that path, and it also narrows the other included paths of the type that the request gives no key of their own: fields[posts]=title narrows latestPost as well, unless fields[latestPost]=... is given too.
A dotted field in fields() (profile.bio) allows a field of a relation that has no definition of its own.
Reaching one path by two different keys (for example fields=... together with fields[users]=...) is rejected with ambiguous_fields.
In a cyclic definition the type of the root also reaches the nested paths of that type (fields[users] narrows posts.author); a nested path with a key of its own (fields[posts.author]=id) takes that key instead.
The names under a key are its own fields: fields[users]=users.name is name, while a name qualified by another resource (fields[users]=posts.title) is an unknown_field error rather than the root's title. Fields of a relation go under its own key (fields[posts]=title).
Keys are always selected
The primary key of the root and of every relation with a fieldset is always selected, as well as the keys every included relation needs. They are ordinary attributes of the loaded models, so hide them in your API resources if they must not be exposed.
Virtual fields
Field::virtual('display_name', dependsOn: ['name', 'nickname']) is a field that is not a column. Requesting it selects its dependencies, and when the model has an accessor for it (displayName() returning an Attribute, or getDisplayNameAttribute()), the field is appended to the loaded models, so toArray() and a plain JsonResource return it.
The virtual name itself never reaches the SELECT. Without an accessor, compute the value in your resource from the dependencies.