Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

flashman's avatar

Filament, table and form in CustomPage?

Hello,

I am learning Filament and now I am stuck with something. I am building Vacation Listing Page and use Filament for admin section.

The structure is: One property can have multiple units. For example property is house and unit is room/apartment in that house. I have model Property and model Unit with Property -> hasMany Unit.

I have Filament PropertyResource and that is ok. I am also using subnavigation when I am editing property I have navigation like:

  • main information
  • gallery
  • units (this is my problem)
  • map position
  • description.

I have managed to add Units using Repeater component and that is working. But my problem is that I need to change content for Unit. For example, I need to enter available periods for that unit or pricelist for unit in some period.

So I need some ActionMenu to open modal where I have table with prices and option to add new price, edit it and so on.

So, can I show form with table in modal, like Resource with all options and pass pass $propertyId to that modal?

What I have tried?

  • Tried to make UnitsPage Livewire component and implement HasTable and HasForms interface.
  • in view I added {{$this->table()}}
  • I added query to table like: Unit::query()->where('property_id', $this->record->id)

With that I have got data (units of property) in table. Edit and Delete menus where working. But problem is with Add menu.

When saving, $propertyId is not passed to insert and I have SQLException that property_id is null.

  1. How can I pass $propertyId to create action?
  2. Why fields declared in form schema are not displayed in form? They are only shown if I pass it as parameter to ActionMenu

This is my component to show Units:

<?php

namespace App\Filament\Resources\PropertyResource\Pages;

use App\Filament\Resources\PropertyResource;
use App\Models\PropertyType;
use App\Models\Unit;
use AymanAlhattami\FilamentPageWithSidebar\Traits\HasPageSidebar;
use Filament\Actions\Contracts\HasActions;
use Filament\Actions\CreateAction;
use Filament\Forms\Components\Grid;
use Filament\Forms\Components\Repeater;
use Filament\Forms\Components\Select;
use Filament\Forms\Components\SpatieMediaLibraryFileUpload;
use Filament\Forms\Components\TextInput;
use Filament\Forms\Concerns\InteractsWithForms;
use Filament\Forms\Contracts\HasForms;
use Filament\Forms\Form;
use Filament\Pages\Concerns\InteractsWithFormActions;
use Filament\Pages\Concerns\InteractsWithHeaderActions;
use Filament\Resources\Pages\Concerns\InteractsWithRecord;
use Filament\Resources\Pages\Page;
use Filament\Tables\Actions\DeleteAction;
use Filament\Tables\Actions\EditAction;
use Filament\Tables\Columns\TextColumn;
use Filament\Tables\Concerns\InteractsWithTable;
use Filament\Tables\Contracts\HasTable;
use Filament\Tables\Table;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Relations\Relation;

class ExampleUnits extends Page implements HasTable, HasForms, HasActions
{
    use HasPageSidebar, InteractsWithRecord, InteractsWithTable, InteractsWithForms, InteractsWithFormActions, InteractsWithHeaderActions;

    protected static string $resource = PropertyResource::class;

    protected static string $view = 'filament.resources.property-resource.pages.example-units';

    public ?array $data = [];

    public function getModel(): string
    {
        return Unit::class;
    }

    public function table(Table $table): Table
    {
        return $table
            ->query(Unit::query()->where('property_id', $this->record->id))
            ->columns([
                TextColumn::make('name')
                    ->searchable(),
            ])
            ->filters([
                //
            ])
            ->actions([
                EditAction::make()
                    ->modalHeading('Izmjena značajke')
                    ->form($this->getFormContent()),
                DeleteAction::make()
                    ->modalHeading('Brisanje značajke?'),
            ])
            ->emptyStateActions([
                \Filament\Tables\Actions\CreateAction::make()
            ]);
    }

    public function form(Form $form): Form
    {
        return $form->schema($this->getFormContent());
    }

    private function getFormContent(): array
    {
        return [
            Grid::make([
                'default' => 3,
            ])
                ->schema([
                    Select::make('type_id')
                        ->label('Tip')
                        ->options(PropertyType::get()->pluck('name', 'id'))
                        ->required(),

                    TextInput::make('name')
                        ->required()
                        ->label('Naziv sm. jedinice'),

                    TextInput::make('unit_size')
                        ->numeric()
                        ->minValue(1)
                        ->label('Površina')
                        ->suffix('m2')
                        ->minValue(1),

                ]),


            //Max.
            Grid::make([
                'default' => 4,
            ])
                ->schema([
                    TextInput::make('total_rooms')
                        ->numeric()
                        ->minValue(0)
                        ->label('Broj soba')
                        ->minValue(1)
                        ->maxValue(10),
                    TextInput::make('total_bathrooms')
                        ->numeric()
                        ->minValue(0)
                        ->label('Broj kupaonica')
                        ->minValue(1)
                        ->maxValue(10),
                    TextInput::make('max_persons')
                        ->numeric()
                        ->label('Max. osoba')
                        ->minValue(1)
                        ->maxValue(10),
                    TextInput::make('max_adults')
                        ->numeric()
                        ->label('Max. odraslih')
                        ->minValue(1)
                        ->maxValue(10),
                ]),


            //Cjenik
            Grid::make([
                'default' => 3,
                'sm' => 1,
                'md' => 3,
                'lg' => 3,
                'xl' => 3,
                '2xl' => 3,
            ])
                ->schema([
                    TextInput::make('preseason_price')
                        ->numeric()
                        ->label('Predsezona')
                        ->suffix('€')
                        ->required(),

                    TextInput::make('season_price')
                        ->numeric()
                        ->label('Sezona')
                        ->suffix('€')
                        ->minValue(1)
                        ->required(),

                    TextInput::make('postseason_price')
                        ->numeric()
                        ->label('Post-sezona')
                        ->suffix('€')
                        ->minValue(1)
                        ->required()
                ]),

            SpatieMediaLibraryFileUpload::make('images')
                ->multiple()
                ->label('Slike smj. jedinice')
                ->reorderable()
                ->responsiveImages()
                ->columnSpanFull()
                ->image()
                ->visibility('public')
                ->maxSize(1024)
                ->imagePreviewHeight(150),
        ];
    }

    public function mount(int|string $record): void
    {
        $this->record = $this->resolveRecord($record);
        $this->form->fill();

    }

    protected function getHeaderActions(): array
    {
        return [
            CreateAction::make()
                ->label('Dodaj vrstu')
                ->modalHeading('Nova vrsta')
                // ->form($this->getFormContent())
                ->model(Unit::class)
        ];
    }

    protected function getTableQuery(): Builder|Relation|null
    {
        return Unit::query();
    }
}

This is view of component

<x-filament-panels::page>
    <div>
        {{ $this->table }}
    </div>
    <x-filament-actions::modals />
</x-filament-panels::page>

0 likes
0 replies

Please or to participate in this conversation.