In this article, you'll learn how to get started up and running with Vultr Object Storage on Laravel 8 Application using Flysystem integration from scratch. Do follow along with the code example below and let's build a simple image upload application that will directly store the image into Vultr Object Storage.
Prerequisites
You will need:
- PHP installed on the local system
- Composer dependency manager
- Laravel Installer installed globally
- League Flysystem AWS S3 adapter package
- Vultr Object Storage credentials (access key & secret key)
Install Laravel and Dependencies
First thing first, install the new Laravel 8 application and the necessary dependencies for this guide. This is very straightforward instruction but make sure to have all the prerequisites above ready.
# create new laravel application called "image-uploader" laravel new image-uploader # change into the project directory cd image-uploader # install flysystem adapter for AWS S3 SDK v3.x and intervention image package composer require league/flysystem-aws-s3-v3:~1.0 intervention/image # run PHP local server to serve the application php artisan serve # open project in Visual Studio Code or other IDE code .
Environment and Configuration
The Laravel .env (environment) configuration is where the Vultr Object Storage credentials is defined. Create a new key and value pair like below at the end of the .env file and copy out the value from your Vultr Object Storage dashboard panel.
# update the filesystem driver that will be used FILESYSTEM_DRIVER=vultr # define new key-value pair for vultr object storage credentials VULTR_ACCESS_KEY=PASTE_YOUR_VULTR_ACCESS_KEY_HERE VULTR_SECRET_KEY=PASTE_YOUR_VULTR_SECRET_KEY_HERE VULTR_REGION=ewr1 VULTR_BUCKET=PASTE_YOUR_BUCKET_NAME_HERE VULTR_ENDPOINT=ewr1.vultrobjects.com


