Hey guys,
Let’s do some coding for today!
As we all know, Laravel has a strong built-in Filesystem, we can handle many operations easily (upload, copy, move, etc)
But, Laravel doesn’t ship a File Manager to manage, and store the file’s info/metadata in the database.
From time to time, we will definitely lose the file tracks and everything, hard to maintain & manage (well, imagine if there are a lot of out-of-date files and we need to clean them but also need to ensure we don’t delete the wrong file).
DummyTechDev will show you how to build up a simple but powerful File Manager today!
This “File Manager” topic will be split into 3 parts:
- Part 1: File Manager
- Part 2: File APIs
- Part 3: Simple Gallary Uploader (Vue JS) and Image Management
Why build a simple File Manager for Laravel apps?
FileManager will come in handy in case you want to:
- Manage your uploaded files
- Listing
- Upload
- Delete
- Binding relationship between files & entities
- For instance: an article has many image files
- (Future-proof) Migrate to a new storage with ease
- E.g.:
- At first, you are using the local filesystem.
- After a year, you want to migrate all the files to
s3
- E.g.:
Pretty awesome, right?
However, you don’t need a FileManager if:
- You have a simple app
- You don’t upload that many files
Let’s build a simple File Manager for Laravel apps
1. Database Schema (Migration)
Hit this command: php artisan make:model File --migration
Then open the new migration file and add these columns:
return new class () extends Migration {
public function up(): void
{
Schema::create('files', function (Blueprint $table) {
$table->id();
$table->ulid()->unique();
$table->string('disk');
$table->string('path');
$table->string('url', 500);
$table->string('filename');
$table->integer('size');
$table->timestamps();
});
}
};
Info:
ulid
: this will act as a secondary identifier, we’ll use it in the frontenddisk
: this is the current disk of the file (local, public, s3)
Feel free to add your custom columns (if you want to).
Then, open the newly created Eloquent Model file and add the configuration there (table, fillable, cast)
2. Create a FileManager service
Let’s create a FileManager service using this path: app/services/FileManager/FileManager.php
<?php
namespace App\Services\FileManager;
use App\Models\File;
use Illuminate\Http\UploadedFile;
use Illuminate\Support\Facades\Storage;
class FileManager
{
protected string $disk;
public function __construct()
{
$this->disk = config('filesystems.default');
}
public function setDisk(string $disk): self
{
$this->disk = $disk;
return $this;
}
/**
* Upload a file to the configured disk
*
* @param UploadedFile $file
* @param array{path: string} $option
*
* @return File|null - null on storing failure
*/
public function upload(UploadedFile $file, array $option): ?File
{
$localFilePath = $file->storePublicly(
path: $option['path'],
options: ['disk' => $this->disk]
);
if ($localFilePath === false) {
return null;
}
return File::create([
'disk' => $this->disk,
'url' => $this->getUrl($localFilePath),
'path' => $localFilePath,
'filename' => $file->getClientOriginalName(),
'size' => $file->getSize(),
]);
}
public function getUrl(File|string $file): string
{
return Storage::disk($this->disk)->url(
$file instanceof File ? $file->path : $file
);
}
public function delete(File $file): bool
{
if (Storage::disk($this->disk)->exists($file->path)) {
Storage::disk($this->disk)->delete($file->path);
}
return $file->delete();
}
}
As you can see, we have these methods:
setDisk
: set a specific diskupload
: upload a new filegetUrl
: get the URL of a given filedelete
: delete the file
3. Test
Let’s write some Feature test cases for our new FileManager
, it is ridiculously easy to write integration tests in Laravel nowadays (thanks to their powerful Service Container and Facade strategy).
(We’ll update this once we finished writing the test cases, apologies)
Final
Well, just as simple as that, we now have a simple yet powerful FileManager to manage our files in the application.
This will be the Base
of our next technical guide: File Management APIs đ. Stays tuned.
Thanks for reading!