FilamentPHP 4 Beta

Publicado por Andrés López

FilamentPHP 4 Beta

El equipo de Filament ha lanzado finalmente la beta de Filamentphp 4, con unas mejoras increíbles de velocidad y un montón de nuevas herramientas. Abordaré la mayoría de los cambios hasta ahora vistos, aquí tienes un pequeño índice para que no te pierdas nada:

Como probar la Beta

Puedes revisar la nueva documentación para ver los nuevos cambios.

Para instalar la versión Beta primero tu proyecto debe permitir versiones no estables, corre el siguiente comando en tu aplicación:

composer config minimum-stability beta

Después corre la instalación de Filament

composer require filament/filament:"^4.0"

php artisan filament:install --panels

Y eso es todo, el resto es lo mismo que la versión 3.

General

Rendimiento

El renderizado y la interacción en general ha sido retrabajado y mejorado enormemente, sobre todo a la hora de manejar mucha información, se redujo la cantidad de blade necesario para mostrar componentes, se redujo el HTML y las clases de TailwindCSS utilizadas.

TailwindCSS

Es oficial Filament tiene soporte para la nueva versión de TailwindCSS ahora tendrás que usar oklch para configurar tus temas:

use Filament\Support\Facades\FilamentColor;

FilamentColor::register([
    'danger' => [
        50 => 'oklch(0.969 0.015 12.422)',
        100 => 'oklch(0.941 0.03 12.58)',
        200 => 'oklch(0.892 0.058 10.001)',
        300 => 'oklch(0.81 0.117 11.638)',
        400 => 'oklch(0.712 0.194 13.428)',
        500 => 'oklch(0.645 0.246 16.439)',
        600 => 'oklch(0.586 0.253 17.585)',
        700 => 'oklch(0.514 0.222 16.935)',
        800 => 'oklch(0.455 0.188 13.697)',
        900 => 'oklch(0.41 0.159 10.272)',
        950 => 'oklch(0.271 0.105 12.094)',
    ],
]);

Autenticación

Añadieron una de las funcionalidades más solicitadas y es el doble factor de autenticación, soportando aplicaciones como Google Auhtenticator, Authy, etc o puedes usar la implementación de correo para enviar contraseñas de un solo uso.

Aunque es un poco más complicado de lo esperado la implementación sigue siendo bastante más sencillo que implementarlo tu mismo, además de permitirte agregar tu propia lógica:

use Filament\Auth\MultiFactor\App\AppAuthentication;
use Filament\Auth\MultiFactor\Email\EmailAuthentication;
use Filament\Panel;

public function panel(Panel $panel): Panel
{
    return $panel
        // ...
        ->multiFactorAuthentication([
             AppAuthentication::make()
                ->recoverable()
                ->recoveryCodeCount(10)
                ->regenerableRecoveryCodes(false)
                ->codeWindow(4)
                ->brandName('Filament Demo'),
            EmailAuthentication::make()
                ->codeExpiryMinutes(2)
        ], isRequired: true);
}

Heroicons

¿Eres un desquiciado del tipado fuerte? Ahora puedes usar los heroicons como enum:

use Filament\Support\Icons\Heroicon;

Heroicon::OutlinedStar;
Heroicon::Star;

Timezone por defecto

Ahora puedes definir un timezone específico para Filament:

use Filament\Support\Facades\FilamentTimezone;

public function boot(): void
{
    FilamentTimezone::set('America/Mexico_City');
}

Resources

Recursos anidados

Ahora por fin puedes añadir recursos anidados, esto es especialmente útil si la relación que estas manejando es demasiado grande para ser creado/editado mediante un modal, ahora puedes crear un recurso anidado para darle una página entera, de cualquier manera se sigue recomendando utilizar relationManagers para manejar el listado de la relación.

Cambio en la organización de archivos

Ahora cada resource tendrá su propio namespace para permitir una mejor organización:

Anteriormente             Ahora

Filament/                 Filament/
├─Resources/              ├─Resources/
├───UserResource/         ├───Users/
└───UserResource.php      └─────UserResource.php

Recomendaciones de código

En toda la documentación agregaron nuevos ejemplos y recomendaciones de como dividir tu código para que sea escalable, entendible y mantenible.

