carloschininin/attach-file-bundle
Attach File Bundle adds reusable file-attachment support to Symfony/PHP apps, letting you associate uploaded files with Doctrine entities and manage storage through simple, configurable bundle services. It streamlines uploads, entity relationships, and file handling so you can add attachments withou...
Install and register
composer require carloschininin/attach-file-bundle
Symfony Flex may not have a recipe for a package at this maturity level, so expect to register the bundle by hand in config/bundles.php:
CarlosChininin\AttachFileBundle\AttachFileBundle::class => ['all' => true],
Then check whether the bundle ships a DependencyInjection/Configuration.php — if it does, dump the reference config rather than guessing:
php bin/console config:dump-reference attach_file
php bin/console debug:container --tag=twig.extension | grep -i attach # confirm what actually registered
Where to look first
With one star, zero dependents and no ecosystem around it, the README is not a reliable spec — the source is. Before writing any application code, spend fifteen minutes in vendor/carloschininin/attach-file-bundle/:
src/ — the public classes you are meant to touch (likely an entity trait or attribute, a storage/uploader service, a form type, possibly a controller for downloads).DependencyInjection/Configuration.php and Extension.php — the real list of options, storage paths, and service IDs.docs/ or the tests — the only trustworthy usage examples, since nobody else is using this in production.First use case
The happy path for a package of this shape is "attach an uploaded file to a Doctrine entity." Start with the smallest slice: one entity (e.g. Post), one field, local filesystem storage, and a form upload. Get upload → persist → download working end to end before adding storage adapters, image processing, or polymorphic attachment tables. If the bundle exposes an entity trait, use it on a throwaway entity first; if it exposes an attribute/marker interface, mirror the bundle's own test fixtures.
Entity-level attachment
Most "attach file" bundles give you one of three shapes: a reusable Attachment entity you relate to, a trait that adds a file property plus lifecycle callbacks, or a PHP attribute that a Doctrine listener reacts to. Read the bundle's Resources/config/doctrine or src/Entity to determine which. Prefer relating to a shared Attachment entity over a trait that copies file columns into every table — it keeps storage logic in one place and makes a future migration to object storage a one-line change.
Upload handling
Keep the upload concern at the boundary and the storage concern behind the bundle's service:
#[Route('/posts/{id}/attachment', methods: ['POST'])]
public function attach(Post $post, Request $request, AttachmentManager $attachments): Response
{
$file = $request->files->get('attachment');
$attachments->attach($post, $file); // verify the actual API in src/
return $this->redirectToRoute('post_show', ['id' => $post->id]);
}
If a Symfony form type is provided, use it ($builder->add('attachment', AttachFileType::class)) rather than hand-rolling $request->files — you get the bundle's validation constraints for free, and upgrading later won't break your controller.
Download and access control
Never expose the storage path directly. Either use the bundle's download controller/route (it usually resolves an ID to a streamed response) or write a thin controller that checks $this->denyAccessUnlessGranted('view', $attachment) before streaming. This is the single most common security gap in file bundles and the one thing you should not assume the package handles.
Testing
Abstract the bundle's uploader behind your own small interface (FileStorageInterface) and bind it in services.yaml. That gives you a fake in tests and a single seam to swap if the bundle is abandoned:
App\Storage\FileStorageInterface:
'@CarlosChininin\AttachFileBundle\...\Uploader' # confirm the concrete ID with debug:container
For functional tests, point the configured storage directory at %kernel.cache_dir%/test-files so uploads never touch your dev or production storage.
Pin the version and vendor the confidence
The metadata shows a release dated 2026-07-18 against a repository with a single star. Treat the version constraint as load-bearing: use an exact tag ("carloschininin/attach-file-bundle": "1.0.0" rather than ^1.0) so a re-tagged or force-pushed release can't silently change behaviour under you. Check the tag history and the diff between v1.0.0 and dev-main — if main is far ahead, you are effectively consuming an untested branch.
You are the QA team
Zero dependents means zero downstream bug reports. Assume edge cases are unhandled: filenames with unicode or ../, zero-byte uploads, files exceeding upload_max_filesize, and deletion of an entity whose file has already vanished from disk. Write your own integration tests for these rather than trusting the bundle's.
Doctrine schema is on you
If the bundle ships mapped entities, they are not in your migrations automatically. Run php bin/console doctrine:migrations:diff and read the generated SQL by hand — bundles frequently ship mappings with sensible defaults that nonetheless collide with your naming strategy or table prefix. Also verify orphan removal: deleting the owning entity should delete the attachment row and the stored file.
Configuration quirks
%kernel.project_dir%; confirm whether the bundle does that or expects an absolute path, and whether it creates missing directories.public vs private storage split — files that should be behind auth can silently land in public/.config:dump-reference is authoritative; README examples for small bundles are routinely stale relative to the code.Debugging
When uploads "do nothing," check in this order: (1) is the bundle actually registered and its services visible in debug:container; (2) did the Doctrine event fire (--debug on the listener, or a dump() in the subscriber); (3) does the target directory exist and is it writable by the PHP user; (4) is the form field name matching what the listener expects. Most failures land in step 2 or 3.
Extension points and exit strategy
Before adopting, identify the bundle's interfaces (a storage adapter, a name generator, an event subscriber). If it exposes none — everything is a concrete final class — treat this as a library you are prototyping with, not a foundation. Because the blast radius of abandonment is large (files on disk plus database rows plus a download route), keep the attachment abstraction behind your own interface from day one. That makes replacing the bundle a single service binding change rather than a schema migration plus a data backfill.
How can I help you explore Laravel packages today?