Uploading Files
#Storing Files
There are various elements for uploading single files like avatar, file or image. What they have in common is that their files are being stored the same way on the database and filesystem level.
If you check out config/laraform.php
config file you'll see that there's a property called 'store'
:
/*
|--------------------------------------------------------------------------
| Store
|--------------------------------------------------------------------------
|
| Default location to store uploaded files.
|
*/
'store' => [
'disk' => 'public',
'folder' => 'files'
],
By default the disk
is set to public
, which refers to the public
disk in config/filesystems.php
and the folder
is an optional directory name which will be stored along with the filename. Let's see a few examples.
First, we have the default store setup pointing to public
disk and using uploads
folder in
/*
|--------------------------------------------------------------------------
| Store
|--------------------------------------------------------------------------
|
| Default location to store uploaded files.
|
*/
'store' => [
'disk' => 'public',
'folder' => 'uploads'
],
// config/filesystems.php
return [
// ...
'disks' => [
'local' => [
'driver' => 'local',
'root' => storage_path('app'),
],
'public' => [
'driver' => 'local',
'root' => storage_path('app/public'),
'url' => env('APP_URL') . '/storage',
'visibility' => 'public',
],
's3' => [
'driver' => 's3',
'key' => env('AWS_KEY'),
'secret' => env('AWS_SECRET'),
'region' => env('AWS_REGION'),
'bucket' => env('AWS_BUCKET'),
],
],
// ...
]
This means that files will be stored in you storage/app/public/uploads
directory because:
storage/app/public
comes fromconfig.filesystems.disks.public.root
uploads
comes fromconfig.laraform.folder
.
The file therefore can be accessed via APP_URL/storage/uploads/filename.ext
because:
APP_URL/storage
comes fromconfig.filesystems.disks.public.url
uploads/filename.ext
is what being stored in the database (uploads/
being prepended to the filename on a database level).
Just as a quick note
APP_URL/storage
directory is not available to users because nostorage
directory exists inpublic
folder. Files are being stored instorage/app/public
which needs to be symlinked topublic
directory, which you can achieve that easily by runningphp artisan storage:link
artisan command. After that described setup will work.
#Custom Store Location
In a next example we'll create a new filesystem disk, that anything uploaded via Laraform will use directly. Let's get started by creating a new disk called laraform
:
// config/filesystems.php
return [
// ...
'disks' => [
'local' => [
'driver' => 'local',
'root' => storage_path('app'),
],
'public' => [
'driver' => 'local',
'root' => storage_path('app/public'),
'url' => env('APP_URL') . '/storage',
'visibility' => 'public',
],
's3' => [
'driver' => 's3',
'key' => env('AWS_KEY'),
'secret' => env('AWS_SECRET'),
'region' => env('AWS_REGION'),
'bucket' => env('AWS_BUCKET'),
],
'laraform' => [
'driver' => 'locale',
'root' => public_path('uploads'),
'url' => env('APP_URL') . '/uploads',
],
],
// ...
]
As you can see we set the root path directly to public/uploads
and the url to APP_URL/uploads
. Now let's modify our Laraform store config to use this disk:
/*
|--------------------------------------------------------------------------
| Store
|--------------------------------------------------------------------------
|
| Default location to store uploaded files.
|
*/
'store' => [
'disk' => 'public',
'folder' => null
],
Now every file will be stored in public/uploads
and can be accessed via APP_URL/uploads
while only filenames will be stored in the database.
This is not a recommended approach, because it's better to store your files in the
storage
directory but it's only here to demonstrate that you are completely free to set up your storage location when using Laraform.
#Overriding Configuration
When you are using any element which involves file uploads, the files will be stored based on the disk
and folder
defined in the config. However you are free to define these two on an element level:
<?php
namespace App\Forms;
use Laraform;
use App\User;
class UserForm extends Laraform
{
public $model = User::class;
public $schema = [
'id' => [
'type' => 'key'
],
'name' => [
'type' => 'text',
'label' => 'Name',
],
'avatar' => [
'type' => 'avatar',
'label' => 'Avatar',
'disk' => 'public',
'folder' => 'avatars'
]
];
}
The users avatar pictures will be uploaded to the public
disk into avatars
folder and the avatars/filename.ext
will be stored in the database.
id
field is required in every case when you are using files within your forms.#Custom Store Function
To go even further you can write your own storing function as an anonym function:
<?php
namespace App\Forms;
use Laraform;
use App\User;
class UserForm extends Laraform
{
public $model = User::class;
public $schema = [
'id' => [
'type' => 'key'
],
'username' => [
'type' => 'text',
'label' => 'Username',
],
'avatar' => [
'type' => 'avatar',
'label' => 'Avatar',
'store' => function($file, $entity){
// storing file
return [
// ...
]
}
]
];
}
First you have the $file
which is the Illuminate\Http\UploadedFile
instance created from the uploaded file, then there's the $entity
which is basically an Illuminate\Database\Eloquent\Model
instance, in our case App\User
.
Using this custom store function you have to take care of saving the file with the help of $file
and $entity
, and you must return the final filename which should be stored in the database along with any other data which supposed to be updated (eg. meta data, as we will see later).
Let's see how we would finish our custom store function in order to store the filename in avatars/id-username.ext
format:
<?php
namespace App\Forms;
use Laraform;
use App\User;
use Illuminate\Support\Str;
class UserForm extends Laraform
{
public $model = User::class;
public $schema = [
'id' => [
'type' => 'key'
],
'username' => [
'type' => 'text',
'label' => 'Username',
],
'avatar' => [
'type' => 'avatar',
'label' => 'Avatar',
'folder' => 'avatars',
'disk' => 'public',
'store' => function($file, $entity){
$folder = 'avatars';
$filename = $entity->id . '-' . Str::slug($entity->username) . '.' . $file->extension();
$file->storeAs(
$folder,
$filename,
['disk' => 'public']
);
return [
'avatar' => $folder . '/' . $filename
];
}
]
];
}
#Storing Meta Data
Let's suppose we not only intend to store the filenames in our database but also some information about them. These typically include original filename, file size, mime type, etc. and can be extracted from the files, once uploaded.
To store them you simply need to instruct Laraform to do so by using store${Meta}
option:
<?php
namespace App\Forms;
use Laraform;
use App\User;
class UserForm extends Laraform
{
public $model = User::class;
public $schema = [
'id' => [
'type' => 'key',
],
'avatar' => [
'type' => 'avatar',
'label' => 'Avatar',
'storeSize' => 'avatar_size',
]
];
}
This will append a meta element to the form and store the file size in it.
In fact you can also create an element that storeSize
refers to and Laraform, instead of adding a meta element will store the size in that field:
<?php
namespace App\Forms;
use Laraform;
use App\User;
class UserForm extends Laraform
{
public $model = User::class;
public $schema = [
'id' => [
'type' => 'key',
],
'avatar' => [
'type' => 'avatar',
'label' => 'Avatar',
'storeSize' => 'avatar_size',
],
'avatar_size' => [
'type' => 'text',
'label' => 'File size'
]
];
}
The following meta options are provided within Laraform by default:
storeSize
- file sizestoreMime
- mime typestoreOriginalName
- original namestoreExtension
- extension.
#Storing Meta With Custom Store Function
When you are using custom store function you can't rely on Laraform's default meta storing functionality. Instead, you have to take care of adding your own meta elements to the form and return data form them from the store function. Let's see that by action:
Sticking with our example from custom store function we'll now add additional meta tags that will be saved along with the avatar image:
<?php
namespace App\Forms;
use Laraform;
use App\User;
use Illuminate\Support\Str;
class UserForm extends Laraform
{
public $model = User::class;
public $schema = [
'id' => [
'type' => 'key'
],
'username' => [
'type' => 'text',
'label' => 'Username',
],
'avatar' => [
'type' => 'avatar',
'label' => 'Avatar',
'folder' => 'avatars',
'disk' => 'public',
'store' => function($file, $entity){
$folder = 'avatars';
$filename = $entity->id . '-' . Str::slug($entity->username) . '.' . $file->extension();
$file->storeAs(
$folder,
$filename,
['disk' => 'public']
);
return [
'avatar' => $folder . '/' . $filename,
'avatar_size' => $file->getClientSize(),
'avatar_mime' => $file->getClientMimeType(),
'avatar_original_name' => $file->getClientOriginalName(),
'avatar_extension' => $file->getClientOriginalExtension(),
]
}
],
'avatar_size' => [
'type' => 'meta'
],
'avatar_mime' => [
'type' => 'meta'
],
'avatar_original_name' => [
'type' => 'meta'
],
'avatar_extension' => [
'type' => 'meta'
]
];
}
#Deleting Files
By default when you remove a file and save the changes the files will not only be removed from the database but also physically.
If you wish to turn off this feature you can set the prunable
option of the element to false
:
<?php
namespace App\Forms;
use Laraform;
use App\User;
class UserForm extends Laraform
{
public $model = User::class;
public $schema = [
'id' => [
'type' => 'key',
],
'avatar' => [
'type' => 'avatar',
'label' => 'Avatar',
'prunable' => false
]
];
}
#Custom Delete Function
To implement a custom deletion logic you can use delete
option of the element. Here's how you can perform the default deletion on your own:
<?php
namespace App\Forms;
use Laraform;
use App\User;
use Illuminate\Support\Facades\Storage;
class UserForm extends Laraform
{
public $model = User::class;
public $schema = [
'id' => [
'type' => 'key',
],
'avatar' => [
'type' => 'avatar',
'label' => 'Avatar',
'disk' => 'public',
'delete' => function($path, $entity){
Storage::disk('public')->delete($path);
}
]
];
}
The $path
is the path to the filename within storage disk root
and the $entity
is the Illuminate\Database\Eloquent\Model
instance, in our case App\User
.
#Handling Multiple Files
There are certain elements, like gallery and multifile which allows uploading multiple files. These elements in fact are very similar to list element and can be used to store their data the same way. Let's see what we mean by that.
#Simple Array
First of all there is the simple usage of a multifile type element, which stores uploaded filenames in an array:
Form's `data`:
<?php
namespace App\Forms;
use Laraform;
use App\User;
class UserForm extends Laraform
{
public $model = User::class;
public $schema = [
'documents' => [
'type' => 'multifile',
'label' => 'Documents'
]
];
}
On the backend User
model you can store these documents as a JSON string:
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class User extends Model
{
public function setDocumentsAttribute($value) {
$json = json_encode($value);
$this->attributes['documents'] = $json;
}
public function getDocumentsAttribute() {
if (!isset($this->attributes['documents'])) {
return null;
}
return json_decode($this->attributes['documents'], true);
}
}
#Array Of Objects
Secondly, if you want to use relationships to store your files data or add meta data you can define a storeFile
option which will transform the data structure into an array of objects, where the filename will be stored under the element specified as storeFile
:
Form's `data`:
<?php
namespace App\Forms;
use Laraform;
use App\User;
class UserForm extends Laraform
{
public $model = User::class;
public $schema = [
'documents' => [
'type' => 'multifile',
'label' => 'Documents',
'storeFile' => 'file'
]
];
}
As you can see file
is now stored within objects which enables us to store the data as a hasMany relationship:
<?php
// App/User.php
namespace App;
use Illuminate\Database\Eloquent\Model;
class User extends Model
{
public function documents() {
return $this->hasMany(Document::class);
}
}
<?php
// App/Document.php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Document extends Model
{
public $fillable = ['file'];
}
Of course this still does allow you to store documents in a JSON string too the same way described above.
#Using Meta
Meta data can be added to multiple files also when you are using the storeFile
option, because that way each file is an object.
To do that simply define store${Meta}
options for the element, just like when using single files:
<?php
namespace App\Forms;
use Laraform;
use App\User;
class UserForm extends Laraform
{
public $model = User::class;
public $schema = [
'id' => [
'type' => 'key',
],
'documents' => [
'type' => 'multifile',
'label' => 'Documents',
'storeFile' => 'file',
'storeSize' => 'size'
]
];
}
This will also append a meta element to each file object and can be stored by adding size
to the Document
model:
<?php
// App/Document.php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Document extends Model
{
public $fillable = ['file', 'size'];
}
If you wish to define your own elements for containing meta data you can do that by adding them to fields
option:
<?php
namespace App\Forms;
use Laraform;
use App\User;
class UserForm extends Laraform
{
public $model = User::class;
public $schema = [
'id' => [
'type' => 'key',
],
'documents' => [
'type' => 'multifile',
'label' => 'Documents',
'storeFile' => 'file',
'storeSize' => 'size',
'fields' => [
'size' => [
'type' => 'text'
]
]
]
];
}
fields
option as Laraform will handle them as normal elements, but it's main purpose is to store meta data. Therefore defining any other type of element than meta
may result in unintended side effects as it's not a supported feature of Laraform at this stage. If you do so make sure you use it with caution!#Custom Store Function
Writing a custom store function for multiple file upload elements is exactly as writing for single file uploaders:
<?php
namespace App\Forms;
use Laraform;
use App\User;
class UserForm extends Laraform
{
public $model = User::class;
public $schema = [
'id' => [
'type' => 'key',
],
'documents' => [
'type' => 'multifile',
'label' => 'Documents',
'store' => function($file, $entity){
// define store logic here
return [
// return file data
];
}
]
];
}
#Custom Delete Function
Just like writing custom store function creating custom delete functions are just the same as for single file uploaders:
<?php
namespace App\Forms;
use Laraform;
use App\User;
class UserForm extends Laraform
{
public $model = User::class;
public $schema = [
'id' => [
'type' => 'key',
],
'documents' => [
'type' => 'multifile',
'label' => 'Documents',
'delete' => function($path, $entity){
// define deletion logic here
}
]
];
}
#Validating File
When you define rules
option for an element the validation rules are applied to the element where they are defined. Because of this rules
will be applied for the multiple file upload element and validation rules like max will limit the number of files that can be uploaded but not the file size.
To apply rules for the uploaded files, use fileRules
option:
<?php
namespace App\Forms;
use Laraform;
use App\User;
class UserForm extends Laraform
{
public $model = User::class;
public $schema = [
'id' => [
'type' => 'key',
],
'documents' => [
'type' => 'multifile',
'label' => 'Documents',
// Maximum 2 files can be uploaded.
'rules' => 'max:2',
// File can be maximum 1 MB each.
'fileRules' => 'max:1024',
]
];
}