F
FilamentTrauma Zombie

Reusable sections

Hi, I was wondering how you usually create reusable parts for example e.g. infolists or forms? For example I create my own AddressEntry that I am using on multiple resources. The problem comes when I need to access the Address model. This is my demo custom entry:
<?php

namespace App\Filament\Components\Infolists\Entries;

class AddressEntry
{
public static function make(string $name = 'address'): Entry
{
return TextEntry::make($name)
->label(__('Address'))
->hintAction(
Action::make('openMaps')
->url(fn (Model $record) => match (true) {
$record instanceof User => 'https://google.com/maps/place/'.$record->address->street,
$record instanceof Client => 'https://google.com/maps/place/'.$record->user->address->street,
default => 'https://google.com/maps/place/'.$record->street,
})
->openUrlInNewTab(),
);
}
}
<?php

namespace App\Filament\Components\Infolists\Entries;

class AddressEntry
{
public static function make(string $name = 'address'): Entry
{
return TextEntry::make($name)
->label(__('Address'))
->hintAction(
Action::make('openMaps')
->url(fn (Model $record) => match (true) {
$record instanceof User => 'https://google.com/maps/place/'.$record->address->street,
$record instanceof Client => 'https://google.com/maps/place/'.$record->user->address->street,
default => 'https://google.com/maps/place/'.$record->street,
})
->openUrlInNewTab(),
);
}
}
Model in my closure in url method is taken from resource, when I use it on UserResource, it is User etc. It kinda works, but I don't like this implementation. Do you know better way?
B
bernhard41d ago
What exactly is your problem with your implementation? Why don't you like it? One point for me is, that the wording doesn't make sense in a laravel context. In most cases in laravel, a static makemethod would create an instance of the current class (in this case AddressEntry). All Filament classes behave like that, the Model::make does it etc. Your code created not an instance of AddressEntry but an instance of TextEntry. So I think its a bad idea to use the word make. I would suggest to be more generic and use this class for other common fields/entries, otherwise you would have to create separete classes for every szenario. Just my two cents 😉
TZ
Trauma Zombie41d ago
I really appreciate your comments. But could you be more specific?
B
bernhard41d ago
I don't know your exact scenario so it is a bit hard to be very specific, but I will try to explain it with FormFields which I centralize in my own projects. For this, I either create a class CommonField where I put static methods in for common used fields, even if there is not that much logic on the field. For example:
class CommonFields
{
public static function cols(): Select
{
return Select::make("cols")
->label("Spalten")
->options([
1 => "1",
2 => "2",
3 => "3",
4 => "4",
]);
}


public static function money(string $name): TextInput
{
return TextInput::make($name)
->numeric()
->postfix("€");
}


public static function priority(): TextInput
{
return TextInput::make("priority")
->label("Priorität")
->numeric()
->minValue(-100)
->maxValue(100);
}

public static function parent(string $name = "parent_id", string $relationName = "parent", string $relationNameAttribute = "name"): Select
{
return Select::make($name)
->relationship($relationName, $relationNameAttribute)
->searchable()
->preload()
->label("Eltern-Element");
}
}
class CommonFields
{
public static function cols(): Select
{
return Select::make("cols")
->label("Spalten")
->options([
1 => "1",
2 => "2",
3 => "3",
4 => "4",
]);
}


public static function money(string $name): TextInput
{
return TextInput::make($name)
->numeric()
->postfix("€");
}


public static function priority(): TextInput
{
return TextInput::make("priority")
->label("Priorität")
->numeric()
->minValue(-100)
->maxValue(100);
}

public static function parent(string $name = "parent_id", string $relationName = "parent", string $relationNameAttribute = "name"): Select
{
return Select::make($name)
->relationship($relationName, $relationNameAttribute)
->searchable()
->preload()
->label("Eltern-Element");
}
}
This is of course a matter of taste, but I like it this way, because (for example) all fields with currency values in my project to have the "€" postfix and be numeric, but I don't wanna create for a 3-liner an extra class/file. Of course there are more complex ones, with callbacks 😉 I hope that helps. But as I said, this is a matter of taste
TZ
Trauma Zombie41d ago
Thanks, now I understand what you mean. My example was simplified to make it more clear. I understand your comments, but the problem was more related to the ->url section. I will give you another example where I have extracted multiple fields into one array. My question is about the ->url section. I don't like what I created in closure, but it works.
class CompanyInfolist
{
public static function get(): array
{
return [
Infolists\Components\TextEntry::make('company')
->label(__('Company')),

Infolists\Components\TextEntry::make('business_no')
->label(__('Business No.'))
->hintActions([
Infolists\Components\Actions\Action::make('openBusinessRegister')
->hiddenLabel()
->url(
fn (Model $record) => match (true) {
$record instanceof Contract => $record->client->entity->business_no ? 'https://orsr.sk/hladaj_ico.asp?ICO='.$record->client->entity->business_no : null,
$record instanceof Entity => $record->business_no ? 'https://orsr.sk/hladaj_ico.asp?ICO='.$record->business_no : null,
default => $record->entity->business_no ? 'https://orsr.sk/hladaj_ico.asp?ICO='.$record->entity->business_no : null,
}
)
->openUrlInNewTab()
->tooltip(__('Visit business register')),

]),

Infolists\Components\TextEntry::make('tax_no')
->label(__('Tax No.')),

Infolists\Components\TextEntry::make('vat_no')
->label(__('VAT No.')),

Infolists\Components\TextEntry::make('iban')
->label(__('IBAN')),
];
}
}
class CompanyInfolist
{
public static function get(): array
{
return [
Infolists\Components\TextEntry::make('company')
->label(__('Company')),

Infolists\Components\TextEntry::make('business_no')
->label(__('Business No.'))
->hintActions([
Infolists\Components\Actions\Action::make('openBusinessRegister')
->hiddenLabel()
->url(
fn (Model $record) => match (true) {
$record instanceof Contract => $record->client->entity->business_no ? 'https://orsr.sk/hladaj_ico.asp?ICO='.$record->client->entity->business_no : null,
$record instanceof Entity => $record->business_no ? 'https://orsr.sk/hladaj_ico.asp?ICO='.$record->business_no : null,
default => $record->entity->business_no ? 'https://orsr.sk/hladaj_ico.asp?ICO='.$record->entity->business_no : null,
}
)
->openUrlInNewTab()
->tooltip(__('Visit business register')),

]),

Infolists\Components\TextEntry::make('tax_no')
->label(__('Tax No.')),

Infolists\Components\TextEntry::make('vat_no')
->label(__('VAT No.')),

Infolists\Components\TextEntry::make('iban')
->label(__('IBAN')),
];
}
}
I use this array inside my ViewContract and ViewEntity pages.
B
bernhard41d ago
Ok, but why don't you like it? I don't get the point? That your argument $record is a Model instead of User? If you would be lazy, you wouldn't even need a type at all 😄
TZ
Trauma Zombie41d ago
I use it like this:
// UserResource.php

