Skip to main content
Tutorial Free

Filament: Edit Only Single Record with Custom Page

August 26, 2023
4 min read

Filament is great for creating full CRUDs in Resources. But what if you want to create just a single Edit form for one specific record, without the full Resource? Let's use a Custom Page for it.

In this example, we will use a company that belongs to a user.

The idea comes from the question on the official Filament Discord:

Here's the result of what we're gonna be building.

edit company page


Prepare Custom Page

The main thing you need to understand that it's sometimes easier to create a Custom Page with just the actions you want than to create a full Filament Resource and then try to "strip it down".

Also, Filament comes with separated packages, so you can use Filament Forms/Tables even outside of Filament, in Livewire components.

So first, create a custom page and prepare it to use Filament Forms.

php artisan make:filament-page EditCompany --type=custom

Notice: when creating a custom page, don't specify any associated Resource.

Now, we need to implement an interface and add a trait to use Filament forms.

app/Filament/Pages/EditCompany.php:

use Filament\Forms\Contracts\HasForms;
use Filament\Forms\Concerns\InteractsWithForms;
 
class EditCompany extends Page implements HasForms
{
use InteractsWithForms;
}

Custom page now can use Filament Forms, so let's add a form.

app/Filament/Pages/EditCompany.php:

use Filament\Forms\Form;
use Filament\Forms\Components\TextInput;
use Filament\Forms\Concerns\InteractsWithForms;
 
class EditCompany extends Page implements HasForms
{
use InteractsWithForms;
 
public ?array $data = [];
 
protected static ?string $navigationIcon = 'heroicon-o-document-text';
 
protected static string $view = 'filament.pages.edit-company';
 
public function mount(): void
{
$this->form->fill();
}
 
public function form(Form $form): Form
{
return $form
->schema([
TextInput::make('name')
->required(),
])
->statePath('data');
}
}

And we need to render the form in the Blade file.

resources/views/filament/pages/edit-company.blade.php:

<x-filament-panels::page>
<x-filament-panels::form>
{{ $this->form }}
</x-filament-panels::form>
</x-filament-panels::page>

After visiting the edit company page, we now see an empty form.

empty edit company form


Filling and Submitting Form

We must fill form inputs with the company information first. Because here we have a hasOne relation to the company, we get all the company data by just doing auth()->user()->company.

app/Models/User.php:

use Illuminate\Database\Eloquent\Relations\HasOne;
 
class User extends Authenticatable implements FilamentUser
{
// ...
 
public function company(): HasOne
{
return $this->hasOne(Company::class);
}
}

app/Filament/Pages/EditCompany.php:

class EditCompany extends Page implements HasForms
{
// ...
 
public function mount(): void
{
$this->form->fill();
$this->form->fill(auth()->user()->company->attributesToArray());
}
 
// ...
}

Next, we need a submit button.

app/Filament/Pages/EditCompany.php:

use Filament\Actions\Action;
 
class EditCompany extends Page implements HasForms
{
// ...
 
protected function getFormActions(): array
{
return [
Action::make('save')
->label(__('filament-panels::resources/pages/edit-record.form.actions.save.label'))
->submit('save'),
];
}
}

resources/views/filament/pages/edit-company.blade.php:

<x-filament-panels::page>
<x-filament-panels::form>
{{ $this->form }}
 
<x-filament-panels::form.actions
:actions="$this->getFormActions()"
/>
</x-filament-panels::form>
</x-filament-panels::page>

After revisiting the edit company page, we can see the form is filled with data, and we have a submit button.

edit company page

All that's left is to update the data. We need a method for updating the company and to add wire:submit directive to the form.

resources/views/filament/pages/edit-company.blade.php:

<x-filament-panels::page>
<x-filament-panels::form>
<x-filament-panels::form wire:submit="save">
{{ $this->form }}
 
<x-filament-panels::form.actions
:actions="$this->getFormActions()"
/>
</x-filament-panels::form>
</x-filament-panels::page>

app/Filament/Pages/EditCompany.php:

use Filament\Support\Exceptions\Halt;
 
class EditCompany extends Page implements HasForms
{
// ...
 
public function save(): void
{
try {
$data = $this->form->getState();
 
auth()->user()->company->update($data);
} catch (Halt $exception) {
return;
}
}
 
// ...
}

Lastly, let's send a success notification message after updating company information.

use Filament\Notifications\Notification;
 
