# VichUploaderBundle gives Doctrine-backed uploads a lifecycle instead of a string

> VichUploaderBundle is a Symfony bundle that attaches uploaded files to ORM entities, MongoDB ODM documents and PHPCR ODM documents, names and stores the file, injects a File object when the record is loaded, deletes it when the record is removed, and provides templating helpers for public URLs. Configuration is per application, and the test suite runs in Docker against PHP 8.3 by default.

**dustin10/VichUploaderBundle** — A simple Symfony bundle to ease file uploads with ORM entities and ODM documents.

- Repository: https://github.com/dustin10/VichUploaderBundle
- Stars: 1,901 · Forks: 521
- Language: PHP
- License: MIT
- Published: 2026-10-16 · Updated: 2026-10-16 · Language: en
- Canonical page: https://hysenlabs.com/projects/dustin10-vichuploaderbundle

## Four jobs, and only one of them is moving the file

The README describes what the bundle does in four bullets, and the ordering matters.

The first is naming and saving: a file is automatically named and saved to a configured directory. That is the part every application writes by hand once, then copies into every controller.

The second is injection. When the entity or document is loaded from the datastore, the file is injected back into it as an instance of `Symfony\Component\HttpFoundation\File\File`. That is the real reason to use this bundle. Storing a path in a string column means every consumer of that entity has to know how to turn the path into a file, and every consumer gets it slightly differently. Making the property a `File` means the rest of the application treats it like any other Symfony file, including validation and streamed responses.

The third is deletion: the file is removed from the file system when the entity or document is removed from the datastore. An upload handler that only saves is half a solution, because orphaned files accumulate and nobody notices until the disk is full.

The fourth is templating helpers to generate public URLs to the file, so a template does not concatenate a path by hand.

The README states that all of this functionality is fully configurable to allow for app-specific customization. It works with ORM entities, MongoDB ODM documents and PHPCR ODM documents, so the same bundle covers relational, document and tree-style persistence.

## Configurability is the feature and the documentation burden

The README's promise is that everything is configurable. What it does not do is show the configuration in the README itself. The usage documentation lives at `docs/index.md`, and there is a separate `UPGRADE.md` that the README tells you to read if you are coming from a previous version.

That split is normal for a mature Symfony bundle and it means the README alone is not enough to configure this. Treat `docs/index.md` as a required read rather than optional, and treat `UPGRADE.md` as required if you already run this bundle.

Two structural files in the repository hint at how configuration is delivered. There is a `config/` directory, which is where a bundle ships its services and its semantic configuration, and `vich_uploader.xsd`, an XML schema that validates a configuration supplied as XML. A schema file means the bundle will refuse a malformed configuration at compile time rather than failing at runtime on an upload, which is the right way to behave in a framework that caches its container.

There is also a `translations/` directory, so the validation messages the bundle generates are translatable rather than hardcoded English.

For an application-specific setup the practical consequence is that you will be reading the documentation rather than the README to find out which mapping types exist and what each one does. That is a fair trade for a bundle with this much behaviour, but it is a trade.

## The test suite runs in a container built from docker/Dockerfile83

The repository is serious about how it is tested, and the mechanism is Docker rather than a local PHP.

The Makefile has `TARGET?=83`, so the default PHP version is 8.3, and the image is built from a numbered Dockerfile:

```
docker build -t dustin10/vichuploader_php${TARGET} -f ./docker/Dockerfile${TARGET} .
```

The full test run builds that image and executes PHPUnit inside it:

```
docker run dustin10/vichuploader_php${TARGET} -d date.timezone='UTC' vendor/bin/phpunit --display-phpunit-notices
```

Two details in that line are deliberate. The `date.timezone='UTC'` override forces a fixed timezone so tests that touch dates do not depend on the host, and `--display-phpunit-notices` makes deprecations visible rather than hiding them.

A single test file is one target away, and the Makefile documents the invocation with a worked example for IndexTest.php by setting `TEST`. The output format is PHP's PHPUnit rather than anything custom, so a failure reads the way you expect.

There is housekeeping too: `destroy-environment` runs `remove-containers` and then removes all images, with the leading hyphen on each docker line telling make to ignore failures, since a clean machine has nothing to stop. `clean-images` removes just the one tagged image.

For a contributor this means you never install PHP or its extensions locally to run the suite. For an adopter it means the compatibility claim the bundle makes is whatever is in those numbered Dockerfiles, and there is more than one, since the TARGET variable selects among them.

## Maintenance tools in the repository are aimed at contributors, not users

