Skip to content

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.

php
// config/filament-sanchaya.php
'model' => \App\Models\MediaFile::class,
'attachment_model' => \App\Models\MediaAttachment::class,
'policy' => \App\Policies\MediaFilePolicy::class,
php
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.

php
// config/filament-sanchaya.php
'actions' => [
    'delete' => [
        'enabled' => true,
        'label' => 'Delete',
        'icon' => 'heroicon-m-trash',
        'class' => \App\Actions\MyCustomDeleteAction::class,
    ],
],
php
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 keyDefault classMethods used by the manager
actions.download.classDownloadActionexecute($file), bulk($items)
actions.create_folder.classCreateFolderActionexecute($name, $disk, $parentId)
actions.rename.classRenameActionexecute($item, $newName)
actions.move.classMoveActionexecute($item, $destinationId, $destinationDisk)
actions.copy.classCopyActionexecute($item, $destinationId, $destinationDisk)
actions.delete.classDeleteActionexecute($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.

php
// 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:

MethodPurpose
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:

php
'file' => [
    'chunk_upload' => [
        'register_route' => false,
        'route_name' => 'media.chunks',
        'require_signed_url' => true,
    ],
],

Register the route in your application using the same name:

php
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:

php
// config/filament-sanchaya.php
'file' => [
    'finalizer' => \App\Actions\FinalizeMediaUploadAction::class,
],

The custom class must implement DP0\Sanchaya\Contracts\ChunkUploadFinalizer:

php
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.

Released under the MIT License.