Web

Cloudinary Laravel: Fix Upload Error & Configure

Fix 'Call to undefined method Cloudinary::upload()' in Laravel. Step-by-step guide to configure Cloudinary Laravel package, import correct facade, set up config, and upload files using facade or Storage disk.

1 answer 1 view

How should I configure Cloudinary and use it in Laravel? I’m trying to implement Cloudinary in my Laravel project but I get the following error when calling the upload() method:

Call to undefined method Cloudinary\Cloudinary::upload()

How can I fix this? I installed the package with composer and imported the facade, but it seems to resolve to Cloudinary\Cloudinary. Below are my installation steps, controller code and config files.

Installed the package:

bash
composer require cloudinary-labs/cloudinary-laravel

Facade import:

php
use CloudinaryLabs\CloudinaryLaravel\Facades\Cloudinary;

Controller code:

php
use CloudinaryLabs\CloudinaryLaravel\Facades\Cloudinary;

public function images(Request $request)
{
 $request->validate([
 'file' => 'required|image|max:1024',
 ]);

 $result = Cloudinary::upload($request->file('file')->getRealPath())->getSecurePath();
 dd($result->getPublicId());
}

cloudinary.php config:

php
return [

 'notification_url' => env('CLOUDINARY_NOTIFICATION_URL'),

 'cloud_url' => env('CLOUDINARY_URL', 'cloudinary://' . env('CLOUDINARY_KEY') . ':' . env('CLOUDINARY_SECRET') . '@' . env('CLOUDINARY_CLOUD_NAME')),
 'cloud' => [
 'cloud_name' => env('CLOUDINARY_CLOUD_NAME'),
 'api_key' => env('CLOUDINARY_API_KEY'),
 'api_secret' => env('CLOUDINARY_API_SECRET'),
 ],
];

filesystems.php config (relevant parts):

php
'disks' => [

 'local' => [
 'driver' => 'local',
 'root' => storage_path('app/private'),
 'serve' => true,
 'throw' => false,
 'report' => false,
 ],

 'public' => [
 'driver' => 'local',
 'root' => storage_path('app/public'),
 'url' => env('APP_URL') . '/storage',
 'visibility' => 'public',
 'throw' => false,
 'report' => false,
 ],

 's3' => [
 'driver' => 's3',
 'key' => env('AWS_ACCESS_KEY_ID'),
 'secret' => env('AWS_SECRET_ACCESS_KEY'),
 'region' => env('AWS_DEFAULT_REGION'),
 'bucket' => env('AWS_BUCKET'),
 'url' => env('AWS_URL'),
 'endpoint' => env('AWS_ENDPOINT'),
 'use_path_style_endpoint' => env('AWS_USE_PATH_STYLE_ENDPOINT', false),
 'throw' => false,
 'report' => false,
 ],
 'cloudinary' => [
 'driver' => 'cloudinary',
 'key' => env('CLOUDINARY_KEY'),
 'secret' => env('CLOUDINARY_SECRET'),
 'cloud' => env('CLOUDINARY_CLOUD_NAME'),
 'url' => env('CLOUDINARY_URL'),
 'secure' => (bool) env('CLOUDINARY_SECURE', true),
 ],
 ],

Questions:

  • Why does calling Cloudinary::upload(…) throw ‘Call to undefined method Cloudinary\Cloudinary::upload()’?
  • Am I importing the correct facade or class?
  • What is the correct way to configure Cloudinary in Laravel and upload files from a controller?

Cloudinary Laravel throws that error because your code is resolving to the SDK core class (Cloudinary\Cloudinary) instead of the package facade that exposes upload(); make sure you import and use the facade CloudinaryLabs\CloudinaryLaravel\Facades\Cloudinary, publish the package config and set CLOUDINARY_URL or the API key/secret environment variables, then clear caches. Once the facade (or the cloudinary filesystem disk) is wired correctly you can upload with Cloudinary::upload(...) or Storage::disk('cloudinary')->putFile(...) and read public_id / secure_url from the returned result.


Contents


Why this error happens (Cloudinary Laravel)

That error — Call to undefined method Cloudinary\Cloudinary::upload() — means PHP ended up calling a method on the SDK core class, Cloudinary\Cloudinary, which doesn’t implement an upload() helper. The Cloudinary Laravel package exposes convenient helpers through its facade CloudinaryLabs\CloudinaryLaravel\Facades\Cloudinary; if your code or an alias points to Cloudinary\Cloudinary instead, upload() won’t exist there.

