anomaly/files-field_type
Anomaly Files Field Type provides a Streams field type for uploading and managing one or more files in Laravel applications. It integrates file storage with Anomaly's Streams platform and offers a simple way to attach downloadable assets to entries.
This is a Streams Platform field type addon (PyroCMS 3.x lineage), not a standalone Laravel package. It gives you a files field that stores a many-to-many relationship between a stream entry and rows in the Files module's files table. If you aren't already running anomaly/streams-platform, nothing here will work — there is no service provider you can register into a vanilla Laravel app.
Prerequisites, in order:
anomaly/files-module) installed and at least one storage disk/folder configured. The field type is a thin UI over that module; without it you get an empty uploader.composer require anomaly/files-field-type
php artisan addon:install anomaly.field_type.files
php artisan addon:list) before touching any stream definition — an uninstalled field type fails silently at migration time with a "field type does not exist" style error.Where to look first, in this order:
src/FilesFieldType.php — the config array, $inputView, and the relationship definition. This file is the source of truth for every option you can pass in a migration.resources/views/input.twig — the CP uploader markup; this is what you override when the UI doesn't match your admin theme.src/FilesFieldTypePresenter.php — the methods available on the field value in Twig (url, path, thumbnail, etc.). Don't guess these; read the class.src/FilesFieldTypeAccessor.php / Modifier.php — how IDs are read back out of the request and persisted.Your first use case should be a single "attachments" or "gallery" field on one stream — a page with a files field, upload two images in the CP, and render them in Twig. That exercises the whole path (upload → Files module → pivot → presenter) in about fifteen minutes.
Defining the field. Do it in the stream migration, not by hand in the CP, so it's reproducible:
$fields->addField('gallery', 'files', [
'type' => 'anomaly.field_type.files',
'config' => [
'allowed_types' => ['jpg', 'jpeg', 'png', 'webp'],
],
]);
Check FilesFieldType::config() in your installed version for the exact keys — the config surface is small and has changed between releases. Assigning the field creates the pivot table automatically; if you rename the slug later you orphan that table.
Reading the value in Twig. The value is a collection of File presenters, so treat it like any Laravel collection:
{% for file in entry.files %}
<img src="{{ file.url }}" alt="{{ file.name }}">
{% endfor %}
{# count, first, empty checks #}
{% if entry.files is not empty %}
{{ entry.files|length }} file(s)
{% endif %}
Use file.path for filesystem paths and file.url for public links — mixing them up is the single most common bug. If you need the raw model, file.make() returns the underlying FileModel.
Writing the value programmatically. The field behaves like a standard Streams multiple field, so assignment works from PHP:
$entry->files = $fileIds; // array of File module IDs
$entry->save();
If you're syncing manually outside the entry lifecycle, reach for the relationship directly ($entry->files()->sync($ids)) and be aware you skip the field type's accessor/modifier logic.
Uploading outside the CP. The field type's uploader is CP-oriented. For front-end uploads, use the Files module's uploader service and then attach the resulting IDs — don't try to POST to the field type's input endpoint directly, it expects CP auth and the form's session token.
Ordering. Files are stored with pivot ordering; if presentation order matters (a gallery, a carousel), set it explicitly after attach rather than relying on insertion order, which is not guaranteed across versions.
Extending rather than forking. Two clean options: (a) copy resources/views/input.twig into your own addon's view namespace and point the field's $inputView at it via a subclass; (b) subclass FilesFieldType in your own addon and register a new field type slug. Both survive composer update; editing files under vendor/ does not.
anomaly/streams-platform long before you get to this addon. Budget for --ignore-platform-reqs at best, and a fork at worst. If you're greenfielding, use spatie/laravel-medialibrary or plain HasMany uploads instead.files and the bytes on disk stay. Add an observer or a cleanup command, or your storage will grow monotonically.allowed_types is a UI hint, not validation. It filters the file picker client-side. Any server-side enforcement is on you — validate mime and extension in your own request/observer.files. If your stream already declares a files relation, one silently wins. Rename your field's slug or your other relation.{% for file in entry.files %} inside a loop over many entries fires a query per entry unless you eager-load the relation in whatever repository/query you use to fetch the entries.addon:list shows the field type installed; (2) the pivot table exists and has rows (select * from <prefix>files_field_type_<slug> or the generic pivot, depending on version); (3) the Files module actually stored the file — check the CP Files listing, not the entry form; (4) php artisan view:clear after any view override, Twig caches aggressively.vendor/anomaly/files-field-type/resources/... is lost on the next composer update and is invisible to code review.$file->url on the raw model throws; calling it on the presenter works. When in doubt, chain ->make() → presenter, or just use the field value directly in Twig where the presenter is already applied.streams-platform and files-module. These three move in lockstep and a partial upgrade produces the confusing "field type exists but input renders blank" failure mode.How can I help you explore Laravel packages today?