laravel
laravel copied to clipboard
Question: How do i create a write-only field?
The question boils down to this: How do i create a write-only field?
Right now I have a field catachall that should be write-only for a super user. But that field seems to be writable no matter what i do. Even if I force always readonly() or change the field type f.e. to Str, it still allows creation of the resource.
MailAddressSchema.php
public function fields(): array
{
return [
//..
Boolean::make('catchall')
->sortable()
->readOnly(static fn ($request): bool => !$request->user()->isSuperUser()),
//..
];
}
MailAddressResource.php
public function attributes($request): iterable
{
$attributes = [
//..
'name' => $this->name,
//..
];
if ($request->user()->isSuperUser()) {
$attributes['catchall'] = $this->catchall;
}
return $attributes;
}
MailAddressRequest.php
class MailAddressRequest extends ResourceRequest
{
/**
* Get the validation rules for the resource.
*
* @return array
*/
public function rules(): array
{
return [
//..
'name' => ['required', 'string', 'max:255'],
//..
'catchall' => 'nullable|boolean',
];
}
}
Policies
Policies allow all actions on this resource, which is what i want.
- laravel-json-api/laravel: 2.3.0
- laravel/framework: 8.83.9
I've 'fixed' this by using this rule (but i wonder if this is the right way):
public function rules(): array
{
$user = Auth::user();
return [
\\...
'catchall' => $user && $user->isSuperUser() ? 'nullable|boolean' : 'prohibited',
\\...
];
}
Thanks for raising this. I think your solution is a good one for now.
There's actually two things going on here:
- you're only serializing the attribute if the user is a super user
- you only want to fill the attribute with a value if the user is a super user.
So the field itself isn't really "write-only", it has conditional visibility and is conditionally written. I'll need to think about whether there's a better way of implementing this.
FYI the "conditional visibility" is definitely already support via a resource class, which I see you've written. Your MailAddressResource class could be a lot simpler if you make use of conditional attributes: https://laraveljsonapi.io/docs/2.0/resources/attributes.html#conditional-attributes
Leaving a note to myself...
We currently have fillUsing() on the fields to control field hydration. We could perhaps add fillWhen() and fillUnless() methods that take a callback like readOnly(), which would allow something like this:
public function fields(): array
{
return [
//..
Boolean::make('catchall')
->sortable()
->fillWhen(static fn ($request): bool => $request->user()->isSuperUser()),
//..
];
}
@preliot just checked the docs and you can actually already do conditional visibility of the field via the hidden() method. Here's the docs:
https://laraveljsonapi.io/docs/2.0/schemas/attributes.html#hiding-fields
And here's an example that would work in your use case:
Boolean::make('catchall')->hidden(
static fn($request) => !$request->user()->isSuperUser()
)
But this would mean the field:
- is not writable for ! su users (needed)
- not readable for ! su users (unwanted side effect).
@preliot sorry, but I'm not following how this wouldn't meet your use case.
Not readable by users who are not super users is covered by this (existing functionality):
Boolean::make('catchall')->hidden(
static fn($request) => !$request->user()->isSuperUser()
)
As that hides the field if the user is not a super user.
Not writable by users who are not super users would be covered by this (future functionality):
Boolean::make('catchall')
->fillWhen(static fn ($request): bool => $request->user()->isSuperUser()),
As that would make it fillable only if the user is a super user.
If this doesn't meet your use-case, can you provide a lot more clarity as to why it wouldn't?
Now i understand, you're talking about future functionaliy. I was under the impression it should be possible somehow now.