Andrew Wallo
Andrew Wallo
FFilament
Created by Andrew Wallo on 6/8/2024 in #❓┊help
Help Needed with Sorting an Accessor in Filament Table
I'm working on a Filament table where I need to display and sort by a computed accessor, formatted_ending_balance, in my Account model. The accessor calculates the ending balance based on account transactions and formats it. Account model:
class Account extends Model
{
public function formattedEndingBalance(): Attribute
{
return Attribute::get(function () {
return $this->ending_balance->convert()->formatWithCode();
});
}

protected function endingBalance(): Attribute
{
return Attribute::get(function () {
$company = $this->company;
$fiscalYearStart = $company->locale->fiscalYearStartDate();
$fiscalYearEnd = $company->locale->fiscalYearEndDate();

return Accounting::getEndingBalance($this, $fiscalYearStart, $fiscalYearEnd);
});
}
// Unrelated code...
class Account extends Model
{
public function formattedEndingBalance(): Attribute
{
return Attribute::get(function () {
return $this->ending_balance->convert()->formatWithCode();
});
}

protected function endingBalance(): Attribute
{
return Attribute::get(function () {
$company = $this->company;
$fiscalYearStart = $company->locale->fiscalYearStartDate();
$fiscalYearEnd = $company->locale->fiscalYearEndDate();

return Accounting::getEndingBalance($this, $fiscalYearStart, $fiscalYearEnd);
});
}
// Unrelated code...
Filament table column:
Tables\Columns\TextColumn::make('account.formatted_ending_balance')
->localizeLabel('Current Balance')
->alignment(Alignment::End)
->sortable(),
Tables\Columns\TextColumn::make('account.formatted_ending_balance')
->localizeLabel('Current Balance')
->alignment(Alignment::End)
->sortable(),
The problem is that when I try to sort by formatted_ending_balance, I get a SQL error because this accessor is not a database column. How can I enable sorting for this computed accessor in the Filament table? Is it currently possible? Any possible workarounds or similar solutions? Making it a database column is not an option for me based on my app logic. Thanks.
9 replies
FFilament
Created by Andrew Wallo on 4/7/2024 in #❓┊help
How to make tenant available to render hooks?
I’m currently building a plugin which allows users to register navigation items for a custom menu in the panel. When creating a tenant by clicking on the tenant register link, I get an error saying the tenant parameter is missing for navigation items registered in the panel using my plugin. It would be very helpful to know how I can go about fixing this. I have already done so much work on this plugin. Thanks.
4 replies
FFilament
Created by Andrew Wallo on 1/26/2024 in #❓┊help
Question about supported relationship syntax in Filament Forms
Quick question for anyone who knows. Does Filament support saving/creating relationships in a Form using syntax similar to this? Kind of like what you would see for Table columns..
Forms\Components\TextInput::make('account.name')->required(),
Forms\Components\TextInput::make('account.name')->required(),
I could have sworn that I remember using syntax like this and it would work..
4 replies
FFilament
Created by Andrew Wallo on 12/15/2023 in #❓┊help
How to Reset the Active Tab in Table
So, I am displaying tabs dynamically, and it works, but when a User deletes all of the records that are "connected", even though all of the tabs disappear correctly and the default table from index view is shown, the "connected" active tab query string in the URL is still shown. Setting it to null doesn't work and instead just makes the query string in the URL actually be ?activeTab=null. Which seems odd. The only way I could get it to work correctly is by redirecting to the index view URL. Is there a simpler way?
public function getTabs(): array
{
if (! Account::where('is_connected_account', true)->exists()) {
if ($this->activeTab === 'connected') {
$this->redirect(url(static::getUrl()));
}

return [];
}

return [
'manual' => Tab::make('Manual')
->modifyQueryUsing(function (Builder $query) {
$query->where('is_connected_account', false)
->whereNull('available_balance');
}),
'connected' => Tab::make('Connected')
->modifyQueryUsing(function (Builder $query) {
$query->where('is_connected_account', true)
->whereNotNull('available_balance');
}),
];
}
public function getTabs(): array
{
if (! Account::where('is_connected_account', true)->exists()) {
if ($this->activeTab === 'connected') {
$this->redirect(url(static::getUrl()));
}

return [];
}

return [
'manual' => Tab::make('Manual')
->modifyQueryUsing(function (Builder $query) {
$query->where('is_connected_account', false)
->whereNull('available_balance');
}),
'connected' => Tab::make('Connected')
->modifyQueryUsing(function (Builder $query) {
$query->where('is_connected_account', true)
->whereNotNull('available_balance');
}),
];
}
2 replies
FFilament
Created by Andrew Wallo on 10/12/2023 in #❓┊help
How to reactively update the `displayFormat()` of DatePicker?
This will not work. It shows the display format changed in the html, but not in the UI:
Select::make('date_format')
->options(DateFormat::class)
->required()
->live()
->markAsRequired(false),
DatePicker::make('fiscal_year_start')
->label('Start')
->live()
->native(false)
->maxDate(static fn (Get $get) => $get('fiscal_year_end'))
->displayFormat(static function(Get $get) {
return $get('date_format') ?? DateFormat::DEFAULT;
})
->seconds(false)
->required()
->localizeLabel()
->markAsRequired(false),
Select::make('date_format')
->options(DateFormat::class)
->required()
->live()
->markAsRequired(false),
DatePicker::make('fiscal_year_start')
->label('Start')
->live()
->native(false)
->maxDate(static fn (Get $get) => $get('fiscal_year_end'))
->displayFormat(static function(Get $get) {
return $get('date_format') ?? DateFormat::DEFAULT;
})
->seconds(false)
->required()
->localizeLabel()
->markAsRequired(false),
10 replies
FFilament
Created by Andrew Wallo on 9/15/2023 in #❓┊help
Using `getRawState()` returns an array for FileUpload
I use getRawState() in order to display previews within my blade view, but the only problem is that while getState() returns the correct string and URL for a $logo value/state, using getRawState() doesn't and causes issues. How would I fix this?
21 replies
FFilament
Created by Andrew Wallo on 9/11/2023 in #❓┊help
Swap Panel Font depending on database value?
Question. I have a field that allows the user to switch the font used for the Panel. How would I go about switching the panel's font used depending on that value from the database? Obviously, I couldn't do it inside a Service Provider for the panel because the current company instance couldn't be resolved by that time, so I decided to use a Listener.
public function handle(TenantSet $event): void
{
/** @var Company $company */
$company = $event->getTenant();
$paginationPageOptions = RecordsPerPage::caseValues();
$defaultPaginationPageOption = $company->defaults->records_per_page->value ?? RecordsPerPage::DEFAULT;
$defaultSort = $company->defaults->table_sort_direction->value ?? TableSortDirection::DEFAULT;
$defaultPrimaryColor = $company->defaults->primary_color ?? PrimaryColor::from(PrimaryColor::DEFAULT);
$defaultFont = $company->defaults->font->value ?? Font::DEFAULT;

Table::configureUsing(static function (Table $table) use ($paginationPageOptions, $defaultSort, $defaultPaginationPageOption): void {
$table
->paginationPageOptions($paginationPageOptions)
->defaultSort(column: 'id', direction: $defaultSort)
->defaultPaginationPageOption($defaultPaginationPageOption);
}, isImportant: true);

$defaultColor = FilamentColor::register([
'primary' => $defaultPrimaryColor->getColor(),
]);

FilamentColor::swap($defaultColor);
}
public function handle(TenantSet $event): void
{
/** @var Company $company */
$company = $event->getTenant();
$paginationPageOptions = RecordsPerPage::caseValues();
$defaultPaginationPageOption = $company->defaults->records_per_page->value ?? RecordsPerPage::DEFAULT;
$defaultSort = $company->defaults->table_sort_direction->value ?? TableSortDirection::DEFAULT;
$defaultPrimaryColor = $company->defaults->primary_color ?? PrimaryColor::from(PrimaryColor::DEFAULT);
$defaultFont = $company->defaults->font->value ?? Font::DEFAULT;

Table::configureUsing(static function (Table $table) use ($paginationPageOptions, $defaultSort, $defaultPaginationPageOption): void {
$table
->paginationPageOptions($paginationPageOptions)
->defaultSort(column: 'id', direction: $defaultSort)
->defaultPaginationPageOption($defaultPaginationPageOption);
}, isImportant: true);

$defaultColor = FilamentColor::register([
'primary' => $defaultPrimaryColor->getColor(),
]);

FilamentColor::swap($defaultColor);
}
This works for everything else such as the Panel color, etc... But I couldn't find a way to register or switch the Panels font. Could anyone help me?
4 replies
FFilament
Created by Andrew Wallo on 9/9/2023 in #❓┊help
How do I disable the Auto-saving of Form field relationships?
I currently have a form similar to the following:
public function form(Form $form): Form
{
return $form
->schema([
Forms\Components\Section::make('General')
->schema([
Forms\Components\Select::make('account_id')
->label('Account')
->relationship('account', 'name')
->searchable()
->preload()
->nullable(),
Forms\Components\Select::make('currency_code')
->label('Currency')
->relationship('currency', 'code')
->searchable()
->preload()
->nullable(),
])->columns(),
// More fields...
]);
}
public function form(Form $form): Form
{
return $form
->schema([
Forms\Components\Section::make('General')
->schema([
Forms\Components\Select::make('account_id')
->label('Account')
->relationship('account', 'name')
->searchable()
->preload()
->nullable(),
Forms\Components\Select::make('currency_code')
->label('Currency')
->relationship('currency', 'code')
->searchable()
->preload()
->nullable(),
])->columns(),
// More fields...
]);
}
The entire form has around 8 fields that all have a relationship. The only issue is that it is absolutely necessary that I use the handleRecordUpdate() method to further handle the data and record being saved. I honestly think that it should be mentioned in the documentation that Filament will automatically save the data before you can handle it any further within the handleRecordUpdate() method since this may cause Users confusion. But anyways, is there a way to disable the auto saving of relationships for the entire form? I am aware that I could do this for each field:
Forms\Components\Select::make('currency_code')
->label('Currency')
->relationship('currency', 'code')
->saveRelationshipsUsing(null) // Here
->searchable()
->preload()
->nullable(),
Forms\Components\Select::make('currency_code')
->label('Currency')
->relationship('currency', 'code')
->saveRelationshipsUsing(null) // Here
->searchable()
->preload()
->nullable(),
But I find this a little exhaustive.
4 replies
FFilament
Created by Andrew Wallo on 9/8/2023 in #❓┊help
Options List for Select Field
No description
5 replies
FFilament
Created by Andrew Wallo on 9/7/2023 in #❓┊help
Customizing the Unique validation rule
How would I appropriately customize the Unique validation rule/method that Filament has according to the following? Here is my categories table:
Schema::create('categories', function (Blueprint $table) {
$table->id();
$table->foreignId('company_id')->constrained()->cascadeOnDelete();
$table->string('name')->index();
$table->string('type');
$table->string('color');
$table->boolean('enabled')->default(true);
$table->foreignId('created_by')->nullable()->constrained('users')->nullOnDelete();
$table->foreignId('updated_by')->nullable()->constrained('users')->nullOnDelete();
$table->timestamps();

$table->unique(['company_id', 'name', 'type']);
});
Schema::create('categories', function (Blueprint $table) {
$table->id();
$table->foreignId('company_id')->constrained()->cascadeOnDelete();
$table->string('name')->index();
$table->string('type');
$table->string('color');
$table->boolean('enabled')->default(true);
$table->foreignId('created_by')->nullable()->constrained('users')->nullOnDelete();
$table->foreignId('updated_by')->nullable()->constrained('users')->nullOnDelete();
$table->timestamps();

$table->unique(['company_id', 'name', 'type']);
});
This is what I have so far for the Field:
Forms\Components\TextInput::make('name')
\\ ...
->unique(modifyRuleUsing: static function (Unique $rule, Request $request) {
return $rule->where('company_id', auth()->user()->currentCompany->id)
->where('type', $request->input('type'));
}),
Forms\Components\TextInput::make('name')
\\ ...
->unique(modifyRuleUsing: static function (Unique $rule, Request $request) {
return $rule->where('company_id', auth()->user()->currentCompany->id)
->where('type', $request->input('type'));
}),
Will the above work? Or do I need to do something like this or something else?
Forms\Components\TextInput::make('name')
\\ ...
->unique(modifyRuleUsing: static function (Unique $rule, Forms\Get $get) {
return $rule->where('company_id', auth()->user()->currentCompany->id)
->where('type', $get('type'));
}),
Forms\Components\TextInput::make('name')
\\ ...
->unique(modifyRuleUsing: static function (Unique $rule, Forms\Get $get) {
return $rule->where('company_id', auth()->user()->currentCompany->id)
->where('type', $get('type'));
}),
7 replies
FFilament
Created by Andrew Wallo on 8/17/2023 in #❓┊help
Trying to delete company: Error "Can't set model as property if it hasn't been persisted yet."
This is a delete action similar to deleting a company in Laravel Jetstream. I get the error; I have to reload the page and then I get taken to the dashboard as my other company. What does this mean and what do I do to fix this?
44 replies
FFilament
Created by Andrew Wallo on 8/17/2023 in #❓┊help
How do I get this Registration Page to work?
I want to create a company immediately after registration. This should work, but the issue is I get an error: "email is already taken for the email field". I then refresh and get taken to the Tenant Registration page. So, Filament cancels out my DB Transaction somehow.. Even though the user is created just not the company.
class Register extends FilamentRegister
{
public function form(Form $form): Form
{
return $form
->schema([
$this->getNameFormComponent(),
$this->getEmailFormComponent(),
$this->getPasswordFormComponent(),
$this->getPasswordConfirmationFormComponent(),
])
->statePath('data')
->model(User::class);
}

public function register(): ?RegistrationResponse
{
parent::register();

$user = $this->createUser();

event(new Registered($user));

Filament::auth()->login($user);

session()->regenerate();

return app(RegistrationResponse::class);
}

public function createUser(): User
{
$data = $this->form->getState();

return DB::transaction(function () use ($data) {
return tap(User::create($data), function (User $user) {
$this->createCompany($user);
});
});
}

protected function createCompany(User $user): void
{
$user->ownedCompanies()->save(Company::forceCreate([
'user_id' => $user->id,
'name' => explode(' ', $user->name, 2)[0]."'s Company",
'personal_company' => true,
]));
}
}
class Register extends FilamentRegister
{
public function form(Form $form): Form
{
return $form
->schema([
$this->getNameFormComponent(),
$this->getEmailFormComponent(),
$this->getPasswordFormComponent(),
$this->getPasswordConfirmationFormComponent(),
])
->statePath('data')
->model(User::class);
}

public function register(): ?RegistrationResponse
{
parent::register();

$user = $this->createUser();

event(new Registered($user));

Filament::auth()->login($user);

session()->regenerate();

return app(RegistrationResponse::class);
}

public function createUser(): User
{
$data = $this->form->getState();

return DB::transaction(function () use ($data) {
return tap(User::create($data), function (User $user) {
$this->createCompany($user);
});
});
}

protected function createCompany(User $user): void
{
$user->ownedCompanies()->save(Company::forceCreate([
'user_id' => $user->id,
'name' => explode(' ', $user->name, 2)[0]."'s Company",
'personal_company' => true,
]));
}
}
2 replies
FFilament
Created by Andrew Wallo on 8/16/2023 in #❓┊help
Overriding Registration Form does not work.
I made a custom page. Literally copied the same content from the Registration form and I get error: "email is already taken". The email was not already taken. I then refresh the page and then it takes me to the tenant creation page.
2 replies
FFilament
Created by Andrew Wallo on 8/13/2023 in #❓┊help
Anyone get these weird dark streaks in dark mode?
18 replies
FFilament
Created by Andrew Wallo on 8/10/2023 in #❓┊help
Reactivity Issue with ColorPicker Field When "id" Attribute from my Model is Present
In my application, I'm using a ColorPicker field to allow users to select a color. Everything works fine until I include the "id" attribute in the data array passed to fill the form. When the "id" attribute is present, the ColorPicker field loses its reactivity. Specifically, after selecting a color from the color pallet, the field does not retain the selected color, and the color pallet does not remain open. The browser console also throws a ton of errors when it is included versus when it is not included. Note: I noticed this when using:
$this->form->fill($this->invoice->attributesToArray());
$this->form->fill($this->invoice->attributesToArray());
So I tested this out to see what the problem was, and I have to say this is very weird. This results in zero errors and the Color Picker is working fine.
public function mount(): void
{
$this->form->fill([
'number_prefix' => $this->invoice->number_prefix,
'number_digits' => $this->invoice->number_digits,
'number_next' => $this->invoice->number_next,
'payment_terms' => $this->invoice->payment_terms,
'accent_color' => $this->invoice->accent_color,
'template' => $this->invoice->template,
]);
}
public function mount(): void
{
$this->form->fill([
'number_prefix' => $this->invoice->number_prefix,
'number_digits' => $this->invoice->number_digits,
'number_next' => $this->invoice->number_next,
'payment_terms' => $this->invoice->payment_terms,
'accent_color' => $this->invoice->accent_color,
'template' => $this->invoice->template,
]);
}
This on the other hand makes the color picker lose its selection after the first choice in color (which it doesn't usually do).
public function mount(): void
{
$this->form->fill([
'id' => $this->invoice->id;
'number_prefix' => $this->invoice->number_prefix,
'number_digits' => $this->invoice->number_digits,
'number_next' => $this->invoice->number_next,
'payment_terms' => $this->invoice->payment_terms,
'accent_color' => $this->invoice->accent_color,
'template' => $this->invoice->template,
]);
}
public function mount(): void
{
$this->form->fill([
'id' => $this->invoice->id;
'number_prefix' => $this->invoice->number_prefix,
'number_digits' => $this->invoice->number_digits,
'number_next' => $this->invoice->number_next,
'payment_terms' => $this->invoice->payment_terms,
'accent_color' => $this->invoice->accent_color,
'template' => $this->invoice->template,
]);
}
Video:
10 replies
FFilament
Created by Andrew Wallo on 8/7/2023 in #❓┊help
Customizing Profile Page in Tenant Panel
How do I customize the base profile page for a user who happens to belong to a panel that has tenants? I keep getting an error such as:
Missing required parameter for [Route: filament.company.pages.dashboard] [URI: company/{tenant}] [Missing parameter: tenant].
Missing required parameter for [Route: filament.company.pages.dashboard] [URI: company/{tenant}] [Missing parameter: tenant].
7 replies
FFilament
Created by Andrew Wallo on 8/5/2023 in #❓┊help
Understanding Multi-Tenant Architecture in Filament V3
Hello everyone, I'm currently working on a project using Filament V3 and I'm trying to get a clear understanding of how multi-tenancy is handled compared to something like Laravel Jetstream's Teams feature. My application involves a scenario where users can belong to multiple companies (tenants) and switch between them, similar to switching between teams in Jetstream. I'm exploring how to best design this multi-tenancy structure using Filament V3. Here are a few specific questions I have: 1. How does Filament V3 handle tenant switching in the database? Is there a recommended method to manage this process? 2. How does Filament register, switch, and apply the current tenant in its architecture? Is there any session or user property involved, or does it use a different mechanism? 3. How does the multi-tenancy design in Filament V3 compare to the Teams feature in Laravel Jetstream? Are there any key differences I should be aware of? 4. Any guidance or insights on this topic would be greatly appreciated, especially if you've implemented multi-tenancy in Filament V3 before. Thanks in advance for your help!
5 replies
FFilament
Created by Andrew Wallo on 7/4/2023 in #❓┊help
The selected {field} is invalid?
29 replies
FFilament
Created by Andrew Wallo on 7/3/2023 in #❓┊help
Problem with Reactive Searchable Select
17 replies
FFilament
Created by Andrew Wallo on 5/18/2023 in #❓┊help
Setting value of Select field with Boolean value options does not work.
Using afterStateUpdated() to set the value of another field, which is a Select component that has Boolean options does not work. Example:
Forms\Components\Select::make('code')
->label('Code')
->options(Currency::getCurrencyCodes())
->searchable()
->reactive()
->afterStateUpdated(static function (callable $set, $state) {
$code = $state;
$symbol_first = config("money.{$code}.symbol_first");

$set('symbol_first', $symbol_first);
})
->required(),
Forms\Components\Select::make('symbol_first')
->label('Symbol Position')
->options([
true => 'Before Amount',
false => 'After Amount',
])
->required(),
Forms\Components\Select::make('code')
->label('Code')
->options(Currency::getCurrencyCodes())
->searchable()
->reactive()
->afterStateUpdated(static function (callable $set, $state) {
$code = $state;
$symbol_first = config("money.{$code}.symbol_first");

$set('symbol_first', $symbol_first);
})
->required(),
Forms\Components\Select::make('symbol_first')
->label('Symbol Position')
->options([
true => 'Before Amount',
false => 'After Amount',
])
->required(),
That does not work, but using a Radio component instead with the same method does work:
Forms\Components\Select::make('code')
->label('Code')
->options(Currency::getCurrencyCodes())
->searchable()
->reactive()
->afterStateUpdated(static function (callable $set, $state) {
$code = $state;
$symbol_first = config("money.{$code}.symbol_first");

$set('symbol_first', $symbol_first);
})
->required(),
Forms\Components\Radio::make('symbol_first')
->label('Symbol Position')
->options([
true => 'Before Amount',
false => 'After Amount',
])
->required(),
Forms\Components\Select::make('code')
->label('Code')
->options(Currency::getCurrencyCodes())
->searchable()
->reactive()
->afterStateUpdated(static function (callable $set, $state) {
$code = $state;
$symbol_first = config("money.{$code}.symbol_first");

$set('symbol_first', $symbol_first);
})
->required(),
Forms\Components\Radio::make('symbol_first')
->label('Symbol Position')
->options([
true => 'Before Amount',
false => 'After Amount',
])
->required(),
Am I doing something wrong here? How do I get this to work using the Select component?
2 replies