Several root files exist purely to keep contributions consistent, and knowing they are there saves time.

`.php-cs-fixer.php` configures PHP-CS-Fixer, so the code style is enforced by a tool rather than by review comments. `phpstan.dist.neon` sets the static analysis configuration and `phpstan-baseline.neon` holds the accepted existing violations, which is the conventional way to introduce static analysis to a legacy codebase without blocking every pull request.

`phpunit.xml.dist` is the test configuration, and the presence of the `.dist` suffix means it is the template a developer copies to a local `phpunit.xml` when they need to override something.

`.dockerignore` keeps the Docker build context small, and `CONTRIBUTING.md` holds the contribution process.

The source lives in `src/`, templates in `templates/`, and the XML schema in `vich_uploader.xsd`. That template directory is worth noting: the public URL helpers need templates, so the bundle ships rendering for that purpose, which is one of the reasons it feels like a bundle rather than a library.

In short, this is a well-equipped repository for a component that has been around long enough to need a baseline file. The tooling tells you the project is maintained by people who care about consistency; it does not by itself tell you anything about how the bundle behaves, which is what `docs/index.md` is for.

## A 3.0 tag shipped two days before a 2.10 patch

The release history is short and readable, and it shows a maintained project in the middle of a major version transition.

v3.0.0 was tagged on 2026-09-07. v2.10.1 followed on 2026-09-09, two days later, and v2.10.0 on 2026-08-20 before that. The default branch is master rather than main.

That pattern, a major release immediately followed by a patch on the old line, is what a maintenance policy looks like when it is being executed: the new major goes out, and the previous line keeps receiving fixes so existing users are not forced to move immediately. It also means there are two supported lines right now, and which one you should be on depends on your Symfony version, which the README does not spell out.

The repository is not archived and the last push was on 2026-09-21, so work is continuing shortly after those tags.

Installation is through Packagist as `vich/uploader-bundle`, and the README carries the usual Composer badges for download counts and stable versus unstable versions. Those badges are the mechanism by which you tell an application which line to resolve: constrain to `^2.10` if you are upgrading cautiously, or move to `^3.0` if your Symfony version requires it.

Licensing is MIT, which for a bundle you install into a proprietary Symfony application carries no obligation on your code. Nothing in the repository suggests a copyleft component dependency that would change that.

The documentation URL the README points to is a GitHub path under `docs/index.md` rather than a separate documentation site, so the documentation lives in the repository and is versioned with the code.

## Conclusion

Adopt VichUploaderBundle when your Symfony application uploads files that belong to a Doctrine entity or an ODM document and you want naming, storage, deletion and URL generation handled by configuration instead of per-controller code. Do not adopt it for uploads that belong to no record, for a replacement of your object storage layer, or without reading UPGRADE.md if you are moving between major versions. Verify first which persistence layer your entities use, what your existing upgrade path requires across a major version, and what the deletion lifecycle does to a file that two records share.

## FAQ

### What does VichUploaderBundle do with uploaded files?

It automatically names and saves a file to a configured directory, injects it back into the entity or document when loaded from the datastore as a Symfony HttpFoundation File object, deletes it from the file system when the record is removed, and provides templating helpers to generate public URLs.

### Which persistence layers does VichUploaderBundle support?

ORM entities, MongoDB ODM documents and PHPCR ODM documents, according to the README.

### How do I install VichUploaderBundle?

Through Composer via Packagist as vich/uploader-bundle. The README carries badges for the latest stable and unstable versions, which is how you see which line resolves.

### How do I run the VichUploaderBundle tests?

With the Makefile, which builds a Docker image from docker/Dockerfile83 by default and runs vendor/bin/phpunit inside it with date.timezone='UTC'. Set TARGET to pick another PHP version, and TEST to run a single file such as IndexTest.php.

### What should I read when upgrading VichUploaderBundle?

The UPGRADE.md file. The README points to it specifically for people upgrading from a previous version, alongside the main usage documentation in docs/index.md.

### What license is VichUploaderBundle under?

MIT. The README points at the bundled LICENSE file rather than restating the terms.

## Sources

- [dustin10/VichUploaderBundle on GitHub](https://github.com/dustin10/VichUploaderBundle)
- [Issues](https://github.com/dustin10/VichUploaderBundle/issues)
- [License: MIT](https://github.com/dustin10/VichUploaderBundle/blob/master/LICENSE)
- [README](https://github.com/dustin10/VichUploaderBundle/blob/master/README.md)
- [Releases](https://github.com/dustin10/VichUploaderBundle/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/dustin10-vichuploaderbundle