Personalización del contenido

Ahora hay un nuevo método content para configurar como se ve cada página de listar, crear, editar y ver:

use Filament\Schemas\Components\EmbeddedTable;
use Filament\Schemas\Components\RenderHook;
use Filament\Schemas\Schema;

public function content(Schema $schema): Schema
{
    return $schema
        ->components([
            $this->getTabsContentComponent(), // This method returns a component to display the tabs above a table
            RenderHook::make(PanelsRenderHook::RESOURCE_PAGES_LIST_RECORDS_TABLE_BEFORE),
            EmbeddedTable::make(), // This is the component that renders the table that is defined in this resource
            RenderHook::make(PanelsRenderHook::RESOURCE_PAGES_LIST_RECORDS_TABLE_AFTER),
        ]);
}

Ahora el sidebar y el topbar de tu panel también son componentes de livewire, permitiendo actualizarlo junto a acciones de livewire:

use Filament\Actions\Action;
use Livewire\Component;

Action::make('create')
    ->action(function (Component $livewire) {
        // ...
    
        $livewire->dispatch('refresh-sidebar');
        $livewire->dispatch('refresh-topbar');
    })

Schemas

Ahora los forms y los infolists están por debajo de Schema, lo cual los hace compatibles entre los dos:

use Filament\Forms\Components\Checkbox;
use Filament\Forms\Components\Select;
use Filament\Forms\Components\TextInput;
use Filament\Infolists\Components\TextEntry;
use Filament\Schemas\Components\Section;

$schema
    ->components([
        Grid::make(2)
            ->schema([
                Section::make('Details')
                    ->schema([
                        TextInput::make('name'),
                        Select::make('position')
                            ->options([
                                'developer' => 'Developer',
                                'designer' => 'Designer',
                            ]),
                        Checkbox::make('is_admin'),
                    ]),
                Section::make('Auditing')
                    ->schema([
                        TextEntry::make('created_at')
                            ->dateTime(),
                        TextEntry::make('updated_at')
                            ->dateTime(),
                    ]),
            ]),
    ])

Tabs verticales

Ahora puedes colocar tus tabs de tus formularios e infolist de manera vertical:

use Filament\Schemas\Components\Tabs;
use Filament\Schemas\Components\Tabs\Tab;

Tabs::make('Tabs')
    ->tabs([

    ])
    ->vertical();

Vertical tabs

Container queries

Con la implementación de TailwindCSS 4 la adición de los container queries tambien serán compatibles con FilamentPHP:

use Filament\Schemas\Components\Grid;

Grid::make()
    ->gridContainer()
    ->columns([
        '@md' => 3,
        '@xl' => 4,
    ])
    ->schema([
        // ...
    ])

Forms

Nuevo editor enriquecido

El editor enriquecido de FilamentPHP ahora utilizará Tiptap lo cual da mucho mejores herramientas, permitiendo guardar como json, añadir bloques personalizados, tags personalizadas para tener contenido calculado, extensión de funcionalidades mediante plugins del editor:

TipTap

Nuevo slider

Ahora puedes usar un slider en tus forms, permitiendo usar rangos menos y mayores totalmente personalizables.

Slider

Editor de código

Ahora tienes un editor de código con opción de resaltado de código:

Editor de código

Table repeaters

Una de las mejores adiciones es la mejora de los repeaters que ahora pueden convertirse en tablas:

Table repeater

Javascript para evitar peticiones HTTP

Una de las mayores debilidades de Livewire es la necesidad constante de realizar peticiones HTTP para sincronizar los states de las distintas propiedades y con FilamentPHP esto es un problema aún mayor al tener todo escrito en PHP, con la nueva versión se agregan 2 nuevos métodos: hiddenJs() y visibleJs()

Toggle::make('is_admin')
    ->hiddenJs(<<<'JS'
        $get('role') !== 'staff'
        JS)

Adicional a lo anterior, ahora cuentas con el objeto JsContent que permite evaluar js directamente en cualquier atributo de tus componentes:

use Filament\Forms\Components\TextInput;
use Filament\Schemas\JsContent;

