ffhs/filament-package_ffhs_custom_forms
FFHS Custom Forms is a Filament package for Laravel that enables you to create, manage, and reuse dynamic forms with configurable fields and reusable form definitions. It integrates into Filament panels and simplifies collecting and handling form submissions in admin interfaces.
This is a niche package: a Filament plugin maintained by FFHS (Fernfachhochschule Schweiz) that ships a dynamic/custom form builder — definitions, fields, and submissions — as Filament resources. It is aimed at internal FFHS admin panels, not a general-purpose form library, so treat the README as the source of truth and verify behavior against a scratch panel before wiring it into a real app.
Minimal setup:
composer require ffhs/filament-package_ffhs_custom_forms. Check composer.json constraints first — pinned Filament versions are the most common failure mode with small vendor packages.php artisan vendor:publish --tag="filament-package_ffhs_custom_forms-migrations" (and the config tag if present), then php artisan migrate.app/Providers/Filament/AdminPanelProvider.php:
->plugin(\Ffhs\FilamentPackageFfhsCustomForms\FilamentPackageFfhsCustomFormsPlugin::make())
The namespace is the first thing to confirm from the README — small packages sometimes rename classes between releases.CustomForm (or equivalent) resource appears in navigation; create a form, add fields, save, and submit it to confirm the full round-trip before touching any custom code.Where to look first, in order: the plugin class (what resources/pages it registers), the published config (labels, navigation group, disk/storage options), and the migrations (this tells you the data model — forms, fields, submissions, answers — faster than any docs). If the repo has tests/ or src/Models, read those next; with 17 stars, tests are your best documentation of intended usage.
Treat it as a submission engine, not a schema library. The typical lifecycle is: define a form in the admin UI → render it somewhere → persist a submission → read answers back. Get each of those four steps working in isolation before building features on top.
Render forms from your own pages. If the package exposes a Livewire component or a form schema builder, wrap it in a custom Filament page or a Blade view rather than embedding it in a controller. Pattern:
// A custom Filament Page that hosts a published form definition
public static function getFormId(): int
{
return CustomForm::where('slug', 'contact')->firstOrFail()->id;
}
Then mount the package's form component/fields in form(Schema $schema). Confirm whether the package's field types implement Filament\Forms\Components\Field — if so, they compose cleanly into any existing Filament form, which is the most valuable integration point.
Separate definition changes from live submissions. Once a form has answers, editing its field definitions is a schema migration problem in disguise. Practical workflow: version forms (or clone a form) instead of mutating a live one; keep a is_active/published flag and render only active definitions. If the package has no built-in versioning, add it at the application layer — a form_version column on submissions plus a snapshot of the schema in JSON will save you repeatedly.
Multi-step and conditional logic. If the package supports sections/steps, model each step explicitly and validate per step server-side; do not rely on client-side visibility rules for validation. Conditional fields should re-validate on the server against the current answers.
Submissions as first-class models. Rather than reading submissions only through the package's admin resource, query the submission model directly for downstream work — exports, notifications, PDFs, API endpoints. Map answers via the field definitions (keyed by field ID or key) into a stable DTO, and keep that mapping in one service class so a package upgrade only breaks one place.
Notifications and exports. A common day-to-day need is "email someone when a form is submitted." Use model events on the submission model (created) in an observer rather than editing vendor code, and queue the mail. Same for CSV exports: write your own Filament export or a queued job rather than depending on package-provided exporters.
Authorization. Small vendor packages often omit policies. Publish/define policies for the form and submission models and register them in AuthServiceProvider; otherwise any panel user can usually see every submission.
composer.json and test upgrades deliberately; a composer update can silently break form rendering.vendor:publish and diff the config against defaults before debugging odd behavior — navigation group, route slugs, and storage paths are often read from config, not the database.forms, submissions), check the published migrations before running migrate. Small packages sometimes use generic table names without a configurable prefix.->navigationGroup(...)) rather than editing published views, so upstream updates stay cheap.TextInput render in the same page? If yes, the issue is in the package's field component (often a missing view namespace or unpublished assets). Check php artisan filament:assets and view:clear first.storage:link change. Test with local and public disks, and check whether uploads are cleaned up when a submission is deleted.filament/forms plus your own persistence — keep your rendering and DTO layers thin so that exit stays cheap.How can I help you explore Laravel packages today?