rekalogika/file-symfony-bridge
Symfony bridge for the Rekalogika File library, integrating file handling, storage abstractions, and metadata with Symfony’s service container, configuration, and framework conventions. Helps wire file repositories and delivery into Symfony applications.
This package is a bridge, not a standalone bundle. It does nothing without the core packages installed and configured. Plan on pulling in at least:
rekalogika/file-interface (the FileInterface abstraction)rekalogika/file-bundle (the Symfony wiring / DI configuration)rekalogika/file-flysystem + a Flysystem adapter, or rekalogika/file-local-filesystemYou only need this bridge when you want FileInterface to play nicely with HttpFoundation uploads, Symfony Forms, and the Validator — which, in a typical Symfony app, is almost immediately.
Where to look first. The bridge's entire surface area lives in three namespaces:
Form/ — the FileType (and friends) form typeValidator/Constraints/ — size, MIME, extension constraints for FileInterfaceHttpFoundation/ — converters/factories between UploadedFile and FileInterface, and helpers for turning a FileInterface back into a ResponseRead the file-bundle README first for YAML configuration (config/packages/rekalogika_file.yaml), then come back to the bridge for the three integration points above.
First use case. The canonical "hello world" is a simple upload form:
FileRepositoryInterface is resolvable.FileInterface property.FileType for that property.UploadedFile into a FileInterface → persist via the repository and save the ID on your entity.Before touching forms, prove the adapter works with a bare round-trip: store a file, get it back by ID, read the stream. If that fails, everything downstream will too, and you'll waste hours debugging the wrong layer.
Form + DTO is the happy path. Model the file as a nullable FileInterface on a DTO:
final class UploadDto
{
public ?FileInterface $file = null;
// other fields...
}
The bridge's form data transformer converts the submitted UploadedFile into a FileInterface before it hits the DTO. Your controller never sees UploadedFile, and your domain never depends on HttpFoundation. This is the entire reason the abstraction exists — lean on it.
Put constraints on the FileInterface property, not the raw upload. The bridge ships validators that target the file abstraction (size, MIME type, extension). Because the transformer has already run, constraints apply to the same object the rest of your app will consume. If you try to validate UploadedFile directly you'll get a type mismatch or a silently skipped constraint.
Manual conversion when you're not using forms. JSON APIs and one-off upload endpoints can skip the form layer entirely. Look for a converter/factory in HttpFoundation/ that takes $request->files->get('file') (an UploadedFile) and returns a FileInterface. Resist the urge to hand-roll it — the bridge handles the details of mapping name, size, MIME, and stream.
Serving files back. The HttpFoundation helpers convert a FileInterface into a Symfony Response (or StreamedResponse/BinaryFileResponse depending on adapter). Use them — do not build Content-Type and Content-Disposition headers yourself. For S3-backed or otherwise remote adapters, prefer streamed responses and set the appropriate headers so large files don't load into memory.
Keep FileInterface in your entities. Store the file ID (or the FileInterface itself, depending on how the bundle's persistence works in your version) — not the UploadedFile or the path. This is what lets you swap local storage for S3 in production without touching entity code.
Swap the adapter in tests. Because the bridge is storage-agnostic, you can point the repository at an in-memory or temp-dir adapter in config/packages/test/ so form and controller tests don't hit real storage. The bridge doesn't ship a test adapter; that comes from the adapter packages.
Almost all configuration lives in file-bundle, not here. If a form type, converter, or constraint isn't behaving, check bundle YAML and the Flex recipe before digging into the bridge. The bridge has a tiny config surface of its own, and blaming it for an adapter misconfiguration is the classic first mistake.
It's marked READ-ONLY upstream. Treat it as fixed. When you need different behavior, decorate the bridge's services in services.yaml (form data transformer, HTTP converters, constraints) rather than editing vendor code.
Empty uploads vs null. The form type will typically leave a nullable property as null when no file is submitted. Decide early if the field is required: either add NotBlank on the DTO property or configure the form type's required flag. Mixing the two (a NotBlank plus a nullable file constraint) generates confusing violation messages.
Type-mismatch errors are the most common failure. Passing an UploadedFile where a FileInterface is expected (or the reverse — e.g. feeding a FileInterface into an API that wants getRealPath()) means you've bypassed the bridge's transformer. If you find yourself reaching for $upload->getRealPath(), you're outside the intended path and will hit adapter-specific bugs.
MIME detection is not universal. The bridge's validators may rely on either the client-declared filename or server-side magic bytes depending on configuration. For security-sensitive uploads, verify what your adapter reports versus what the constraint actually checked, and consider an additional content-based check upstream.
Metadata doesn't persist itself. If your app attaches metadata (original name, alt text, etc.) via the metadata layer, that's a separate configuration from the file adapter. Without the metadata persistence wired up, metadata vanishes on the next request — this bites people who test in a single request and assume it'll survive.
Extension points to know about. The form data transformer, the HTTP converters, and the constraint classes are the natural seams. If the bridge doesn't cover a case (e.g. custom response headers, custom file naming), wrap or decorate those services rather than forking.
Maturity signal. Three stars and zero dependents means the API may shift between minor versions. Pin a specific version in composer.json, and keep at least one integration test that exercises form submit → convert → validate → store → download. That test is your early-warning system for breaking changes in a package that probably won't advertise them loudly.
Debugging tip. When a file "disappears" between the form and the repository, add a dump() in the form data transformer (or temporarily decorate it) to confirm the transformed type. Nine times out of ten the file arrived correctly and a later service silently rejected it or returned null.
How can I help you explore Laravel packages today?