Your own folder model
The tree is built from Koassi\FilamentFileExplorer\Models\Folder. Point the config at a subclass to add columns, a global scope, a factory or anything else:
// config/filament-file-explorer.php
'folders' => [
'model' => \App\Models\Folder::class,
],
namespace App\Models;
class Folder extends \Koassi\FilamentFileExplorer\Models\Folder
{
// whatever you need
}
It has to extend the package’s model — the media collection, the thumbnail conversion, the soft deletes and the containment walk all live on it — and a class that does not is refused with a clear message at the first query, rather than failing three layers down.
Two things this deliberately does not change
Media rows keep storing the package’s class in model_type. getMorphClass() answers for the base model on purpose, so switching does not orphan a library already uploaded: every row still passes the containment guard, which checks that value.
Had it followed the subclass, every existing row would have started failing that check — silently, and only for the applications that took the new feature up.
Set morph_class only if you are migrating from some other implementation whose rows already carry a different value:
'morph_class' => 'folder',
It is config, not a plugin setting. A model cannot know which panel it is being browsed from — the same reason the thumbnail settings are config only. Everything else that is panel-scoped is set on the plugin.
What is safe to add
A subclass is an ordinary Eloquent model, so: extra columns, casts, accessors, relations, a factory, observers, and scopes of your own.
class Folder extends \Koassi\FilamentFileExplorer\Models\Folder
{
protected static function booted(): void
{
static::addGlobalScope('team', fn ($query) => $query->where('team_id', auth()->user()?->current_team_id));
}
}
A global scope narrows what the explorer can see, which is a legitimate way to partition a tree — but note it is not a substitute for the containment check. The check exists because ids arrive as user input; a scope that hides a folder from a listing does not stop an id being passed to an action. Do both.
What the explorer needs from it
If you are overriding rather than adding, these are load-bearing:
parent() / children() |
The tree, and the containment walk |
registerMediaConversions() |
The thumbnail conversion — driven from config |
SoftDeletes |
The trash |
getMorphClass() |
Containment on media rows — see above |
slug, parent_id |
Unique together; how a folder is addressed |
The relations use static::class, so a subclass gets its own class back from a query and from a relation alike — a mismatch there was the sharp edge of making this swappable at all.
Reading the configured model
Never Folder::class directly in code that has to respect the setting. Support\FolderModel is the single place the class is resolved:
use Koassi\FilamentFileExplorer\Support\FolderModel;
FolderModel::query()->find($id);
FolderModel::morphClass();
A test asserts the static forms never come back into the package, since they creep in the moment someone adds a query without thinking about it.
The table
'folders' => [
'table' => 'file_explorer_folders',
'max_depth' => 12,
],
max_depth bounds folder creation, folder uploads, copies and the column view’s pane count. It is also settable per panel: ->maxFolderDepth(6).