Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Entity File Bundle Laravel Package

2lenet/entity-file-bundle

EntityFileBundle lets Symfony entities attach and manage files through configurable FlySystem storage adapters, with simple save, retrieve, access, delete, rename, and move operations via a per-configuration manager.

View on GitHub
Deep Wiki
Context7

Getting Started

This is a Symfony bundle, not a Laravel package. Day-to-day use in a Laravel codebase means adopting Doctrine ORM (doctrine/orm + a Doctrine service-provider bridge such as laravel-doctrine/orm) and wiring the bundle's Symfony services manually. If your app is Eloquent-based, stop here and use spatie/laravel-medialibrary instead — the integration cost is not worth it.

Minimal steps:

  1. composer require 2lenet/entity-file-bundle
  2. Register the bundle/services (Symfony: config/bundles.php; Laravel: bind Lle\EntityFileBundle\Service\EntityFileLoader and the config in a service provider).
  3. Create config/lle_entity_file.yaml defining one configuration per entity/filesystem pair:
    lle_entity_file:
        configurations:
            seller_logos:
                class: "App\\Models\\Seller"
                storage_adapter: "lle_entity_file.storage.default"
    
  4. Ensure Flysystem is configured. The default adapter writes under data/seller_logos; point it elsewhere (S3, FTP, local disk) by defining a new storage in flysystem.yaml and referencing it in storage_adapter.
  5. Register the routes if you want built-in file serving:
    lle_entity_file:
        resource: "@LleEntityFileBundle/Resources/config/routes.yaml"
    

First use case — attach a logo to a Seller:

$manager = $entityFileLoader->get('seller_logos');

$entityFile = $manager->save($seller, $request->file('logo'), 'logo.png');

$this->em->persist($entityFile);   // required
$this->em->flush();                // required

Where to look first: the README configuration section, then the Flysystem bundle's storage reference (you'll spend most of your integration time there), then the bundle's EntityFileController to understand how it serves files and enforces the role config.

Implementation Patterns

Config-per-entity is the core abstraction. Treat each configurations entry as a named "file collection" — e.g. seller_logos, product_images, order_reports. Different entities, different storage backends, different roles, different dispositions. Use this to separate public images (S3 public bucket) from private documents (local encrypted disk).

Manager resolution via the loader. Always go through $entityFileLoader->get($configName); don't instantiate managers directly. Inject the loader into controllers, jobs, and console commands.

Controller pattern for uploads:

public function store(Request $request, Seller $seller, EntityFileLoader $loader)
{
    $manager = $loader->get('seller_logos');
    $file = $manager->save($seller, $request->file('logo'), $request->input('path', 'logo.png'));

    $file->setDescription($request->input('description'));
    $this->em->persist($file);
    $this->em->flush();

    return response()->json(['id' => $file->getId()]);
}

Custom EntityFile entity for metadata. If you need description, uploaded_by, checksum, etc., create your own Doctrine entity implementing Lle\EntityFileBundle\Entity\EntityFileInterface, use LleEntityFileBundle\Entity\Trait\EntityFileTrait, and set entity_file_class: "App\\Entity\\UnicornEntityFile" under the config. This is the main extension point — the bundle otherwise stores only the bare minimum.

Dynamic paths. Paths are just strings passed to save(), so build them from entity state:

$dir  = $order->getDate()->format('Y-m');
$name = $order->getId() . '.xml';
$manager->save($order, $data, "$dir/$name");

This is the idiomatic way to shard storage by date/tenant/entity, and it avoids needing path logic inside the bundle.

Serving files. Use the two built-in routes for simple cases:

  • lle_entityfile_entityfile_read — /lle-entity-file/{configName}/{id}
  • lle_entityfile_entityfile_readbypath — /lle-entity-file/{configName}?path=2le.png

For anything requiring per-record authorization (e.g. "only the seller's owner can see this invoice"), skip the routes and write your own controller using readStream() + StreamedResponse. Set role: "PUBLIC_ACCESS" only for genuinely public assets (product images); leave the default (logged-in users) or a coarse ROLE_OPERATOR for internal files, and use a Symfony voter for finer-grained checks.

