Skip to content

YassineDabbous/laravel-file-cast

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

52 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

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),
];

About

Easily link your uploads with their table columns

Topics

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages