Can i use in a form a list checkboxex to show or hide other list of checkboxes

In the user model, I have a list of checkboxes that display roles, and another list of checkboxes that show values from a model called 'instruments'. What I'm trying to do is to have the list of instruments displayed when a certain role is selected, and not displayed otherwise. Some of this works, but when I save it, the values disappear even though they are present in the database. If I refresh the page, they appear as I show you in the following video. Code example
Section::make('Roles')->schema([
CheckboxList::make('roles')
->columnSpan('full')
->reactive()
->relationship('roles', 'name', function (Builder $query) {
if (! auth()->user()->hasRole('super_admin')) {
return $query->where('name', '<>', 'super_admin');
}
return $query;
})
->afterStateUpdated(function (Closure $set, Closure $get) {
$roles = $get('roles');
if ($roles !== null) {
$roleNames = Role::whereIn('id', $roles)->pluck('name')->toArray();
if (! in_array('musico', $roleNames)) {
$set('instruments', null);
}
} else {
$set('instruments', null);
}
})
->getOptionLabelFromRecordUsing(function ($record) {
return Str::of($record->name)->headline();
})->columns(4),
]),
Section::make('Instrumentos')
->when(function (Closure $get): bool {
$roles = $get('roles');
if ($roles !== null) {
$roleNames = Role::whereIn('id', $roles)->pluck('name')->toArray();
return in_array('musico', $roleNames);
}
return false;
})
->schema([
CheckboxList::make('instruments')
->columnSpan('full')
->reactive()
->relationship('instruments', 'name')
->getOptionLabelFromRecordUsing(function ($record) {
return Str::of($record->name)->headline();
})->columns(4),
])
Section::make('Roles')->schema([
CheckboxList::make('roles')
->columnSpan('full')
->reactive()
->relationship('roles', 'name', function (Builder $query) {
if (! auth()->user()->hasRole('super_admin')) {
return $query->where('name', '<>', 'super_admin');
}
return $query;
})
->afterStateUpdated(function (Closure $set, Closure $get) {
$roles = $get('roles');
if ($roles !== null) {
$roleNames = Role::whereIn('id', $roles)->pluck('name')->toArray();
if (! in_array('musico', $roleNames)) {
$set('instruments', null);
}
} else {
$set('instruments', null);
}
})
->getOptionLabelFromRecordUsing(function ($record) {
return Str::of($record->name)->headline();
})->columns(4),
]),
Section::make('Instrumentos')
->when(function (Closure $get): bool {
$roles = $get('roles');
if ($roles !== null) {
$roleNames = Role::whereIn('id', $roles)->pluck('name')->toArray();
return in_array('musico', $roleNames);
}
return false;
})
->schema([
CheckboxList::make('instruments')
->columnSpan('full')
->reactive()
->relationship('instruments', 'name')
->getOptionLabelFromRecordUsing(function ($record) {
return Str::of($record->name)->headline();
})->columns(4),
])
4 Replies
awcodes
awcodes12mo ago
At first glance I think the checkbox list shouldn’t have the same same as your relationship.
Patrick Boivin
Patrick Boivin12mo ago
@gonzalo2683 Can you share a bit more details on the relationships? How are things setup on the models?
Chaloman
Chaloman12mo ago
Sure @pboivin
class Instrument extends Model
{
use HasFactory;

protected $fillable = ['name'];

public function users(): BelongsToMany
{
return $this->belongsToMany(User::class);
}
}
class Instrument extends Model
{
use HasFactory;

protected $fillable = ['name'];

public function users(): BelongsToMany
{
return $this->belongsToMany(User::class);
}
}
User model, the roles come from the Spatie permissions package.
class User extends Authenticatable
{
use HasApiTokens, HasFactory, Notifiable, HasRoles;

//....

public function scopeDirector($query)
{
return $query->role('director');
}

public function scopeMusician($query)
{
return $query->role('musico');
}

public function instruments(): BelongsToMany
{
return $this->belongsToMany(Instrument::class);
}
}
class User extends Authenticatable
{
use HasApiTokens, HasFactory, Notifiable, HasRoles;

//....

public function scopeDirector($query)
{
return $query->role('director');
}

public function scopeMusician($query)
{
return $query->role('musico');
}

public function instruments(): BelongsToMany
{
return $this->belongsToMany(Instrument::class);
}
}
As @awcodes suggested, I've changed the field names to prevent conflicts with the relationships and it seems to work better now. The last issue I noticed is that when I uncheck the "musico" role and set the instruments to null, it still retains the instruments upon saving. For some reason, the field value is not cleared when using ->when(). I've tried commenting out the ->when() part and indeed the values are correctly unchecked. It seems that something happens when using it in conjunction with ->when().
Section::make('Roles')->schema([
CheckboxList::make('all_roles')
->columnSpan('full')
->reactive()
->relationship('roles', 'name', function (Builder $query) {
if (! auth()->user()->hasRole('super_admin')) {
return $query->where('name', '<>', 'super_admin');
}
return $query;
})
->afterStateUpdated(function (Closure $set, Closure $get) {
$roles = $get('all_roles');
if ($roles !== null) {
$roleNames = Role::whereIn('id', $roles)->pluck('name')->toArray();
if (! in_array('musico', $roleNames)) {
$set('all_instruments', null);
}
} else {
$set('all_instruments', null);
}
})
->getOptionLabelFromRecordUsing(function ($record) {
return Str::of($record->name)->headline();
})
->columns(4),
]),

Section::make('Instrumentos')
->when(function (Closure $get): bool {
$roles = $get('all_roles');
if ($roles !== null) {
$roleNames = Role::whereIn('id', $roles)->pluck('name')->toArray();
return in_array('musico', $roleNames);
}
return false;
})
->schema([
CheckboxList::make('all_instruments')
->columnSpan('full')
->reactive()
->relationship('instruments', 'name')
->getOptionLabelFromRecordUsing(function ($record) {
return Str::of($record->name)->headline();
})
->columns(4),
]),
Section::make('Roles')->schema([
CheckboxList::make('all_roles')
->columnSpan('full')
->reactive()
->relationship('roles', 'name', function (Builder $query) {
if (! auth()->user()->hasRole('super_admin')) {
return $query->where('name', '<>', 'super_admin');
}
return $query;
})
->afterStateUpdated(function (Closure $set, Closure $get) {
$roles = $get('all_roles');
if ($roles !== null) {
$roleNames = Role::whereIn('id', $roles)->pluck('name')->toArray();
if (! in_array('musico', $roleNames)) {
$set('all_instruments', null);
}
} else {
$set('all_instruments', null);
}
})
->getOptionLabelFromRecordUsing(function ($record) {
return Str::of($record->name)->headline();
})
->columns(4),
]),

Section::make('Instrumentos')
->when(function (Closure $get): bool {
$roles = $get('all_roles');
if ($roles !== null) {
$roleNames = Role::whereIn('id', $roles)->pluck('name')->toArray();
return in_array('musico', $roleNames);
}
return false;
})
->schema([
CheckboxList::make('all_instruments')
->columnSpan('full')
->reactive()
->relationship('instruments', 'name')
->getOptionLabelFromRecordUsing(function ($record) {
return Str::of($record->name)->headline();
})
->columns(4),
]),
awcodes
awcodes12mo ago
On my phone but you might be creating a race condition. Instead of a get in the all_instruments. I think you should just be setting it from the roles. The relationships should cascade down, at least in my head.