json-api-server/docs/writing.md

4.2 KiB

Field Writability

By default, fields are read-only. You can allow a field to be written to in PATCH and POST requests using the writable and readonly methods.

You can optionally supply a closure to these methods which will receive the model instance, and should return a boolean value.

For example, the following schema will make an email attribute that is only writable by the self:

$type->attribute('email')
    ->writable(function ($model, Request $request, Attribute $field) {
        return $model->id === $request->getAttribute('userId');
    });

Writable Once

You may want a field to only be writable when creating a new resource, but not when an existing resource is being updated. This can be achieved by calling the once method:

$type->hasOne('author')
    ->writable()->once();

Default Values

You can provide a default value to be used when creating a new resource if there is no value provided by the consumer. Pass a value or a closure to the default method:

$type->attribute('joinedAt')
    ->default(new DateTime);

$type->attribute('ipAddress')
    ->default(function (Request $request, Attribute $attribute) {
        return $request->getServerParams()['REMOTE_ADDR'] ?? null;
    });

::: tip If you're using Eloquent, you could also define default attribute values to achieve a similar thing. However, the Request instance will not be available in this context. :::

Validation

You can ensure that data provided for a field is valid before the resource is saved. Provide a closure to the validate method, and call the first argument if validation fails:

$type->attribute('email')
    ->validate(function (callable $fail, $value, $model, Request $request, Attribute $attribute) {
        if (! filter_var($value, FILTER_VALIDATE_EMAIL)) {
            $fail('Invalid email');
        }
    });

::: tip You can easily use Laravel's Validation component for field validation with the rules helper function. :::

This works for relationships, too. The related models will be retrieved via your adapter and passed into your validation function.

$type->hasMany('groups')
    ->validate(function (callable $fail, array $groups, $model, Request $request, Attribute $attribute) {
        foreach ($groups as $group) {
            if ($group->id === 1) {
                $fail('You cannot assign this group');
            }
        }
    });

Transformers

Use the transform method on an attribute to mutate any incoming value before it is saved to the model.

$type->attribute('firstName')
    ->transform(function ($value, Request $request, Attribute $attribute) {
        return ucfirst($value);
    });

::: tip If you're using Eloquent, you could also define attribute casts or mutators on your model to achieve a similar thing. :::

Setters

Use the set method to define custom mutation logic for your field, instead of just setting the value straight on the model property.

$type->attribute('firstName')
    ->set(function ($value, $model, Request $request, Attribute $attribute) {
        $model->first_name = ucfirst($value);
        if ($model->first_name === 'Toby') {
            $model->last_name = 'Zerner';
        }
    });

Savers

If your field corresponds to some other form of data storage rather than a simple property on your model, you can use the save method to provide a closure that will be run after your model has been successfully saved. If specified, the adapter will NOT be used to set the field on the model.

$type->attribute('locale')
    ->save(function ($value, $model, Request $request, Attribute $attribute) {
        $model->preferences()
            ->where('key', 'locale')
            ->update(['value' => $value]);
    });

Events

onSaved

Run after a field has been successfully saved.

$type->attribute('email')
    ->onSaved(function ($value, $model, Request $request, Attribute $attribute) {
        event(new EmailWasChanged($model));
    });