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