Cloudinary Integration in Laravel: A Step-by-Step Guide

Share this post on:
Discover Cloudinary Integration in Laravel with a Step-by-Step Guide by 200OK Solutions. Image shows a person working on a laptop with the Laravel logo. Read now at www.200oksolutions.com.

Media management is a critical part of any modern web application. Instead of storing and serving large image and video files directly from your server, you can offload this to a powerful media service like Cloudinary. It not only provides storage, but also offers real-time transformations, optimization, and global delivery through CDN.

In this tutorial, we’ll walk through integrating Cloudinary with a Laravel application to upload and manage images seamlessly.

1. Create a Free Cloudinary Account

  1. Go to Cloudinary Signup.
  2. Create a free account using your email, Google, or GitHub login.
  3. After logging in, navigate to the Dashboard → you’ll see your Cloud name, API Key, and API Secret. These are needed for integration.

2. Create an Upload Preset

Cloudinary uses upload presets to define upload rules (like allowed formats, storage folder, transformations).

  1. In your Cloudinary Dashboard, go to Settings → Upload → Upload Presets.
  2. Click Add Upload Preset.
  3. Give it a name (e.g., laravel_preset) and configure options as needed.
  4. Save it.

3. Create a Fresh Laravel Project

If you don’t already have a Laravel project, create one:

composer create-project laravel/laravel cloudinary_demo

4. Install the Cloudinary SDK

Install the Cloudinary Laravel SDK:

composer require cloudinary-labs/cloudinary-laravel

Then publish the configuration file:

php artisan vendor:publish --tag=cloudinary

5. Configure .env

In your Laravel project root, open the .env file and add the Cloudinary credentials.

#Cloudinary Configuration

CLOUDINARY_URL=cloudinary://<API_KEY>:<API_SECRET> @<CLOUD_NAME>

CLOUDINARY_UPLOAD_PRESET=laravel_preset

6. Create Model and Migration

We’ll store uploaded photo details in a database. Run:

php artisan make:model Photo -m

Update the migration file (database/migrations/xxxx_create_photos_table.php):

Schema::create('photos', function (Blueprint $table) {

$table->id();

$table->string('title')->nullable();

$table->string('image_url');

$table->string('image_public_id');

$table->timestamps();

});

Run the migration:

php artisan migrate

7. Create a Controller

Generate a controller:

php artisan make:controller PhotoController

Update app/Http/Controllers/PhotoController.php:

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;

use App\Models\Photo;

use CloudinaryLabs\CloudinaryLaravel\Facades\Cloudinary;

class PhotoController extends Controller

{

public function index()

{

$photos = Photo::latest()->get();

return view('photos.index', compact('photos'));

}

public function create()

{

return view('photos.create');

}

public function store(Request $request)

{

$request->validate([

'title' => 'nullable|string|max:255',

'image' => 'required|image|max:2048',

]);

$file = $request->file('image');

// Upload to Cloudinary

$uploadResult = Cloudinary::upload($file->getRealPath(), [

'folder' => 'laravel_uploads',

'upload_preset' => env('CLOUDINARY_UPLOAD_PRESET'),

]);

Photo::create([

'title' => $request->title,

'image_url' => $uploadResult->getSecurePath(),

'image_public_id' => $uploadResult->getPublicId(),

]);

return redirect()->route('photos.index')->with('success', 'Photo uploaded successfully!');

}

public function destroy(Photo $photo)

{

Cloudinary::destroy($photo->image_public_id);

$photo->delete();

return redirect()->route('photos.index')->with('success', 'Photo deleted successfully!');

}

}

8. Define Routes

In routes/web.php:

use App\Http\Controllers\PhotoController;

Route::get('/photos', [PhotoController::class, 'index'])->name('photos.index');

Route::get('/photos/create', [PhotoController::class, 'create'])->name('photos.create');

Route::post('/photos', [PhotoController::class, 'store'])->name('photos.store');

Route::delete('/photos/{photo}', [PhotoController::class, 'destroy'])->name('photos.destroy');

9. Create Blade Views

Resources/views/layouts/app.blade.php

<!DOCTYPE html>

<html>

<head>

<title>Laravel Cloudinary Demo</title>

<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css">

</head>

<body>

<div class="container mt-4">

@yield('content')

</div>

</body>

</html>

resources/views/photos/index.blade.php

@extends('layouts.app')

@section('content')

<div class="container">

<h2>Uploaded Photos</h2>

<a href="{{ route('photos.create') }}" class="btn btn-primary mb-3">Upload New Photo</a>

<div class="row">

@forelse($photos as $photo)

<div class="col-md-4 mb-3">

<div class="card">

<img src="{{ $photo->image_url }}" class="card-img-top" alt="Photo">

<div class="card-body">

<h5>{{ $photo->title ?? 'Untitled' }}</h5>

<form action="{{ route('photos.destroy', $photo) }}" method="POST">

@csrf

@method('DELETE')

<button class="btn btn-danger w-100">Delete</button>

</form>

</div>

</div>

</div>

@empty

<p>No photos uploaded yet.</p>

@endforelse

</div>

</div>

@endsection

Resources/views/photos/create.blade.php

@extends('layouts.app')

@section('content')

<div class="container">

<h1>Upload Photo</h1>

<form method="POST" action="{{ route('photos.store') }}" enctype="multipart/form-data">

@csrf

<div class="mb-3">

<label for="title">Title</label>

<input type="text" name="title" class="form-control">

</div>

<div class="mb-3">

<label for="image">Choose Image</label>

<input type="file" name="image" class="form-control" required>

</div>

<button type="submit" class="btn btn-primary">Upload</button>

</form>

</div>

@endsection

10. Test the Application

  • Run the app:
php artisan serve
  • Visit http://<host:port>/photos/create.
  • Upload an image → it will be stored in Cloudinary and a record saved in your database.

Conclusion

You’ve successfully integrated Cloudinary with Laravel. Now your images are stored in the cloud, optimized automatically, and served globally via CDN. From here, you can explore advanced features like transformations (resize, crop, format conversion), video uploads, and automatic optimization.