class EditCompany extends Page implements HasForms
{
// ...
 
public function save(): void
{
try {
$data = $this->form->getState();
 
auth()->user()->company->update($data);
} catch (Halt $exception) {
return;
}
 
Notification::make()
->success()
->title(__('filament-panels::resources/pages/edit-record.notifications.saved.title'))
->send();
}
 
// ...
}

notification message


If you want more Filament examples, you can find more real-life projects on our FilamentExamples.com.

Enjoyed This Tutorial?

Get access to all premium tutorials, video and text courses, and exclusive Laravel resources. Join our community of 10,000+ developers.

Recent Courses on Laravel Daily

Next.js Basics for Laravel Developers

11 lessons
58 min

Testing in Laravel 13 For Beginners

26 lessons
1 h 41 min read

How to Structure Laravel 13 Projects

16 lessons
1 h 32 min read
anguz avatar

Excellent!

👍 10
🥳 1
👀 1
😍 1
💅 1
devPathbuilder avatar

how about having file upload in the form ?

Nerijus avatar

What about it?

devPathbuilder avatar

i have problem to it

devPathbuilder avatar

The POST method is not supported for route admin/settings. Supported methods: GET, HEAD.

ive always have this message im using costum page

Nerijus avatar

How are you handling upload?

dailer avatar

Can show an example of how to add a custom Clear button with notification? i.e. After clicking on Clear button, it can clear/reset some fields of this record and show a notification that this record was Cleared

Nerijus avatar

Just make an action button where action would be to set fields to initial value and send the notification

dailer avatar

I could not get things to work, that's why I asked for an example.

Jahidul Islam (Shojib Flamon) avatar
Jahidul Islam (Shojib Flamon)

It was an excellent elaboration that might help build any predefined settings page like Site title, Notification Email, Enable/Disable any feature, Some footer link, or many more things.

One thing I would like to address, HasForms interface is an unnecessary implementation. It is already announced in Filament\Pages\Page. So we might remove it as I Think.

Thanks

Nerijus avatar

Kinda. But for settings you should use official plugin for spatie laravel settings package

Mohammad hosein Saghatforoush avatar
Mohammad hosein Saghatforoush

thanks

Vittorio Dormi avatar

Thanks! It was very useful.

Amenkey Pierre avatar

Thank you!!! But my save() not call but getFormActions is call

Chris Allen avatar

Did you solve this issue?

Majd avatar

I get this error Target [Filament\Forms\Contracts\HasForms] is not instantiable while building [Filament\Forms\Form].

any ideas?

Modestas avatar

Could you expand on this a bit more? I'm not sure what you were doing to get this error.

ps. Do not cross post on multiple articles ;)

Majd avatar

:) solved, issue was with actions naming conventions, thanks

justLearning avatar

How about displaying table widget inside tabs?

Nerijus avatar

Can you give more contex? Maybe some screenshots

justLearning avatar

I have successfully identified the solution through a thorough review of the official documentation. Nonetheless, I appreciate your prompt response.

Emmanuel71 avatar

Thanks! A very useful and clean solution!

Kurt Bauman avatar

I'm working on a multi-tanancy site. I have a 'edit' page for the company table. I'm having a hard time trying to figure out how to change this '$this->form->fill(auth()->user()->company->attributesToArray()); ' to pull the company id in using '$tenant = Filament::getTenant();' I'm unexperianced and would appreciate some advice on how to do this. Thank you

Samer Ajlawi avatar

Try Filament::getTenant()->company->attributesToArray() something like that , might work if your team(tenant model) have company.

Kurt Bauman avatar

Thank you. I will let you know if it works.

Steve Fontaine avatar

Very useful information, thanks !

Chris Allen avatar

As with Amenkey Pierre (above - who commented 4 months ago), my save() function is never called. Any ideas why?

Nerijus avatar

No idea what you missed as you didnt provide anything

Chris Allen avatar

I followed your tutorial so don't know what to provide. The Save button does nothing it never calls the save() function.

Chris Allen avatar

If I stuff this button into the form then it works and save() is called.

<button action="submit" wire:click>Save</button

Rasul Chopurov avatar

public function mount(): void { dump(auth()->user()->expertData->attributesToArray()); $this->form->fill(auth()->user()->expertData->attributesToArray()); }

public function form(Form $form): Form
{
return $form
->schema([
TextInput::make('name')
]);
}