Why does that happen? A few common causes:

  • You imported or aliased the wrong class (for example use Cloudinary\Cloudinary; or a bare use Cloudinary;).
  • A manual alias in config/app.php maps Cloudinary to the SDK class instead of the package facade.
  • Package auto-discovery or service provider registration failed (so the facade binding isn’t registered).
  • Cache/autoload still references an old binding (after changing imports/config).

The package README explains the proper facade usage and the blog post on Cloudinary’s site highlights the same root cause and fix (import the facade rather than the core SDK) — see the GitHub README and Cloudinary’s Laravel guide for confirmation and examples. For a community discussion of similar issues see a relevant StackOverflow thread.


Check imports and aliases

Quick checks you can do right away:

  1. Confirm your controller imports the facade exactly:
php
use CloudinaryLabs\CloudinaryLaravel\Facades\Cloudinary;

Do NOT have any competing imports like use Cloudinary\Cloudinary; or use Cloudinary;.

  1. If you prefer not to import, call the facade by its fully-qualified name to be explicit:
php
\CloudinaryLabs\CloudinaryLaravel\Facades\Cloudinary::upload($path);
  1. Look in config/app.php (only if you manually added an alias). The alias should point to the facade, not SDK:
php
'aliases' => [
 // ...
 'Cloudinary' => \CloudinaryLabs\CloudinaryLaravel\Facades\Cloudinary::class,
],
  1. Detect what the facade is resolving to (run this in a route/controller temporarily):
php
dd(get_class(\CloudinaryLabs\CloudinaryLaravel\Facades\Cloudinary::getFacadeRoot()));

If that prints Cloudinary\Cloudinary, you found the problem — the binding points to the SDK core.

For background, the package README contains the canonical import and usage examples: https://github.com/cloudinary-community/cloudinary-laravel. The Cloudinary blog also documents the common mis-import issue: https://cloudinary.com/blog/laravel_file_upload_to_a_local_server_or_to_the_cloud.


Configure Cloudinary in Laravel (Laravel Cloudinary configuration)

Follow these steps to make sure Laravel is configured correctly.

  1. Install (you already did):
bash
composer require cloudinary-labs/cloudinary-laravel
  1. Publish the package config (if needed):
bash
php artisan vendor:publish --provider="CloudinaryLabs\CloudinaryLaravel\CloudinaryServiceProvider"

This drops config/cloudinary.php you can edit.

  1. Provide credentials in your .env (pick one approach). Recommended: use CLOUDINARY_URL:
CLOUDINARY_URL=cloudinary://<API_KEY>:<API_SECRET>@<CLOUD_NAME>

Or define each var:

CLOUDINARY_CLOUD_NAME=your_cloud_name
CLOUDINARY_API_KEY=your_api_key
CLOUDINARY_API_SECRET=your_api_secret

Important: be consistent. Your config/cloudinary.php and config/filesystems.php should read the same env names. In your posted cloudinary.php you mix CLOUDINARY_KEY/CLOUDINARY_SECRET with CLOUDINARY_API_KEY/CLOUDINARY_API_SECRET — unify those names so the package can authenticate.

  1. Filesystem disk (example in config/filesystems.php):
php
'disks' => [
 // ...
 'cloudinary' => [
 'driver' => 'cloudinary',
 'url' => env('CLOUDINARY_URL'),
 'secure' => (bool) env('CLOUDINARY_SECURE', true),
 ],
],

(See the package README for the exact disk keys your installed version expects.)

  1. Clear config cache so Laravel reads the new env entries:
bash
php artisan config:clear
composer dump-autoload

If you want the authoritative config examples and provider details, check the package repo and Cloudinary’s guide: https://github.com/cloudinary-community/cloudinary-laravel and https://cloudinary.com/blog/laravel_file_upload_to_a_local_server_or_to_the_cloud.


Upload examples: Facade and Storage (Cloudinary upload)

Below are safe examples you can paste into your controller. They handle the different return shapes that older/newer versions may produce.

A. Using the package facade (explicit import at top of file: use CloudinaryLabs\CloudinaryLaravel\Facades\Cloudinary;)

