yassinedabbous / laravel-file-cast
Easily link your uploads with their db columns
Requires
- php: >=7.4
- guzzlehttp/guzzle: ^7.5
- laravel/framework: >=9.0
Requires (Dev)
- friendsofphp/php-cs-fixer: ^3.64
- orchestra/testbench: >=7.0
README
Laravel File Cast
Easily link your uploads with their table columns
Table of Content
✨ Features
- Mapping uploaded files from the Request to the Model with ease.
- Dealing with multiple forms of data (File instance, Local path, Remote url, Base64 URI, Array values ...).
- Old files are automatically cleaned on update and delete events.
- No extra code & No media table!
🔻 Installation
composer require yassinedabbous/laravel-file-cast
🧑💻 Usage
Just cast any of your table columns with FileCast::class like that:
use YassineDabbous\FileCast\FileCast; class User extends Model { # Laravel<11.x protected $casts = [ 'avatar' => FileCast::class, // use default disk 'avatar' => FileCast::class.':s3,photo.png', // use "S3" disk and "photo.png" as default value ]; # OR # Laravel >=11.x public function casts(): array { return [ 'avatar' => FileCast::class, // use default disk 'avatar' => FileCast::using(disk: 's3', default: 'photo.png'), // use S3 disk and "photo.png" as default value 'avatar' => FileCast::using(disk: $this->disk_column), // use column value as a disk name ]; } }
This will cast the avatar column.
For Old Laravel Versions (< 11.x):
If for any reason you want to customize the disk with a dynamic value, you should modify your model with a public method that returns an array containing the disk name for each column:
class Post extends Model { ... /** Set a disk for each column */ public function disks(): array { return [ 'photo' => $this->disk_column, # use column value as a disk 'video' => $migrated ? 's3' : 'public', # conditional disk ]; } ... }
🗃️ Supported data formats
• Uploaded file:
Assuming your request form has a file/image named "avatar", you can assign the file to it's column:
$model->avatar = $request->avatar; $model->save();
Or even fill the model with request input:
Model::create( $request->validated() ); // or $model->update( $request->validated() );
It accept any type of file, so you can do that also:
• Local file path:
$model->avatar = '/full/path/to/local/file.ext'; $model->avatar; // /disk/folder/file.ext
• Remote file url:
$model->avatar = 'https://via.placeholder.com/150/FF0000/FFFFFF'; $model->avatar; // /disk/folder/file.png
• Base64 URI:
$model->avatar = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAIAAAACCAYAAABytg0kAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAAEnQAABJ0Ad5mH3gAAAAWSURBVBhXY/zPwABEDAxMIIKBgYEBAB8XAgIcVRdpAAAAAElFTkSuQmCC'; $model->avatar; // /disk/folder/file.png
• JSON string:
$model->avatar = '{"key1": "value1"}'; $model->avatar; // /disk/folder/file.json
• Array as JSON or CSV:
# Store associative array as *json* file # => folder/file.json $model->avatar = ['key1' => 'value1', 'key2' => 'value2']; # Store multi list array as *csv* file # => folder/file.csv $model->avatar = [ ['value1', 'value2', 'value3'], ['value1', 'value2', 'value3'], ['value1', 'value2', 'value3'], ];
• NULL ?
Null value will cause automatic file deletion (configurable):
$model->avatar = null; # == $model->avatar->delete();
It has never been easier!
🛠️ Functionalities:
To provide more functionalities to file caster, this package uses a wrapper class name FileField, this class works as a proxy to the Storage facade with some additional methods.
• Storage Proxy
As a proxy to the Laravel Storage facade, you can call any method from Storage directly on the file field without providing the file path:
use Illuminate\Support\Facades\Storage; # using Laravel Storage Facade: Storage::disk('disk')->url(path: 'path.ext'); Storage::disk('disk')->json(path: 'path.ext'); Storage::disk('disk')->put(path: 'path.ext', contents: 'data'); Storage::disk('disk')->temporaryUrl(path: 'path.ext', expiration: now()->addMinutes(5)); ... # using File Cast $model->avatar->url(); $model->avatar->json(); $model->avatar->put(contents: 'data'); $model->avatar->temporaryUrl(expiration: now()->addMinutes(5)); ...
• Additional Methods
In addition to Storage methods, FileField comes with some useful methods:
$model->avatar->toBase64(); # Base64 string: 'iVBORw0KGgoAAAANS...' $model->avatar->toBase64URI(); # Base64 URI: 'data:image/png;base64,iVBORw0KGgoAAAANS...' $model->avatar->toArray(); # returns *json* and *csv* file's content as array
• File Manipulation
- Deleting file:
Old files are cleaned automatically when column value updated.
$model->avatar; # 'old_file.png'; $model->avatar = $newFileUploaded; # 'old_file.png' deleted!
To automatically delete old files on Model deleted event, add HasFileCast trait to your model.
use YassineDabbous\FileCast\HasFileCast; class User extends Model { use HasFileCast; ... }
To delete files manually:
$model->avatar = NULL; # Delete the file without updating table. $model->avatar->delete(); # Delete the file without updating table. $model->avatar->delete(persist: TRUE); # Delete the file & save changes to DB.
- Moving file to new path:
$model->avatar; # 'folder/old_file.png'; $model->avatar = '@new_folder/new_file.png'; # Move the file without updating table. (path should start with "@") $model->avatar->move(to: 'new_folder/new_file.png'); # Move the file without updating table. $model->avatar->move(to: 'new_folder/new_file.png', persist: TRUE); # Move the file & save changes to DB.
• Extending
File Cast is "macroable", which allows you to add additional methods to FileField class at run time. The FileField class' macro method accepts a closure that will be executed when your macro is called. The macro closure may access the FileField's other methods via $this, just as if it were a real method of the FileField class. For example, the following code adds a resize method to the FileField class:
use YassineDabbous\FileCast\FileField; FileField::macro('resize', function ($with, $height) { $image = imagecreatefrompng($this->path()); $imgResized = imagescale($image , $with, $height); imagejpeg($imgResized, $this->path()); return $this; }); # resize uploaded image $model->photo = $request->file('image'); $model->photo->resize(500, 500);
⚙️ Configuration
You can optionally publish the config file with:
php artisan vendor:publish --tag=file-cast-config
These are the contents of the default config file that will be published:
<?php return [ /** Default value when no file uploaded. */ 'default' => env('FILE_CAST_DEFAULT'), /** Default storage disk */ 'disk' => env('FILE_CAST_DISK'), /** Default storage folder. If NULL, the Model's table name will be used. */ 'folder' => env('FILE_CAST_FOLDER'), /** Automatically clean files on column value updated. */ 'auto_delete' => env('FILE_CAST_AUTO_DELETE', true), /** Serialize attribute to full URL. */ 'serialize_to_full_url' => env('FILE_CAST_SERIALIZE_TO_FULL_URL', true), ];