data not load to form why?

Heru Setiawan avatar

thank you for the clear explanation.

how can you fill the form data from model that is not based on the authenticated user?

for instance, if we want to create a custom page for a form to edit a master data of a product with certain id?

Nerijus avatar

You get a record as you would usually without filament and fill the form. But what it's the difference from the regular edit page then?

Heru Setiawan avatar

thank you. basically I need custom page to restrict some fields and add extra fields like "reason for update" hen editing

Nerijus avatar

You could generate the form based on the role or whatever else conditions

obada halak avatar

I followed your steps, but the page does not appear in dashboard navigation menu

  • "filament/filament": "^3.2",
  • "livewire/livewire": "^3.5",
  • "laravel/framework": "^10.10",
class EditHome extends Page implements HasForms
{
use InteractsWithForms;
 
public ?array $data = [];
 
protected static ?string $navigationIcon = 'heroicon-o-document-text';
 
protected static string $view = 'filament.pages.edit-home';
 
 
protected function getFormSchema(): array
{
return [
TextInput::make('name'),
];
}
 
public static function canAccess(array $parameters = []): bool
{
return true; // Adjust the condition as needed
}
public function mount(): void
{
$this->form->fill();
}
 
public function form(Form $form):Form{
return $form
->schema([
TextInput::make('name')
->required(),
]);
}
 
}
Nerijus avatar

I guess your page is in the namespace where filament doesnt discover them or a simple cache

obada halak avatar

I tried clear the cach, also I check filament discover path

Nerijus avatar

It should just work. Someth is off with your configur maybe. Also, try clearing filament cache, search docs for that

Ahmed Ammar avatar

Great content!

ArtMin96 avatar

How can I make the page translatable?

Fahrurrozy avatar

im trying to create a custom edit page filament for editing the user own school, and it has file upload, at first doing things with text based data is working, when it comes to fileupload, its not working properly

instead of storing filename in database, it store weird json, and the fileupload stuck on tmp file, instead of automatically move it to destination storage on saving. can you spot whats the issue ?

<?php
 
namespace App\Filament\School\Pages;
 
use App\Models\School;
use Filament\Forms\Form;
use Filament\Pages\Page;
use Filament\Actions\Action;
use Filament\Notifications\Notification;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Log;
use Filament\Forms\Components\Wizard;
use Filament\Forms\Contracts\HasForms;
use Filament\Forms\Components\Textarea;
use Filament\Forms\Components\TextInput;
use Filament\Forms\Components\FileUpload;
use Filament\Forms\Components\Wizard\Step;
use Filament\Forms\Concerns\InteractsWithForms;
 
class EditSchool extends Page implements HasForms
{
use InteractsWithForms;
 
protected static ?string $navigationIcon = 'heroicon-o-pencil-square';
protected static string $view = 'filament.school.pages.edit-school';
 
public ?array $data = [];
public ?School $school = null;
 
public function mount(): void
{
$this->school = School::where('id', Auth::user()->school_id)->firstOrFail();
$this->form->fill($this->school->toArray());
 
Log::info('EditSchool mount(): School data loaded', ['school_id' => $this->school->id]);
}
 
public function form(Form $form): Form
{
return $form
->schema([
Wizard::make([
Step::make('School Details')
->schema([
TextInput::make('name')
->label('School Name')
->placeholder('Enter the school name')
->required(),
 
TextInput::make('phone_number')
->label('Phone Number')
->mask('9999-9999-99999')
->tel()
->placeholder('Enter the school contact number')
->required(),
 
TextInput::make('email')
->label('School Email')
->email()
->placeholder('Enter the school email address')
->required(),
 
Textarea::make('address')
->label('Address')
->placeholder('Enter the complete school address')
->required(),
]),
 
Step::make('School Person')
->schema([
FileUpload::make('head_master_photo')
->label('Head Master Photo')
alin avatar

Is there any difference, in this tutorial, between using:

<x-filament-panels::form.actions :actions="$this->getFormActions()" />

and just using a simple submit button? It seems overkill, in this case, but maybe i am missing something or maybe you just want to illustrate the correct way of doing things in Filament.

Nerijus avatar

This way is filament way and only for v3. With v4 everything changed. You can simply add a button

We'd Love Your Feedback

Tell us what you like or what we can improve

Feel free to share anything you like or dislike about this page or the platform in general.