php
public function images(Request $request)
{
 $request->validate(['file' => 'required|image|max:1024']);
 $path = $request->file('file')->getRealPath();

 $upload = Cloudinary::upload($path, ['folder' => 'products']);

 // The package/SDK may return an array or an object depending on version.
 if (is_array($upload)) {
 $publicId = $upload['public_id'] ?? null;
 $secureUrl = $upload['secure_url'] ?? null;
 } else {
 $publicId = method_exists($upload, 'getPublicId') ? $upload->getPublicId() : ($upload->public_id ?? null);
 $secureUrl = method_exists($upload, 'getSecurePath') ? $upload->getSecurePath() : ($upload->secure_url ?? null);
 }

 return response()->json(['public_id' => $publicId, 'secure_url' => $secureUrl]);
}

Note: your original chain did ->getSecurePath() and then ->getPublicId() — that sets $result to a string (secure URL) and then attempts to call a method on a string. Don’t do that; inspect the upload result first.

B. Using Laravel Filesystem (recommended if you want to treat Cloudinary like a disk)

php
use Illuminate\Support\Facades\Storage;

public function images(Request $request)
{
 $request->validate(['file' => 'required|image|max:1024']);
 $result = Storage::disk('cloudinary')->putFile('products', $request->file('file'));

 // $result may be a path string or an array depending on driver version.
 return response()->json(['upload' => $result]);
}

C. Alternative: storeOnCloudinary (macro added by some integrations)

php
$info = $request->file('file')->storeOnCloudinary('products');
// inspect $info to see public_id / secure_url

A community step-by-step example that demonstrates the storeOnCloudinary approach can be helpful: https://dev.to/issaahmed/laravel-image-management-with-cloudinary-a-step-by-step-guide-to-uploading-updating-and-deleting-images-57nl.


Troubleshooting checklist and quick tests

If uploads still fail, go through this checklist in order:

    1. Confirm the facade resolves to the package wrapper:
  • Add dd(get_class(\CloudinaryLabs\CloudinaryLaravel\Facades\Cloudinary::getFacadeRoot())); — it should NOT be Cloudinary\Cloudinary.

    1. Remove conflicting imports/aliases:
  • Search codebase for use Cloudinary\Cloudinary; or any Cloudinary alias in config/app.php and remove/replace it.

    1. Re-publish config and clear caches:
bash
php artisan vendor:publish --provider="CloudinaryLabs\CloudinaryLaravel\CloudinaryServiceProvider"
php artisan config:clear
php artisan cache:clear
composer dump-autoload
    1. Validate env variables:
  • Prefer CLOUDINARY_URL or keep CLOUDINARY_CLOUD_NAME/CLOUDINARY_API_KEY/CLOUDINARY_API_SECRET consistent with your published config.

    1. Check installed packages and versions:
bash
composer show cloudinary-labs/cloudinary-laravel
composer show cloudinary/cloudinary_php

Older/very new versions may change return shapes or method names — consult the package README on GitHub.

    1. If the facade still resolves to the SDK class, inspect the service container:
php
dd(app()->bound('cloudinary'), app('cloudinary'));

That helps identify what the service provider bound.

    1. As a fallback use the SDK directly (if you want tight control):
php
$cloudinary = new \Cloudinary\Cloudinary(env('CLOUDINARY_URL'));
$response = $cloudinary->uploadApi()->upload($path, ['folder' => 'products']);

(Only use this if you understand the SDK API; the package facade is simpler for most apps.)

See community troubleshooting examples on StackOverflow for similar import/binding mistakes: https://stackoverflow.com/questions/77136480/how-can-i-config-properly-cloudinary-in-laravel-10.


Sources


Conclusion

Fixing this is usually straightforward: make sure Laravel resolves to the package facade (use CloudinaryLabs\CloudinaryLaravel\Facades\Cloudinary), publish and align the cloudinary config with your .env (or set CLOUDINARY_URL), clear caches, and then upload using either Cloudinary::upload(...) or Storage::disk('cloudinary')->putFile(...). Once the facade/binding is correct you’ll stop seeing Call to undefined method Cloudinary\Cloudinary::upload() and your Cloudinary Laravel uploads will return public_id and secure_url you can store or serve.

Authors
Verified by moderation
Moderation
Cloudinary Laravel: Fix Upload Error & Configure