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.
- How can I pass $propertyId to create action?
- 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>
Please or to participate in this conversation.