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

Cabinet Laravel Package

andrew13/cabinet

Cabinet is a lightweight Laravel package for managing files and uploads. It provides a simple API for storing, attaching, retrieving, and deleting files, making it easy to add a filing-cabinet-style media layer to your Eloquent models.

View on GitHub
Deep Wiki
Context7

Getting Started

Before you install anything, check the constraints. andrew13/cabinet was last tagged in February 2015 and targets Laravel 4 (illuminate/support ~4.0). On anything from Laravel 5 upward Composer will refuse to resolve it. Treat this section as "I am maintaining a Laravel 4 app and this is already in composer.json", not "I am choosing an upload library."

Minimal steps on a Laravel 4 codebase:

  1. Install and pin a known-good commit:

    composer require andrew13/cabinet:dev-master
    

    Because there are no meaningful semver tags in active use, lock the commit hash in composer.lock and commit that file. Never let a composer update float this dependency.

  2. Register the service provider in app/config/app.php:

    'providers' => array(
        // ...
        'Andrew13\Cabinet\CabinetServiceProvider',
    ),
    

    Add the facade alias in the same file if you prefer the static syntax over constructor injection.

  3. Publish the package config so you can see what knobs exist:

    php artisan config:publish andrew13/cabinet
    

    This drops a config file into app/config/packages/andrew13/cabinet/. Read it end to end — for a package this small, the config file is the documentation. Expect upload path, allowed extensions/mime types, and possibly max size.

  4. Where to look first: the package source in vendor/andrew13/cabinet/src. It is a few hundred lines, mostly a service provider plus one upload class. Skim it before using it — you will need to know whether it moves files itself, returns a path, or returns a filename, because that determines everything downstream.

First use case: a single-file upload on a form (avatar, product image, attachment). Validate with Laravel's validator first, then hand the Symfony\Component\HttpFoundation\File\UploadedFile to the package, persist the returned path/filename on your Eloquent model, and serve it from public/. Do not attempt multi-file galleries or image manipulation on day one; the package is a thin move-and-rename layer, not an asset pipeline.

Implementation Patterns

Validate before you hand off, always. The package's own checks (if any) should be treated as a second line of defence, not the primary one. Keep validation in the controller or a Form Request equivalent:

$rules = ['photo' => 'required|image|max:2048'];
$v = Validator::make(Input::all(), $rules);
if ($v->fails()) { return Redirect::back()->withErrors($v); }

$file = Input::file('photo');

This is also your portability insurance: when you eventually replace the package, the validation layer survives untouched.

Wrap it in one application-level service. This is the single highest-leverage pattern for a package like this. Put every call to the Cabinet facade behind App\Services\UploadService (or a repository) with a method like store(UploadedFile $file, $directory) : string. Controllers depend on your class, not on Cabinet::. Two benefits:

  • The static facade is untestable in isolation; your wrapper is not.
  • Migration to $request->file('photo')->move(...) or Storage::putFile() in a later Laravel version becomes a one-file change rather than a grep across the codebase.

Directory convention over configuration. Derive the destination from a single place — e.g. public/uploads/{type}/{yyyy}/{mm}/ — and generate it inside the wrapper. The package's configured base path should be the only package setting you actually rely on; everything else (subfolders, naming) stays in your code where you can reason about it.

Name files yourself. Do not trust an uploaded file's original name for storage. Generate a collision-resistant name and keep the human-readable original in a database column:

$safe = Str::random(32) . '.' . strtolower($file->getClientOriginalExtension());
// store $safe, persist original name in `original_filename`

If the package exposes a rename/callback hook in its config, use it; otherwise move the file yourself after the package returns its path.

Tie persistence to model events, not controllers. Store the file, then save the path in a saving/saved model event or inside a repository method that wraps both in a transaction-ish flow. On delete, remove the file in the deleting event. The classic failure here is an orphaned file on the filesystem when the DB insert fails, or a dangling DB row pointing at a file that was never written — decide which side you clean up on and write that code once.

