servocoder/richfilemanager-php
Laravel package that integrates the RichFilemanager file browser via a PHP connector, enabling browsing, uploading, editing, and managing files and folders with configurable routes, middleware, and storage settings for quick setup.
What you're actually installing. This is the server-side half of RichFilemanager: a PHP front controller that speaks the RichFilemanager JSON protocol (list, upload, rename, copy, move, delete, create-folder, thumbnails). The JS widget is shipped separately; this package only handles the filesystem side. Everything in it predates Laravel 6 — there is no service provider, no facade, no publishable config, no migrations. Treat it as a drop-in script you wrap, not a package you "install into" Laravel.
Step 1 — install and find the entry point.
composer require servocoder/richfilemanager-php
ls vendor/servocoder/richfilemanager-php/
Look first at two files, in this order:
config/config.php (or connectors/php/config/config.php) — the entire behaviour of the connector is this one returned array: options.uploadDir, options.uploadURL, options.thumbsDir, security.readOnly, security.extensions, exclude.dirs, exclude.files. Read it before writing a line of code.connector.php — the front controller. Note whether it uses __DIR__ (relative paths are safe) or getcwd() (relative paths depend on where you invoke it).Step 2 — give it a home outside vendor/. Copy the connector (and its application//config/ siblings) into public/filemanager/ so the paths it computes stay stable, then edit the copied config. Never edit inside vendor/ — the next composer update silently reverts your config.
Step 3 — point the widget at it.
new RichFilemanager('#fm', {
fileConnector: '/filemanager/connector.php',
});
Step 4 — do the first real use case before anything else: serve a per-user uploads folder.
// public/filemanager/config/config.php (edited)
'options' => [
'uploadDir' => base_path('storage/app/public/uploads/'),
'uploadURL' => '/storage/uploads/',
'thumbsDir' => '.thumbs',
],
Then harden the web server so PHP does not execute there (location ~ \.php$ { deny all; } in the uploads dir), and php artisan storage:link. Browse, upload one image, confirm a thumbnail appears in .thumbs. If that works, the connector is wired correctly; everything after is scoping.
Pattern A — standalone front controller (recommended). Keep connector.php in public/filemanager/ and let it run without Laravel's kernel. Auth is enforced by a small bootstrap at the top of the connector:
require __DIR__.'/../../vendor/autoload.php';
$app = require_once __DIR__.'/../../bootstrap/app.php';
$app->make(Illuminate\Contracts\Http\Kernel::class)->bootstrap();
$user = auth()->user();
if (!$user) { http_response_code(403); exit; }
This gives you the Laravel session and Auth without fighting the connector's own request/response lifecycle. It's the least surprising integration and survives package upgrades.
Pattern B — route require. If you'd rather have middleware, quotas, or request logging, mount it as a route:
// routes/web.php — must be registered before any catch-all
Route::any('/filemanager/connector', function () {
return require public_path('filemanager/connector.php');
})->middleware(['auth']);
Works, but read the Gotchas on __DIR__ and exit semantics before shipping this.
Pattern C — per-user / per-tenant roots. The config is a plain PHP array, so generate it at runtime instead of storing it in a file:
public/filemanager/config/config.php:
<?php
$base = storage_path('app/tenants/'.auth()->user()->tenant_id);
return require base_path('config/richfilemanager.php'); // with $base injected
Or wrap the whole thing: a thin connector.php sets $overrides, then requires the stock config and array_replace_recursives. This is the idiomatic extension point — there is no event system, so config injection is your only hook short of subclassing.
Pattern D — role-scoped read-only views. For "browse the media library but don't touch it" screens, set security.readOnly => true and security.extensions => ['jpg','png','webp'] in a second, separate connector instance. Run two connectors rather than trying to switch config per request: the connector assumes one config per front controller.
Integration tips.
app/Http/Middleware/VerifyCsrfToken.php, $except = ['filemanager/*']) — the widget's XHRs don't carry your token.Route::any, not get/post: listings are GET, mutations are POST.uploadDir (filesystem) and uploadURL (web path) in lockstep. They are two separate strings and the connector will happily produce links to files it can read but the browser can't..thumbs in exclude.dirs so thumbnails don't appear as user folders, and keep .htaccess in exclude.files.fopen/is_dir/rename all the way down. Mount a filesystem or use Laravel's Flysystem behind your own endpoint."servocoder/richfilemanager-php": "1.*" plus a committed composer.lock; there has been no release since June 2018.create_function-style closures, dynamic properties, and each()-adjacent patterns. Run it under your target PHP version before you build anything on top of it; budget for a handful of two-line patches.__DIR__ vs CWD. If the connector resolves config/config.php relative to getcwd(), invoking it from a Laravel route (where CWD is the public root) breaks with "config not found". Check line one of connector.php; if it's getcwd(), prefer Pattern A or chdir() first.exit. Many connector versions terminate the request directly rather than returning a response. Inside a Laravel route that bypasses terminable middleware and can leave the session unclosed — another reason Pattern A is safer.{"status":"error","message":...} and dies, so Laravel's handler and your log never see it. Wrap the route (or the bootstrap) in try/catch, Log::error() the throwable, and keep APP_DEBUG=false in production — the connector will leak absolute paths otherwise.serverRoot/fileRoot control whether paths outside the upload root are reachable. Leave serverRoot => false unless you have a concrete need, and never let fileRoot come from a query string or a raw request parameter..thumbs. Fix with chown -R www-data:www-data on uploadDir; 755/644 for everything else. On nginx, raise client_max_body_size or uploads over the default 1 MB return a 413 that looks like a connector error.public/ are a liability. If you must serve from there, add an nginx/Apache rule killing PHP execution in the upload directory. Better: storage/app/public plus a symlink.uploadURL and fileConnector that work on macOS will 404 on Linux for a single capital letter. Verify the exact path the widget requests in devtools, not from the source.require.mode parameters, response shapes). Match a connector tag to the RichFilemanager build you actually ship, and smoke-test list → upload → rename → delete after any bump.How can I help you explore Laravel packages today?