F
Filament5mo ago
ruff

FileUpload store path instead of filename

Hello everyoone. Just moved to Filament from previous legacy admin panel. I really need FileUpload to store relative path instead of just a filename for compatibility reasons. Is it possible to achieve with hooks like before and after hydration?
Solution:
I was able to achieve this by extending FileUpload class ``` namespace App\Forms\Components; class FileUpload extends BaseFileUpload...
Jump to solution
11 Replies
slawek_n
slawek_n5mo ago
Can you elaborate more? In worst case scenario you can create separate field eg. path_relative and build its state to match your requirement. Take look https://filamentphp.com/docs/3.x/forms/fields/file-upload#generating-custom-file-names Maybe this will solve your case. Edit: fixed link
ruff
ruff5mo ago
Let's say i have field called image. Its value is stored in database as 'uploads/somefile.jpg'. FileUpload expects its value to be 'somefile.jpg' in order to work correctly. So i need somehow to alter its value and remove 'uploads/' before it goes to FileUpload, then i need to add it back before it goes to database.
slawek_n
slawek_n5mo ago
filament uses Laravel storage, so assuming you use 'public' disk, you can go with:
FileUpload::make('image')
->image()
->disk('public')
->directory('uploads')
FileUpload::make('image')
->image()
->disk('public')
->directory('uploads')
https://filamentphp.com/docs/3.x/forms/fields/file-upload#configuring-the-storage-disk-and-directory
slawek_n
slawek_n5mo ago
Then, field will expect the value to be uploads/someFile.jpg not someFile.jpg and will store it properly too.
ruff
ruff5mo ago
nah, i configured uploads disk, i need path to contain disk's directory
slawek_n
slawek_n5mo ago
if above is not the way, afterStateUpdated and dehydrateStateUsing are your friends 😉
ruff
ruff5mo ago
i need something like this, but i'm not sure if it is possible with FIleUpload field
FileUpload::make('photo')
->image()
->label('Photo')
->formatStateUsing(fn ($state) => str_replace("uploads/", "", $state))
->dehydrateStateUsing(fn (string $state): string => "uploads/".$state)
->required(),
FileUpload::make('photo')
->image()
->label('Photo')
->formatStateUsing(fn ($state) => str_replace("uploads/", "", $state))
->dehydrateStateUsing(fn (string $state): string => "uploads/".$state)
->required(),
slawek_n
slawek_n5mo ago
for me it looks like it should be working.
ruff
ruff5mo ago
It works with TextInput, but gives errors with FileUpload, even if i do no processing at all
->formatStateUsing(fn ($state) => $state)
->formatStateUsing(fn ($state) => $state)
Solution
ruff
ruff5mo ago
I was able to achieve this by extending FileUpload class
namespace App\Forms\Components;

class FileUpload extends BaseFileUpload
{
protected function setUp(): void
{
parent::setUp();

$this->afterStateHydrated(static function (BaseFileUpload $component, string | array | null $state): void {
if (blank($state)) {
$component->state([]);

return;
}

$shouldFetchFileInformation = $component->shouldFetchFileInformation();

$files = collect(Arr::wrap($state))
->map(static fn (string $file): string => str_replace("uploads/", "", $file))
->filter(static function (string $file) use ($component, $shouldFetchFileInformation): bool {
if (blank($file)) {
return false;
}

if (! $shouldFetchFileInformation) {
return true;
}

try {
return $component->getDisk()->exists($file);
} catch (UnableToCheckFileExistence $exception) {
return false;
}
})
->mapWithKeys(static fn (string $file): array => [((string) Str::uuid()) => $file])
->all();

$component->state($files);
});

$this->dehydrateStateUsing(static function (BaseFileUpload $component, ?array $state): string | array | null | TemporaryUploadedFile {
$files = collect(array_values($state ?? []))->map(static fn (string $file): string => "uploads/".($file));

if ($component->isMultiple()) {
return $files;
}

return $files[0] ?? null;
});
}
}
namespace App\Forms\Components;

class FileUpload extends BaseFileUpload
{
protected function setUp(): void
{
parent::setUp();

$this->afterStateHydrated(static function (BaseFileUpload $component, string | array | null $state): void {
if (blank($state)) {
$component->state([]);

return;
}

$shouldFetchFileInformation = $component->shouldFetchFileInformation();

$files = collect(Arr::wrap($state))
->map(static fn (string $file): string => str_replace("uploads/", "", $file))
->filter(static function (string $file) use ($component, $shouldFetchFileInformation): bool {
if (blank($file)) {
return false;
}

if (! $shouldFetchFileInformation) {
return true;
}

try {
return $component->getDisk()->exists($file);
} catch (UnableToCheckFileExistence $exception) {
return false;
}
})
->mapWithKeys(static fn (string $file): array => [((string) Str::uuid()) => $file])
->all();

$component->state($files);
});

$this->dehydrateStateUsing(static function (BaseFileUpload $component, ?array $state): string | array | null | TemporaryUploadedFile {
$files = collect(array_values($state ?? []))->map(static fn (string $file): string => "uploads/".($file));

if ($component->isMultiple()) {
return $files;
}

return $files[0] ?? null;
});
}
}