meloflavio/owncloud-uploader-bundle
A Symfony bundle that integrates ownCloud file uploading into your application, providing services and configuration to send files to an ownCloud server. It simplifies wiring uploads into Symfony projects through the bundle's container configuration and uploader services.
meloflavio/owncloud-uploader-bundle is a small Symfony bundle that plugs an ownCloud (WebDAV) backend into VichUploaderBundle, so entities annotated with #[Vich\UploadableField] write to ownCloud instead of the local filesystem. It is not a Laravel package — if you are on Laravel, skip to the last bullet below.
Minimal path to a first upload:
vich/uploader-bundle, an ownCloud/Nextcloud instance reachable over HTTPS, and an app password (not the user's login password). Verify curl -u user:app-password https://oc.example.com/remote.php/dav/files/user/ returns a 207 before touching PHP.composer require meloflavio/owncloud-uploader-bundle. There is almost certainly no Flex recipe (0 stars, no dependents), so add the bundle manually to config/bundles.php.config/packages/ — base URL, username, app password. The exact keys are not documented anywhere reliable; open src/DependencyInjection/Configuration.php first, that file is the authoritative schema.storage option at the service the bundle registers (check config/services.xml / services.php for the id). That service is the whole integration surface — it implements Vich\UploaderBundle\Storage\StorageInterface.#[Vich\Uploadable] + #[Vich\UploadableField(mapping: 'documents', fileNameProperty: 'fileName')], persist through the normal Doctrine lifecycle, and confirm the file appears in the ownCloud web UI.Where to look first, in order: the README (Portuguese), DependencyInjection/Configuration.php, then the Storage class. Read the storage class line by line — it is probably 100–200 lines and is the only part that matters. If it is short enough, copy it into your app as your own StorageInterface implementation and drop the dependency; you get the same behaviour with no supply-chain risk.
First real use case: a single documents mapping on one entity, upload → verify in ownCloud → delete the entity → verify cleanup: true removes the remote file. Do that before rolling out to a second mapping.
Laravel note: the equivalent is league/flysystem-webdav behind a Storage::disk('owncloud') filesystem disk. Do not try to make a Symfony bundle work in Laravel — you would be reimplementing one adapter class for no benefit.
One mapping per purpose, not per entity. Vich mappings are cheap; use documents, avatars, invoices so each can have its own root directory, namer, and cleanup policy. Point them all at the same ownCloud storage service unless the bundle exposes multiple instances — if it does not, decorate the storage service and route by PropertyMapping::getMappingName().
Keep ownCloud out of the domain layer. Entities only know fileName. Credentials, base URL, and path layout live in config and in a custom DirectoryNamer (tenant id, date partition, entity class). This is what lets you swap to S3 or local disk later without touching a single entity.
Serving files is the hard part. vich_uploader_asset() resolves through StorageInterface::resolveUri(), but ownCloud files have no public URL by default. Two workable patterns:
StreamedResponse, and check authorization yourself. Never put the app password in a URL./ocs/v2.php/apps/files_sharing/api/v1/shares) after upload and cache the share URL on the entity. Good for read-only artefacts, bad for anything access-controlled.Backfill with the handler, not raw SQL. For an existing dataset, write a console command that loops entities and calls $uploadHandler->upload($entity, 'file') inside a try/catch per row, logging failures. This reuses Vich's naming and path logic exactly.
Go async for anything large. Vich's UploadHandler is synchronous and runs inside prePersist/preUpdate. For files over a few MB, store locally first, dispatch a Messenger message, and do the ownCloud PUT outside the request cycle.
Test the contract, not the network. Unit-test your code against a stub StorageInterface. For integration, run sabre/dav in a container or use Guzzle's MockHandler to assert the PUT path, headers, and body. There are no tests in the package to inherit, so you own this.
Extension point worth knowing: Vich dispatches vich_uploader.pre_upload / post_upload / pre_remove / post_remove. Use a subscriber for audit logging, virus scanning, or size limits rather than forking the storage class.
composer.json for symfony/* and vich/uploader-bundle constraints before you commit. If it targets Symfony 4/5 or Vich 1.x, it will break on modern stacks.StorageInterface changes between Vich versions. New methods get added to the interface; a stale adapter causes a fatal "must implement" error the moment you bump Vich. Pin Vich's minor version alongside this bundle, or fork the adapter.resolveUri() is a lie for private files. It will happily return something that 404s or leaks a token. Always check what it actually produces before putting it in a template.verify to your internal CA bundle path in Guzzle config. Do not use verify: false in production; the bundle may not even expose the option, in which case you must decorate the HTTP client.#, ?, and non-ASCII filenames must be percent-encoded per path segment, not on the whole URL. Watch for double-encoding (%2520) — a classic bug in small WebDAV adapters.DirectoryNamer producing tenant/2024/06/, verify the adapter issues MKCOL for each missing segment. If it does not, create directories yourself or flatten the layout.204 No Content on replace. If file_namer can collide (original filenames, no UUID), you lose data with no error. Use a UUID or hash namer.401 bad/expired app password, 403 share or ACL issue, 404 missing parent collection, 423 file locked by Office/desktop client, 507 quota exceeded. Log the body — ownCloud returns an XML error document with a real message.cleanup: true only fires when Vich's handler sees the removal. Bulk DELETE queries, onDelete: CASCADE, and manual DB edits bypass it. Schedule a reconciliation command comparing DB filenames against the ownCloud tree.bin/console debug:config <alias> will tell you the alias and the merged config once the bundle is registered — faster than guessing from the README.Middleware::log() on the client the storage uses, and tail the ownCloud server log plus occ files:scan --all to confirm files landed where you think.How can I help you explore Laravel packages today?