Extensibility & Customization
Sanchaya resolves its main behavior from configuration, so production apps can replace the default Eloquent models, authorization policy, and file-operation action classes without editing package files.
Replace Models and Policy
Use custom models when you need extra casts, relationships, scopes, observers, or table names. Extend the package models so the file manager keeps the expected relationships and helpers.
// config/filament-sanchaya.php
'model' => \App\Models\MediaFile::class,
'attachment_model' => \App\Models\MediaAttachment::class,
'policy' => \App\Policies\MediaFilePolicy::class,namespace App\Models;
use DP0\Sanchaya\Models\SanchayaFile;
class MediaFile extends SanchayaFile
{
protected $table = 'sanchaya_files';
protected $casts = [
'metadata' => 'array',
];
}When both default models are replaced, publish and own the migrations in your application. Sanchaya only auto-loads package migrations for the default file or attachment models.
Override Action Classes
Each configured action is resolved from Laravel's container. Create a class with the same public method contract as the action you are replacing; extending the shipped class is the safest starting point.
// config/filament-sanchaya.php
'actions' => [
'delete' => [
'enabled' => true,
'label' => 'Delete',
'icon' => 'heroicon-m-trash',
'class' => \App\Actions\MyCustomDeleteAction::class,
],
],namespace App\Actions;
use DP0\Sanchaya\Actions\DeleteAction;
use DP0\Sanchaya\Models\SanchayaFile;
class MyCustomDeleteAction extends DeleteAction
{
public function execute(SanchayaFile $item): bool
{
// Add audit logging, external cleanup, or business checks here.
return parent::execute($item);
}
}| Config key | Default class | Methods used by the manager |
|---|---|---|
actions.download.class | DownloadAction | execute($file), bulk($items) |
actions.create_folder.class | CreateFolderAction | execute($name, $disk, $parentId) |
actions.rename.class | RenameAction | execute($item, $newName) |
actions.move.class | MoveAction | execute($item, $destinationId, $destinationDisk) |
actions.copy.class | CopyAction | execute($item, $destinationId, $destinationDisk) |
actions.delete.class | DeleteAction | execute($item), bulk($items), restore($item), forceDeleteTrashed($item) |
The preview action has configurable visibility, label, and icon, but it does not resolve a backend action class.
Override Storage Paths
Storage paths are generated through the configured PathGenerator implementation. The default generator preserves the sanchaya/{Y}/{m} layout and supports picker directories.
// config/filament-sanchaya.php
'storage' => [
'base_directory' => 'sanchaya',
'date_format' => 'Y/m',
'path_generator' => \App\Support\MediaPathGenerator::class,
],Custom generators should implement DP0\Sanchaya\Contracts\PathGenerator. Extend DefaultPathGenerator when you only need to change part of the default behavior.
The contract is used by uploads, chunked upload assembly, folder creation, copy, move, and picker scoping. Its methods are:
| Method | Purpose |
|---|---|
normalizeDirectory(?string $directory) | Validate and normalize a relative picker namespace. |
basePath(?string $directory = null) | Resolve the configured package base path, optionally scoped to a picker directory. |
rootPath(?string $directory = null, ?DateTimeInterface $date = null) | Build the date-based root used for new uploads. |
folderRootPath(?string $directory = null) | Resolve the folder namespace used by the manager or picker. |
childPath(string $parentPath, string $name) | Append one safe child folder or file name. |
isWithin(string $path, ?string $directory = null) | Check that a stored path belongs to the requested namespace. |
Keep the generator registered as a class name in configuration so Laravel can resolve it from the container. Generators should return relative disk paths and should preserve the namespace checks when supporting picker directories.
Use a Custom Chunk Upload Route
Applications that need custom chunk storage, validation, or authorization can disable Sanchaya's default route and point the configured route name to an application-owned route:
'file' => [
'chunk_upload' => [
'register_route' => false,
'route_name' => 'media.chunks',
'require_signed_url' => true,
],
],Register the route in your application using the same name:
Route::middleware(['web', 'signed', 'throttle:600,1'])
->post('media/chunks', [MediaChunkController::class, 'store'])
->name('media.chunks');The method receives the Laravel Request. Each successful request must return JSON containing the new assembled byte offset, for example {"offset": 2097152}. The browser uses this value to send the next chunk. Your custom controller must provide authentication, authorization, CSRF, validation, and throttling checks. Keep the signed URL requirement aligned with the middleware on your application route.
Override Chunk Finalization
To customize how a completed chunked upload is validated, stored, or indexed, replace the finalizer action:
// config/filament-sanchaya.php
'file' => [
'finalizer' => \App\Actions\FinalizeMediaUploadAction::class,
],The custom class must implement DP0\Sanchaya\Contracts\ChunkUploadFinalizer:
namespace App\Actions;
use DP0\Sanchaya\Contracts\ChunkUploadFinalizer;
use DP0\Sanchaya\Models\SanchayaFile;
class FinalizeMediaUploadAction implements ChunkUploadFinalizer
{
public function execute(
array $upload,
string $disk,
?int $parentId = null,
?string $directory = null,
array $acceptedTypes = [],
?int $maxSizeKb = null,
): SanchayaFile {
// Publish $upload['path'] and return the created file record.
}
}The default action is DP0\Sanchaya\Actions\FinalizeChunkedUploadAction. It handles the configured size limit, quota, MIME types, picker scope, generated storage path, checksum, disk write, and database record creation.