TextInput::make('greetingResponse')
    ->label(JsContent::make(<<<'JS'
        ($get('name') === 'John Doe') ? 'Hello, John!' : 'Hello, stranger!'
        JS
    ))

Y por último pero no menos importante también un nuevo método para cuando se actualizan los estados desde js:

use Filament\Forms\Components\TextInput;

TextInput::make('name')
    ->afterStateUpdatedJs(<<<'JS'
        $set('email', ($state ?? '').replaceAll(' ', '.').toLowerCase() + '@example.com')
        JS);

Agrupar componentes

Aunque recuerdo que esto ya se podía hacer en Filament 3, esta vez puedes unir aún más componentes:

  • text inputs
  • selects
  • date-time pickers
  • color pickers

Fused group

Contenido extra para todos los componentes

Ahora todos los componentes cuentan con nuevas secciones donde puedes añadir el contenido que necesites:

  • aboveLabel(), beforeLabel(), afterLabel(), belowLabel()
  • aboveLabel(), beforeLabel(), afterLabel(), belowLabel()
  • aboveErrorMessage(), belowErrorMessage()

Estos métodos también aceptan otros componentes:

content

Renderizado parcial

Anteriormente si necesitabas sincronizar estados entre componentes o mismamente con el backend, tenías que usar el método live()ahora Filament 4 añade nuevos métodos para solo tomar efecto cuando lo que desees cambie:

  • partiallyRenderComponentsAfterStateUpdated() vuelve a renderizar solamente un componente en específico después que el estado se actualiza
  • partiallyRenderAfterStateUpdated() vuelve a renderizar solamente el componente mismo
  • skipRenderAfterStateUpdated() previene y vuelve a renderizar.
use Filament\Forms\Components\TextInput;

TextInput::make('name')
    ->live()
    ->partiallyRenderComponentsAfterStateUpdated(['email'])

Mejoras en el casting

Si usas casting para tus selects por ejemplo, ahora las utildidades al acceder a esos valores dará instancias del enum y no los string, facilitando la codificación y entendimiento:

Select::make('status')
    ->label(function(?StatusEnum $state): string {
        return $state?->value ?? 'Label';
    });

Infolists

Code entry

Ahora puedes mostrar código formateado en tus infolist:

use Filament\Infolists\Components\CodeEntry;
use Phiki\Grammar\Grammar;

CodeEntry::make('code')
    ->grammar(Grammar::Php)

Code entry

Tables

Información personalizada

Anteriormente para mostrar información que no proveniera de base de datos era necesario utilizar paquetes como sushi/sushi que aprovechan los modelos para mostrar arrays, con Filament 4 ahora puedes directamente definir tu lógica para mostrar esta información, permitiendo acceder a API (es el ejemplo más claro) e implementar tu propia lógica de obtención de datos:

use Filament\Tables\Table;
use Illuminate\Pagination\LengthAwarePaginator;

public function table(Table $table): Table
{
    return $table
        ->records(function (
                    ?string $sortColumn,
                    ?string $sortDirection,
                    ?string $search,
                    array $filters,
                    int $page,
                    int $recordsPerPage
                ): LengthAwarePaginator {
                    ...



                    return new LengthAwarePaginator(
                        items: $response['products'],
                        total: $response['total'],
                        perPage: $recordsPerPage,
                        currentPage: $page
                    );
                })
        ->columns([
            ...
        ])
        ->filters([
            ....
        ])
        ->searchable();
}

Columnas visibles todo el tiempo

Ahora aunque no existan datos en las tablas estás mostrarán las columnas, mejorando bastante la experiencia de usuario.

Barra de acciones para tablas

Ahora las tablas contarán con una toolbar donde puedes añadir acciones normales y acciones másivas (bulk actions).

Table toolbar

Actions

Acciones unificadas

Ahora ya no necesitas definir un namespace específico para tablas y acciones generales, ahora todas serán Filament\Actions

Bulk actions

Rendimiento

Ahora cuentas con un nuevo método chunkSelectedRecords() que permite dividr los registros seleccionados por chunks:

use Filament\Actions\BulkAction;
use Illuminate\Support\LazyCollection;

BulkAction::make()
    ->chunkSelectedRecords(250)
    ->action(function (LazyCollection $records) {
        // Process the records...
    })