Download vs. inline. Set disposition: "attachment" on report/zip configs so browsers download rather than render. Leave inline for images/PDFs you want previewed.

Deletion / cascading. $manager->delete($file) removes both the EntityFile record and the underlying blob. Hook this into Doctrine lifecycle listeners (preRemove) on the owning entity so deleting a Seller cleans up its logos — the bundle does not do this for you.

Storage abstraction. For multi-environment setups, define separate Flysystem storages (local dev, S3 prod, FTP for a legacy vendor drop) and switch via storage_adapter per config, not per-environment conditionals in code.

Crudit integration. If you're in the 2LE stack, drop EntityFileBrickConfig::new('seller_logos') into your tabs to get a free dropzone with list/add/remove/download. This is the fastest path to an admin UI and is the bundle's strongest selling point.

Gotchas and Tips

  • save() does not persist. This is the single most common bug. You must $em->persist($entityFile) and $em->flush() afterwards. The same applies after move() — the manager updates the file on disk but you must flush the entity.
  • Eloquent won't work. There is no Eloquent integration, no model observers, no config/filesystems.php support. You're using Doctrine's EntityManager and Flysystem's own config, period. If the team can't accept that, pick a different package.
  • data/ may not be writable. The default storage path (data/{config}) breaks in read-only containers and on serverless/stateless deploys. Configure an explicit adapter directory or S3 bucket before going to production.
  • No validation, no MIME checking, no size limits. The bundle accepts strings, UploadedFile/File objects, and resources without inspection. Validate MIME type, extension, and size in your FormRequest/Validator layer, and sanitize the filename before passing it to save().
  • Filename collisions silently overwrite. Paths aren't uniquified. Use entity IDs, UUIDs, or timestamps in the path to prevent one seller's logo.png clobbering another's.
  • read() loads the whole file into memory. Use readStream() for large assets or when serving via StreamedResponse.
  • Catch Flysystem exceptions. Handle UnableToWriteFile, UnableToReadFile, FileNotFound around save/read/move, and translate them into your API's error format. The bundle delegates exception semantics entirely to Flysystem.
  • Role config is coarse. The role key is a single string check. Anything record-scoped (invoice belonging to a customer) needs a custom Symfony voter; don't try to encode it in YAML.
  • Routes require manual registration unless you're on Symfony Flex. Symptom: 404s on /lle-entity-file/... after a clean install.
  • entity_file_class must be set before the first save. Changing it later won't migrate existing rows; plan the schema up front if you expect to add metadata.
  • Release cadence is your compatibility signal. Symfony 7 support landed in 1.1.0, SF8 in 1.2.0, and SQL indexes in 1.2.4 — pin a version and check the changelog when bumping Symfony. With ~3 stars and 0 dependents, you are effectively the QA team; write integration tests around save/read/delete/move against a temp directory.
  • No event system. There are no pre/post-upload hooks. If you need thumbnailing, virus scanning, or audit logs, wrap the manager in your own service rather than expecting lifecycle events.
  • delete() on a missing blob will throw. Guard deletion paths, especially in bulk cleanup jobs where files may have been removed out-of-band.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
mehedimi/laravel-amp-mail
happones/laravel-vue-i18n-generator
jwerd/inertia-table
siberfx/flysystem-wasabi
hollyit/token-replace
ippanelcom/laravel-sdk
adachsoft/changelog-linter
adachsoft/collection
payerurl/telegrambot-crypto-payment-instant-payout
dskripchenko/laravel-hybrid-search
riorizkyrainey/lara-ced
mehedimi/static-vel
zharp/zharp
nonsapiens/bigquery-model-sync
lgu-bayambang-ict-office/lgu-laravel-development-standards
andydefer/laravel-support
se7enxweb/flysystem
lampwick/laravel
jeslage/statamic-emojis
micahdshackelford/rmap-laravel