Selfupdating / On-The-Fly / Dynamic Modal Form on Resources (Tutorial)
Hey - so this is a follow-up to #Selfupdating Modal-Form and an attempt to better state the problem and provide the found solution for future use.
The problem:
Let's say you have to create an Action on a Resource (table) but the required form-entries (like which Inputs are required, which selects, or whatnot) depend on the record or a relation on the record you are working on. (I'm using the EditAction for this, but this should work on any other action as well!)
In my scenario the form-content relied on a model called "Config" and to add method to the madness, a secondary model we call "Parent". In here we call the model that the Resource displays just "Entity", to avoid confusion. So Entity belongs to Parent via parent_id. Config belongs to parent via parent_id. Entity and Config are linked via a BelongsToMany-relationship with a pivot-value called "value", which holds the set value. Now we have a Select on our form that set's the parent_id for Entity and we want the form to display all Config-models with the same parent_id as our select currently uses as selected value and update itself whenever the selected value (parent_id) changes.
The following thread will provide the solution to this, which has been found with extensive help of @pboivin and a pointer in the right direction by @smiliyas. Thank you guys. π
Let's say you have to create an Action on a Resource (table) but the required form-entries (like which Inputs are required, which selects, or whatnot) depend on the record or a relation on the record you are working on. (I'm using the EditAction for this, but this should work on any other action as well!)
In my scenario the form-content relied on a model called "Config" and to add method to the madness, a secondary model we call "Parent". In here we call the model that the Resource displays just "Entity", to avoid confusion. So Entity belongs to Parent via parent_id. Config belongs to parent via parent_id. Entity and Config are linked via a BelongsToMany-relationship with a pivot-value called "value", which holds the set value. Now we have a Select on our form that set's the parent_id for Entity and we want the form to display all Config-models with the same parent_id as our select currently uses as selected value and update itself whenever the selected value (parent_id) changes.
The following thread will provide the solution to this, which has been found with extensive help of @pboivin and a pointer in the right direction by @smiliyas. Thank you guys. π
Solution:Jump to solution
So that's it. Just add those 3 funtions after
BUT I HAVE TO WARN YOU:
- First: This solution works nicely, but any model-policy you set on Entity is completely ignored! So those won't help you! I strongly advise to add a visible-function to your EditAction, so only users that have the appropiate authority can change anything. In my app I'm using it like this:
EditAction::make()
and you have yourself a selfupdating modal-form.BUT I HAVE TO WARN YOU:
- First: This solution works nicely, but any model-policy you set on Entity is completely ignored! So those won't help you! I strongly advise to add a visible-function to your EditAction, so only users that have the appropiate authority can change anything. In my app I'm using it like this:
->visible(fn() => Auth::user()->hasRole(['Admin', 'SuperAdmin']))
- Second: Remember that everytime the Select for parent_id is changed, a request get's fired to your database. There is a theoretical attack-surface if you have a large configs-table and one does inject some JavaScript to rapidly change said select! So use this solution with causion....6 Replies
So the first thing you have to understand is that what we are doing here is "Dependent Fields" on steroids. Some basic info can be found here: https://filamentphp.com/docs/2.x/forms/advanced#dependant-fields--components
Next up is that we're using 3 functions on the
First up is
In our solution it looks like this:
What's happening here is that we load our Entity-model, get the configs-BelongsToMany-relationship and load the currently set pivot values. If non are set - dynamicConfigs just stays an empty array. IMPORTANT: You have to keep the variable named
EditAction::make()
-function.->form()
, ->mountUsing()
and ->action()
.
Let's look at all 3 of them individually.First up is
->form()
to setup our form - the code provided even considers parent_id beeing null on Entity. The found solution looks like this:
We need to take a closer look on TextInput::make('dynamicConfigs.'$config->id.'.value')->label($config->label);
. The 'dynamicConfigs.'$config->id.'.value'
-part is actually important for the action()
function later. With this notation you setup the structure needed to use Laravel's ->sync()
-function. If you don't have a pivot-table and just a plain hasMany, you can use 'dynamicConfigs.'$config->id
to just store anything dynamic in a dynamicConfigs-array.
Now we need to look at ->mountUsing()
.In our solution it looks like this:
What's happening here is that we load our Entity-model, get the configs-BelongsToMany-relationship and load the currently set pivot values. If non are set - dynamicConfigs just stays an empty array. IMPORTANT: You have to keep the variable named
$record
! Your Entity-instance is bound on that variablename (not sure if by Filament or Livewire).
Last up is the ->action()
-function. Here we grab what we have done before and store it inside the database:
I think that should be pretty straight forward. πSolution
So that's it. Just add those 3 funtions after
BUT I HAVE TO WARN YOU:
- First: This solution works nicely, but any model-policy you set on Entity is completely ignored! So those won't help you! I strongly advise to add a visible-function to your EditAction, so only users that have the appropiate authority can change anything. In my app I'm using it like this:
EditAction::make()
and you have yourself a selfupdating modal-form.BUT I HAVE TO WARN YOU:
- First: This solution works nicely, but any model-policy you set on Entity is completely ignored! So those won't help you! I strongly advise to add a visible-function to your EditAction, so only users that have the appropiate authority can change anything. In my app I'm using it like this:
->visible(fn() => Auth::user()->hasRole(['Admin', 'SuperAdmin']))
- Second: Remember that everytime the Select for parent_id is changed, a request get's fired to your database. There is a theoretical attack-surface if you have a large configs-table and one does inject some JavaScript to rapidly change said select! So use this solution with causion.Hey @husky110, you can post this tutorial in https://filamentphp.com/tricks
Filament
Tricks - Filament
Filament is a collection of tools for rapidly building beautiful TALL stack apps, designed for humans.
see pm π
Thanks for the write-up @husky110, really interesting!