Infolists\Components\Group::make()
->relationship('entity')
->schema([
...CompanyInfolist::get(),
])
// UserResource.php

Infolists\Components\Group::make()
->relationship('entity')
->schema([
...CompanyInfolist::get(),
])
B
bernhard41d ago
You could move the callback back to the Resource:
class CompanyInfolist
{
public static function get(callable $cb): array
{
return [
Infolists\Components\TextEntry::make('company')
->label(__('Company')),

Infolists\Components\TextEntry::make('business_no')
->label(__('Business No.'))
->hintActions([
Infolists\Components\Actions\Action::make('openBusinessRegister')
->hiddenLabel()
->url($cb)
->openUrlInNewTab()
->tooltip(__('Visit business register')),

]),

Infolists\Components\TextEntry::make('tax_no')
->label(__('Tax No.')),

Infolists\Components\TextEntry::make('vat_no')
->label(__('VAT No.')),

Infolists\Components\TextEntry::make('iban')
->label(__('IBAN')),
];
}
}
class CompanyInfolist
{
public static function get(callable $cb): array
{
return [
Infolists\Components\TextEntry::make('company')
->label(__('Company')),

Infolists\Components\TextEntry::make('business_no')
->label(__('Business No.'))
->hintActions([
Infolists\Components\Actions\Action::make('openBusinessRegister')
->hiddenLabel()
->url($cb)
->openUrlInNewTab()
->tooltip(__('Visit business register')),

]),

Infolists\Components\TextEntry::make('tax_no')
->label(__('Tax No.')),

Infolists\Components\TextEntry::make('vat_no')
->label(__('VAT No.')),

Infolists\Components\TextEntry::make('iban')
->label(__('IBAN')),
];
}
}
TZ
Trauma Zombie41d ago
It is not about types. It is about that how to access that business_no attribute. For example when I have this inside my UserResource closure is like this: fn ($record) => $record->entity->business_no, but when it is inside ContractResource it is like this: fn ($record) => $record->client->entity->business_no.
B
bernhard41d ago
And use it like:
CompanyInfolist::get(fn (Contract $record) => $record->client->entity->business_no ? 'https://orsr.sk/hladaj_ico.asp?ICO='.$record->client->entity->business_no : null);
CompanyInfolist::get(fn (Contract $record) => $record->client->entity->business_no ? 'https://orsr.sk/hladaj_ico.asp?ICO='.$record->client->entity->business_no : null);
depending on the resource
TZ
Trauma Zombie41d ago
Or just accept that Entity model, that holds business_no, right? Maybe I was kind of hoping that there's some way to get to the Entity model even when I'm on a UserResource or ContractResource without having to push the model there directly.
B
bernhard41d ago
Well or another way would be to implement it on the model itself and just call it then
url(fn (Model $record) => $record->getBusinessNo())
url(fn (Model $record) => $record->getBusinessNo())
so you would implement the method getBusinessNo() on both the Contract and the Entity model
TZ
Trauma Zombie41d ago
I will probably do this:
public static function get(Entity $record): array
public static function get(Entity $record): array
Because I am doing more actions with this model inside that CompanyInfolist, not just one URL.
B
bernhard41d ago
ok, but this would still work, just without the callback:
url($record->getBusinessNo())
url($record->getBusinessNo())
or
url($record->getBusinessNoUrl())
url($record->getBusinessNoUrl())
Btw. if you keep the origional version, I would do something with dublication of the url-string. for example:
->url(function (Model $record) {
$no = match (true) {
$record instanceof Contract => $record->client->entity->business_no ?? null,
$record instanceof Entity => $record->business_no ?? null,
default => $record->entity->business_no ?? null,
};

return $no ? 'https://orsr.sk/hladaj_ico.asp?ICO='. $no : null;
})
->url(function (Model $record) {
$no = match (true) {
$record instanceof Contract => $record->client->entity->business_no ?? null,
$record instanceof Entity => $record->business_no ?? null,
default => $record->entity->business_no ?? null,
};

return $no ? 'https://orsr.sk/hladaj_ico.asp?ICO='. $no : null;
})
because in your example, you have trippled the 'https://orsr.sk/hladaj_ico.asp?ICO=' or the new cleaner version:
->url(function (Model $record) {
$no = $record->getBusinessNo();

return $no ? 'https://orsr.sk/hladaj_ico.asp?ICO='. $no : null;
})
->url(function (Model $record) {
$no = $record->getBusinessNo();

return $no ? 'https://orsr.sk/hladaj_ico.asp?ICO='. $no : null;
})
TZ
Trauma Zombie41d ago
Right, I understand. Thank you very much. If you still have a moment, you could take a look at my other problem I'm having. https://discord.com/channels/883083792112300104/1214858935425703958
Want results from more Discord servers?
Add your server
More Posts
filtersForm()Im using custom livewire page with widget with table. I have defined filter form and $this->filters JS error : "Livewire assets are out of date"Hi guys, I made a few changes on my Filament app and now login isn't working anymore and I think it'It's possible to put the relation on top of form page?I have a big form. After create a record ( with doesn't have the relation fields already ), a user dWork with existing tables without deleting their contents when doing migrationsHi guys, I'd like to work with already existing data in the database. Unfortunately the table is deAction opening an empty state modalI'm using Action in a spatie livewire wizard component. When I click on next button once, the step iBlock Repeater Rating with MorphToMany RelationGreetings, I have a block with this form inside class Rating { public static function make( Ignore using filament:assets for packageI am using this package for inspiration: https://github.com/cheesegrits/filament-google-maps and fouHow to increase Pest test coverageHey, I am attempting to increase my test coverage to 100% for my resources and can't seem to figurHow to put images in a textarea with filament ?I've tried a lot of things but when I insert my images in a textarea, and that I go back to the fronfile uploading to S3 on vapor stuck on uploadingI have set a new server on Laravel Vapor, the S3 was generated by vapor with full public access, theUse filament Components outside filamentCan I use filament components like a table outside filament assuming I have a simple laravel app witDoes --generate flag work when creating a resource?To test it I: 1- Updated filament libs (`composer update`) & cleared cache `php artisan optimize:clMissing New panel resources - links don't appear on sidebar - 404-Not foundHello. I have been developing an app since the beginning of February and everthing was Ok. I have beNested relation formHi guys, is it possible to create form with nested relations? ```php // ClientResource.php Forms\CoDispatching a livewire event from a filament action when submitHello, I need help dispatching a livewire event from a filament action but none of the answers or meButton loading animation too fastIs this button loading animation intentionally designed like this ? i think it is too fastAction afterStateHydratedHello, i want to fire a Action after a user has chosen something in the dropdown of my form, how canTable widget tab filter in DashboardQuestion, out of the box, is it possible to implement table tab filter like List page class's insideSpa mode black screenwhen action any page like edit page then click back button then black screen coming after hard refreSelect field [mountedActionsData.0.brand_id] must have a [createOptionUsing()] closure set.how i can fix it? code: ``` return $form ->schema([ TextInput::make('na