amjadiqbal/filament-url-image-uploader
A Filament form field that uploads images from a file or a pasted URL, fetching and validating remote images server-side. Saves a plain storage path string, with configurable directory, disk, filename preservation, and a URL download size limit.
Architecture fit. This package is a thin Filament form-field decorator, not a platform component. UrlImageUploader composes a native FileUpload and a TextInput inside a two-tab wrapper, dehydrating to a plain string storage path on a single model column. That is the correct architectural choice for a CMS/e-commerce admin: no custom cast, no accessor/mutator, no JSON column, and the value is directly consumable via Storage::disk(...)->url(). It slots into an existing Filament Resource schema as a one-line swap for FileUpload::make('image') and imposes no schema or domain-layer coupling. For a TPM, this is a low-blast-radius dependency — it lives entirely in the presentation layer.
Integration feasibility. High. composer require, one use statement, one field in a form schema. Configuration surface is small and legible: ->directory(), ->disk(), ->preserveFilenames(), ->maxUrlFetchSize(). No migrations, no config publishing, no service provider wiring required by the consumer. Realistic effort for a working prototype is under a day; production hardening is the real cost, not integration.
Technical risk. This is where the assessment turns negative.
getChildComponentContainer(), which then broke every conventional CreateRecord/EditRecord page (No property found for validation: [image] on every save) and required a rewrite in 1.1.0. Five releases in rapid succession, each correcting hydration/dehydration/state-path defects, is a signature of a component that was shipped without end-to-end tests. The permanent Testbench+Livewire suite only arrived in 1.1.0 — meaning the currently good version is the first one that was verified against a real Filament Resource page.http/https scheme allowlist, a 10 MB byte cap, and finfo content-type validation — all good, and the scheme check is a deliberate fix for file:// / data:. But there is no documented block on private/link-local address space (127.0.0.1, 10/8, 169.254.169.254, internal hostnames) and no stated handling of redirects or DNS rebinding. Any authenticated admin can make the application server issue HTTP requests to arbitrary hosts and observe success/failure plus timing. That is an internal-network reconnaissance primitive and, on cloud, a metadata-endpoint risk.Key questions for the maintainer.
allow_redirects bounded?preserveFilenames(), what is the collision/overwrite behaviour?SECURITY.md with a private disclosure route?Stack fit. Requires Filament 3.x on Laravel with the Storage facade. Works with any configured disk, so S3-compatible object storage is supported (->disk('s3')) — relevant if the platform standardises on S3 rather than local public. The plain-string persistence means downstream consumers (API resources, image transforms, CDN URL builders) need no knowledge of this package. Exit cost is one line of schema code.
Migration path. Lowest-friction path is greenfield fields: use UrlImageUploader on new image columns. For existing FileUpload::make('image') single-file fields, the swap is behaviourally compatible if those fields already dehydrate to a string path — verify per-field, because multi-file or array-dehydrating FileUpload configurations would not map cleanly onto a string, nullable column, and this component explicitly does not support multiple(). For galleries, plan one field per image or a Repeater of the component; budget for the Repeater nesting case being the least-tested configuration, since 1.1.0's central bug was losing state-path context under nesting.
Compatibility. Pin an exact version (1.1.0 or later) — do not use a ^1.0 constraint, because 1.0.x contains a defect that breaks saving on every Resource page. Confirm the Filament minor version in use and re-run the smoke test on every Filament upgrade.
Sequencing.
Section/Repeater. These are precisely the flows that regressed historically.Maintenance. Ongoing cost is low in code terms and moderate in diligence terms: one Composer dependency, no runtime services, no migrations. The real burden is version watching — a solo-maintainer package with a demonstrated history of state-path regressions needs its changelog read on every bump, and needs the smoke tests above kept in CI so an upgrade cannot silently break saving. Treat it as a "pin and verify" dependency, not a "commit and forget" one.
Support. Community Discord plus a paid Upwork channel. There is no commercial SLA, no vendor to escalate to, and the security reporting route is a Discord channel — which most security teams will reject outright. Plan to own fixes locally: the component is small enough (one field class, one Blade view) that forking or vendoring it is a realistic contingency, and should be the documented fallback rather than an improvisation.
Scaling. The URL tab performs a synchronous server-side download inside the Livewire request, with a 10-second timeout and up to 10 MB of transfer, plus finfo inspection — all of which occupies a PHP-FPM worker and streams remote bytes through the application tier. At admin-scale concurrency (single-digit simultaneous editors) this is irrelevant. It becomes relevant if the field is exposed to many users, if remote hosts are slow, or if egress is metered — in which case move fetching to a queued job with the URL tab merely capturing the URL. Object storage growth is the other scaling vector: nothing in the package reaps replaced or deleted files, so storage cost grows monotonically unless we add a cleanup job.
Failure modes to instrument.
No property found for validation class of failure; catch via the CI smoke tests, not production.finfo magic-byte check help, but decompression-bomb style images (small file, huge decoded dimensions) are not addressed; the byte cap alone does not bound memory during image processing downstream.preserveFilenames() — two records importing photo.jpg into the same directory is a data-loss path; default to the generated/UUID filenames.Ramp-up. Negligible for a Laravel developer already fluent in Filament: the README covers install, all four configuration methods, the persistence contract, and the multiple() limitation in a few screens. Half a day for a competent dev to be productive; the same day should be spent writing the four regression tests, which is where the actual value is. The absence of a multiple() mode is a scope limitation worth surfacing to product early — if the roadmap includes galleries, assume bespoke work.
Bottom line. Acceptable for internal Filament admin tooling on a pinned >=1.1.0, behind a feature flag, with local regression tests and — if the URL tab is exposed beyond super-admins — an application-level egress guard. Not acceptable as-is for customer-facing or untrusted-user surfaces, and not something to depend on without a fork contingency.
How can I help you explore Laravel packages today?