---
title: "FilamentPHP: a first look"
description: "A first look at Filament, a framework-within-a-framework!"
---

# FilamentPHP: a first look

![Different parts of a plane including wings, cabin, fuselage and more ready to be assembled.](https://fly.io/laravel-bytes/filamentphp-a-first-look/assets/filamentphp-a-first-look-cover.webp)

[Annie Ruygt](https://annieruygtillustration.com/)

The fastest way to get your admin panel up in the clouds? Deploy your [Laravel application](https://docs.fly.io/laravel) on Fly.io, you’ll be up and running in minutes!

As Laravel developers we know how productive a framework can be: It provides a set of common components, reusable functionality and most importantly, a set of guidelines that make everyone using the framework more efficient.

Knowing this, the fine folks over at [FilamentPHP](https://filamentphp.com) created a framework-within-a-framework, consisting of a couple of parts: There’s a Form Builder, a Table Builder and a Notifications package and there’s also an Admin Panel that combines them. Using the admin panel, you can create Resources that enable you to easily add CRUD pages with navigation, tables and forms to your app. It has all been expertly designed, and is very flexible to make your own changes if you need them. Check out their demo here: [Filament Demo](https://demo.filamentphp.com/login).

In this article, we’ll take a first look at Filament and we’ll build upon what we make now in future articles. Prepare for liftoff!

## The app

In this series, I’m going meta.

No, not the social media/virtual reality company. Over the course of this mini-series, I’ll write an app that will show information about its own deployment on Fly.io. It’ll have a `Machine` model for every machine that’s in your app, with a relation to a `Region` model that specifies where that machine is running. This will enable us to do something *very* cool: we can let the machines ping each other and see how fast they respond. In this article, we’ll lay the foundations for that app.

## Setting up Filament

So, create a new Laravel project and install the Filament admin panel with `composer require filament/filament:"^2.0"` . Now, run the migrations and create a user for yourself using `php artisan make:filament-user` .

Filament has been built to be an admin panel, but I’ll color outside of the lines and use Filament for the whole app. I’ve heard that Filament v3 will support this idea even better, but for now it also works well. To make it feel like Filament is the only thing in our app, I removed the default welcome page and changed the Filament URL from `/admin` to the `/` home url. You can do this by adding `FILAMENT_PATH="/"` to your `.env` file. After that, just remove the default route from `web.php` and delete the `resources/views/welcome.blade.php` file.

Now, onto the Filament admin panel itself. I’ll be using two models here: a `Region` model that has data about each region on Fly.io, and a `Machine` model that contains data about each machine where this app will be deployed. `Machines` will have a one-to-many relationship with `Regions`, so each `Machine` belongs to one `Region`. I’ll use this setup later on to illustrate the latency between different regions, but that’s for a different post 😉.

If you want to follow along, here are the models:

```
class Region extends Model
{
    protected $fillable = ['iata_code', 'name'];

    public function machines(): \Illuminate\Database\Eloquent\Relations\hasMany
    {
        return $this->hasMany(Machine::class);
    }
}
```

Both iata_code and name are normal strings.

```
class Machine extends Model
{
    protected $fillable = ['machine_id', 'region_id'];

    public function region(): \Illuminate\Database\Eloquent\Relations\BelongsTo
    {
        return $this->belongsTo(Region::class);
    }
}
```

Machine *id is a string and region* id is a foreign Id for the Region model.

## Adding the RegionResource

Let’s add a resource for our Region first, since that’s the easiest. The model has only two properties: the `iata_code` is the airport code of that region (LAX for Los Angeles) and the `name` is well, the name of the region.

Resources in Filament only describe how users interact with the Model, and how it should be presented, so make sure the Region model and database table already exist.

[Resources](https://filamentphp.com/docs/2.x/admin/resources/getting-started) in Filament are like superpowered classes that can handle all the CRUD operations using forms and tables. It’s almost like magic! You can pick if they need to show separate pages for viewing and editing models or you can opt for a ‘simple’ single view and use modals for creating and updating. I’m a simple person, so I’ll create a simple Resource for my `Region` model using `php artisan make:filament-resource Region --simple` . You’re probably smarter than me so you would let Filament auto-generate the forms and tables as well. Install the `doctrine/dbal` package using `composer require doctrine/dbal --dev` and then add `--generate` to the command: `php artisan make:filament-resource Region --simple --generate`. Much easier!

Here’s how it looks out of the box:

We actually have all we need already: we can do all the CRUD operations on our Region model, with validation on the form inputs! I’ll change the label for the IATA code to have IATA in all caps by opening `RegionResource` and adding `->label("IATA Code")` on the `TextInput` for the `iata_code` property. While we’re at it we can also change the `maxLength` from 255 to 3. Here’s how the `form()` looks now:

```
public static function form(Form $form): Form
    {
        return $form
            ->schema([
                Forms\Components\TextInput::make('iata_code')
                    ->label("IATA Code")
                    ->required()
                    ->maxLength(3),
                Forms\Components\TextInput::make('name')
                    ->required()
                    ->maxLength(255),
            ]);
    }
```

And we can do the same thing for the column in the table:

```
public static function table(Table $table): Table
    {
        return $table
            ->columns([
                Tables\Columns\TextColumn::make('iata_code')
+                   ->label('IATA Code'),
                ...
    }
```

This is how almost all customization on tables and forms will be done: create the correct field and use the static methods to change it to whatever you need!

One last thing: I pulled a sneaky on ya! I used a different icon for the ‘regions’ navigation item. I wanted something more region-like than the default ‘collection’ icon, so I changed the `$navigationIcon` on the `RegionResource` class from `heroicon-o-collection` to `heroicon-o-globe`. It just makes more sense.

## Fly.io ❤️ Laravel

Fly your servers close to your users—and marvel at the speed of close proximity. Deploy globally on Fly in minutes!

[Deploy your Laravel app! →](https://docs.fly.io/laravel)

## Adding the MachineResource

For the Machine resource, let’s switch it up a bit and not use the ‘simple’ resource. This means we’ll have different pages for index, edit, create and view operations. Run `php artisan make:filament-resource Machine --generate` et voilà: a MachineResource! For the Icon I’d suggest the `heroicon-o-chip` icon.

Let’s now take a look at the `MachineResource`. Something special happened here, and I need you to be as excited about it as I am: In the `form` method, there is a `TextInput` just like with the `RegionResource`, but now there’s also a `Select`. This lets you attach a related `Region` using the relationship on the `Machine` model. Filament will preload the Regions and let the user pick them from a list when creating a `Machine`. Pretty cool.

Let’s make it even cooler: let’s enable the user to filter the regions by typing in the field by adding `->searchable()` and `->preload()` on the `Select` field. The preload isn’t necessary here, but it does enhance the experience by showing results before the user has typed and filtering faster. Let’s see it in action!

Now, one final change to make before I let you return to watching cat videos: The `MachineResource` create form looks a bit weird, the input fields look like they’ve been thrown on the page. Let’s group them together in a card! I’ll also arrange both fields on one line, using `->columns(3)` on the Card and `->columnSpan(2)` on the `Select` field.

```
public static function form(Form $form): Form
{
    return $form
        ->schema([
+           Forms\Components\Card::make()
+               ->schema([
                    Forms\Components\TextInput::make('machine_id')
                        ->required()
                        ->maxLength(255),
                    Forms\Components\Select::make('region_id')
                        ->relationship('region', 'name')
                        ->required()
                        ->searchable()
                        ->preload()
+                       ->columnSpan(2),
+               ])
        ]);
}
```

Here’s how it looks now:

Bear this in mind: we have only scratched the surface of what Filament can do, but we’ve already made a lot of progress: We already have CRUD operations for two models of our simple app, including form validation and relationships. On top of that we have authentication and navigation that’s built-in. Not bad, right? Later on we’ll customize the look and feel of our app, and after that we’ll add the dashboard that displays the ping times between the different deployments of our app.

And just between me and you: They don’t even know I’m writing this article. I just genuinely enjoyed using Filament and I couldn’t wait to tell you about it. Better yet, Filament v3 is right around the corner (check out [v3when.com](https://v3when.com)!) so they’ll probably have a lot more in store for us in the future. Keep it up guys!