Once the environment variable is set, define a new filesystem disk configuration like below. Do note that "s3" driver will be used since Vultr Object Storage is s3 compatible.
<?php return [ 'default' => env('FILESYSTEM_DRIVER', 'local'), 'disks' => [ 'vultr' => [ 'driver' => 's3', 'key'=> env('VULTR_ACCESS_KEY'), 'secret' => env('VULTR_SECRET_KEY'), 'region' => env('VULTR_REGION'), 'bucket' => env('VULTR_BUCKET'), 'endpoint' => env('VULTR_ENDPOINT'), ], # other default configuration omitted ] # other default configuration omitted ];
Interacting Vultr Object Storage with Laravel Filesystem
To interact with the configured disk, Laravel provide the `Storage` facade where you can call several predefined methods to perform various actions.
Simple Command
Below are some of the basic command to check if file exists, put contents into a file, and get the content of a file in the object storage. You can run the code below by using the Laravel Tinker tool.
# launch Laravel Tinker (REPL) tool php artisan tinker # declare the namespace Laravel tinker will use use Illuminate\Support\Facades\Storage; # obtaining disk instance (if not specified will use default disk instance defined in config / environment settings) Storage::disk('vultr'); # put "Hello world!" content into "welcome.txt" Storage::put('welcome.txt', 'Hello world!'); # get the content of "welcome.txt" Storage::get('welcome.txt'); # check if "welcome.txt" exists if (Storage::exists('welcome.txt')) { # download the "welcome.txt" file Storage::download('welcome.txt'); }

To learn more on the available methods that you can use to interact with the Object storage, you may refer to [Laravel File Storage documentation](https://laravel.com/docs/master/filesystem) where each of the methods is being documented in a detailed manner.
Simple Image-Upload Application
Now that you have seen the basic command, let's create a simple **image-upload application** that will make use of Laravel model-view-controller (MVC) architecture as well as leveraging Vultr Object Storage.
Let's get started by defining the command to generate the Laravel model and controller that will be used. You may name it however you want but we recommend that you follow along with the example.
# generate the necessaary model and migration php artisan make:model Image -m # generate the necessaray controller php artisan make:controller ImageController php artisan make:controller HomeController
Routes
For this simple application, it will only need 2 routes where the first 1st is for the home page view and the 2nd one is to handle the image uploading to Vultr Object storage and save the image path in the database.
<?php use Illuminate\Support\Facades\Route; use App\Http\Controllers\HomeController; use App\Http\Controllers\ImageController; Route::get('/', [HomeController::class, 'index']) ->name('home'); Route::post('images', [ImageController::class, 'store']) ->name('upload-image');
Migration
When using the `-m` flag to generate the Laravel model, the command will automatically generate a migration file for the associated model as well. Below are the columns that have been defined for the migration.
Schema::create('images', function (Blueprint $table) { $table->id(); $table->string('name'); $table->string('path'); $table->timestamps(); });
Model
For the image model, add a fillable property to make the Image model column mass assignable.
/** * The attributes that are mass assignable. * * @var array */ protected $fillable = [ 'name', 'path', ];
Views
There will only be one view and this is essentially the front-end part of the application. The form will be on this page and upon submission, the uploaded image will appear at the bottom of the page as a thumbnail unless errors occurred when uploading the image files.
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>Image Uploader</title> <link href="https://unpkg.com/tailwindcss@^2/dist/tailwind.min.css" rel="stylesheet" > </head> <body class="bg-gray-100"> <div class="container p-4 mx-auto flex justify-center"> <div class="mt-8 p-6 w-full md:w-1/2 shadow-xl rounded-lg"> <div> <h2 class="text-gray-800 font-bold border-l-4 border-blue-500 pl-3"> Image Uploader </h2> <form action="{{ route('upload-image') }}" method="post" enctype="multipart/form-data" class="mt-8 flex flex-col items-start" > @csrf <input type="file" name="image"> <button type="submit" class="mt-4 px-4 py-1 bg-blue-500 rounded-md shadow text-white font-medium" > Submit </button> </form> @if ($errors->any()) <div class="mt-4 p-4 bg-gray-200 rounded-lg"> <ul> @foreach ($errors->all() as $error) <li>{{ $error }}</li> @endforeach </ul> </div> @endif </div> @if ($images->count()) <div class="mt-8"> <div class="mb-8"> <h2 class="text-gray-800 font-bold border-l-4 border-blue-500 pl-3" > Uploaded Images </h2> </div> <div class="flex flex-wrap"> @foreach($images as $name => $imagePath) <div class="w-1/2 md:w-1/3"> <img src="{{ $imagePath }}" alt="{{ $name }}"> <h3 class="text-center text-sm"> {{ $name }} </h3> </div> @endforeach </div> </div> @endif </div> </div> </body> </html>

Controller
The logic is stored in the controller and on the main page it query the database to check if there are any images available. If there's one then it will be retrieved from Vultr Object Storage and returned to the home page view.
Home Controller
<?php namespace App\Http\Controllers; use App\Models\Image; use Illuminate\Support\Facades\Storage; class HomeController extends Controller { public function index() { $images = Image::query() ->get(['name', 'path']) ->flatMap(function ($image) { if (Storage::exists($image->path)) { return [ $image->name => Storage::url($image->path) ]; } }); return view('home', compact('images')); } }
Image Controller
<?php namespace App\Http\Controllers; use App\Models\Image; use Illuminate\Support\Str; use Illuminate\Support\Facades\Storage; class ImageController extends Controller { public function store() { $hasImagesDirectory = Storage::exists($directory = 'images'); /* create directory if not exist */ if (! $hasImagesDirectory) { Storage::makeDirectory('images'); } /* validate the request to make it required and only accept image file */ $image = request()->validate(['image' => ['required', 'image']])['image']; /* randomise uploaded image name and apply the extension */ $fileName = Str::random(4) . '.' . $image->getClientOriginalExtension(); /* store the image to vultr and retrieve the file path */ $filePath = $image->storePubliclyAs( $directory, $fileName, 'vultr' ); /* save the record to the database */ Image::create(['name' => $fileName, 'path' => $filePath]); return redirect()->route('home'); } }

When you visit the bucket in the Vultr dashboard by right you will see all of the uploaded images inside the images directory.