Autorización

Ahora puedes autorizar registros individualmente acciones mediante policies:

use Filament\Actions\BulkAction;
use Illuminate\Database\Eloquent\Collection;

BulkAction::make('delete')
    ->requiresConfirmation()
    ->authorizeIndividualRecords('delete')
    ->action(fn (Collection $records) => $records->each->delete())

Notificaciones

Ahora las acciones masivas contarán con notificaciones:

use Filament\Actions\BulkAction;

BulkAction::make('delete')
        ->successNotificationTitle('Deleted users')
        ->failureNotificationTitle(function (int $successCount, int $totalCount): string {
            if ($successCount) {
                return "{$successCount} of {$totalCount} users deleted";
            }

            return 'Failed to delete any users';
        })

Rate limit

Ahora las acciones tienen un límite de tiempo para ser ejecutadas nuevamente, lo puedes definir con el método rateLimit():

use Filament\Actions\Action;

Action::make('delete')
    ->rateLimit(5) // minutes

Autorización

Ahora puedes añadir mensajes de autorización en los tooltips y notificaciones:

use Filament\Actions\Action;

Action::make('edit')
    ->url(fn (): string => route('posts.edit', ['post' => $this->post]))
    ->authorize('update')
    ->authorizationTooltip()
    ->authorizationNotification()

Importar relaciones

Ahora puedes realizar importanciones de relaciones belongsToMany

use Filament\Actions\Imports\ImportColumn;

ImportColumn::make('author')
    ->relationship()

Acción de exportar

Ahora puedes estilizar las columnas del xlsx y el writter:

use OpenSpout\Common\Entity\Row;
use OpenSpout\Common\Entity\Style\Style;
use OpenSpout\Writer\XLSX\Options;

/**
 * @param array<mixed> $values
 */
public function makeXlsxRow(array $values, ?Style $style = null): Row
{
    return Row::fromValues($values, $style);
}

public function getXlsxWriterOptions(): ?Options
{
    $options = new Options();
    $options->setColumnWidth(10, 1);
    $options->setColumnWidthForRange(12, 2, 3);
    
    return $options;
}

Tooltips para acciones deshabilitadas

Ahora puedes mostrar un tooltip para indicar las razones de porque una acción esta deshabilitada con el método tooltip().

Widgets

Charts colapsables

Ahora los chart widgets se pueden colapsar:

protected bool $isCollapsible = true;

Filtros personalizados para chart widgets

Ahora puedes filtrar la info para in widgets usando el HasFilterSchema trait.

Dashboard

Ahora soporta el sistema de grid de Filament:

public function getColumns(): int | string | array
{
    return [
        'md' => 4,
        'xl' => 5,
    ];
}

Multi tenacy

Ahora el multitenacy también se aplica a todos los ciclos y eventos.

Nuevas variantes de reglas

Debido a funcionamientos raros con las reglas unique y exists, Filament añade nuevas para respetar el multi tenacy

  • scopedUnique
  • scopedExists

Configuración del panel

Fuentes

Ahora la fuente Inter se cargará de manera local.

Modo estricto

Por defecto si Filament no detecta policies permite seguir navagando y usando tus resources, si acticas el modo estricto arrojará una excepción cada que no encuentre una policy.

use Filament\Panel;

public function panel(Panel $panel): Panel
{
    return $panel
        // ...
        ->errorNotifications(false);
}

Verificación de cambio de correo

Ahora si tienes activado el perfil en tu panel, para cambiar tu correo tendrás que confirmar mediante un email válido por 60 minutos.

Notificaciones de error

Ahora puedes personalizar como los mensajes de error aparecen en tu panel, incluso por código de error:

use Filament\Panel;

public function panel(Panel $panel): Panel
{
    return $panel
        // ...
        ->registerErrorNotification(
            title: 'An error occurred',
            body: 'Please try again later.',
        )
        ->registerErrorNotification(
            title: 'Record not found',
            body: 'A record you are looking for does not exist.',
            statusCode: 404,
        );
}

Eso es todo lo nuevo que Filament 4 beta tiene hasta ahora

Andrés López

Andrés López

Gran fan de Laravel, entusiasta de Vue y escritor de cualquier cosa que suene interesante