Multi-file forms: loop over Input::file('attachments') and call the wrapper per file, collecting results. Do not assume the package handles arrays; most small upload packages do not.

Testing: in acceptance/functional tests, use UploadedFile::fake() equivalents available in L4 (new Symfony\Component\HttpFoundation\File\UploadedFile($path, $name, null, null, null, true)) pointing at a fixture, and assert against the wrapper's return value and the filesystem via File::exists(). Test the wrapper, not the package.

Gotchas and Tips

The dependency ceiling is the headline gotcha. illuminate/support ~4.0 means the package cannot be installed alongside Laravel 5+. If you are on L4 today, the practical risk is not the missing features — it is that you cannot run composer update on the rest of your stack without this package pinning you back. Plan the exit before you need it.

Unmaintained since 2015: no security patches. File upload is exactly the attack surface where you do not want abandoned code. Mitigations that cost little:

  • Serve uploads from a directory with no PHP execution (.htaccess/nginx location denying script execution in public/uploads).
  • Force a whitelist of extensions and verify MIME with finfo, not the client-supplied Content-Type.
  • Never trust getClientOriginalName() in a path.

Path and permissions bugs are the #1 runtime failure. The configured destination must exist and be writable by the web user. Symptom: silent failure or a false return. Debug with:

ls -ld public/uploads && sudo -u www-data touch public/uploads/.probe

Also confirm you are writing to public_path() and not storage_path() if you expect the file to be web-accessible — L4 apps commonly get this wrong when the config default was written for a different layout.

Facade alias not registered → Class 'Cabinet' not found. The service provider registration alone is not enough for the static syntax. Either add the alias in app/config/app.php or inject the class. When debugging any "method does not exist" error, first confirm which class the alias actually resolves to.

Config caching surprises are milder on L4 but still real. After php artisan config:publish, edits go in app/config/packages/andrew13/cabinet/config.php, not in vendor/. Editing vendor files means your fix vanishes on the next composer install. If the package reads config at boot rather than per-call, restart your queue workers after changing it.

Return-value ambiguity. Small upload packages are inconsistent about whether they return an absolute path, a relative path, a filename, or a boolean. Do not guess — var_dump once in tinker/php artisan tinker and write the wrapper against what you observed, with an assertion. Store a relative path in the database (portable across environments); build the absolute URL at render time with URL::to() or asset().

Extension points are limited — use them, don't fight them. Realistically you get: the config file, the service container binding (you can rebind the package's class to your own subclass in a service provider), and whatever events the package fires, if any. If you need thumbnailing, chunked uploads, S3, or virus scanning, this package will not get you there; bolt on intervention/image for resizing and keep the storage decision in your wrapper.

Migration target, when you get there: Laravel's built-in UploadedFile::move() / Storage::putFile() cover 90% of what this package does, with first-party support. If you need more (collections, conversions, S3), spatie/laravel-medialibrary is the modern equivalent. Because everything went through your UploadService, that swap is a contained refactor rather than a rewrite.

Debugging checklist, in order: is the provider registered → is the alias resolving → does the destination directory exist and is it writable → does the config file live under app/config/packages/ → what does the method actually return. That sequence resolves nearly every issue with a package this size.

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.
uni-method/symfony-lib
tacticmedia/scim-server-bundle
neika-ai/symfony-bundle
mattleads/agent-publishing-bundle
glavweb/framework-bundle
glavweb/extra-bundle
glavweb/api-bundle
glavweb/admin-bundle
adachsoft/api-version-bundle
elsayed85/laravel-api-exceptions
ouhssini/laravel-demo-mode
daworks/ncloud-outbound-mailer-driver
gp247/cms
ignibyte/aic-gates
nexa-merchant/blog
novasa/laravel-language-center
opencage/geocode
adolfoholzer/parameters
livenetworks/ln-starter
cgv/localized-resource