Laravel: How to create a folder in public path dynamically when a file is uploaded?

Stefan Bogdanescu

Founder & Senior Architect · 2026-06-29

Laravel Company

Laravel: How to Create Folders in the Public Path Dynamically When Uploading Files

As developers working with web applications, file management is a constant challenge. Often, we store user-uploaded content, but the requirement isn't just to save the file; it’s to organize it logically based on some external identifier—like a reference ID stored in a database. Today, we are diving into a practical scenario within Laravel: how to dynamically create nested directories for uploaded files based on data fetched from the database.

The challenge is simple yet common: if you simply use $file->move('public/uploads/release', $name), all files end up in one flat directory. We need a structure like public/uploads/release/{reference_id}/filename. This requires dynamic folder creation before the file move operation.

This guide will walk you through the most robust and idiomatic ways to achieve this dynamic folder creation in your Laravel application, focusing on best practices for file system interaction.

The Problem with Static Paths

Your current approach relies on hardcoding the destination path:

$file->move('uploads/release', $name);

This method is fine for simple uploads, but it fails when you need to create a unique subdirectory for every record (e.g., ABC123). If the folder ABC123 does not exist, the move operation will likely fail or overwrite existing structures, leading to messy file systems and poor data integrity—a scenario we want to avoid in any scalable application.

Solution 1: Manual Directory Creation (The PHP Way)

The most direct way to solve this is to construct the full path and explicitly ensure that all necessary parent directories exist before attempting the file move. We achieve this using PHP’s built-in mkdir() function with the recursive option set to true.

In your Controller, assuming you have $reference from the request:

use Illuminate\Http\Request;
use Illuminate\Support\Facades\Storage; // Always good to use facades!

class FileUploadController extends Controller
{
    public function upload(Request $request)
    {
        $reference = $request->input('reference');
        if (!$reference) {
            return response()->json(['error' => 'Reference ID is required.'], 400);
        }

        if ($request->hasFile('uploads')) {
            foreach ($request->file('uploads') as $file) {
                $name = $file->getClientOriginalName();
                
                // 1. Construct the dynamic path
                $directoryPath = 'public/uploads/release/' . $reference;

                // 2. Create the directory dynamically (recursive: true creates all parent folders)
                if (!Storage::exists($directoryPath)) {
                    // Note: Using Storage::makeDirectory is often better than raw mkdir() in Laravel context, 
                    // but for direct system interaction, we can use the filesystem helper:
                    if (!mkdir($directoryPath, 0755, true)) {
                        throw new \Exception("Failed to create directory: " . $directoryPath);
                    }
                }

                // 3. Move the file into the newly created dynamic folder
                $file->move($directoryPath, $name);
            }
        }
    }
}

While this approach works, manually managing file system operations can become cumbersome and error-prone as your application scales. For complex storage needs, relying on Laravel’s dedicated Storage facade is significantly more powerful and cleaner.

Solution 2: Leveraging Laravel Storage (The Best Practice)

Laravel provides the Illuminate\Support\Facades\Storage class, which abstracts away the underlying filesystem operations. Instead of manually calling mkdir() and then using $file->move(), we can use the storage methods to handle this interaction seamlessly.

For storing files in a specific path structure like this, where you want to organize files under a logical folder named by a dynamic value, the Storage facade is ideal. You can use the disk configuration or simply construct the path relative to your configured disk (usually public).

use Illuminate\Http\Request;
use Illuminate\Support\Facades\Storage;

class FileUploadController extends Controller
{
    public function upload(Request $request)
    {
        $reference = $request->input('reference');

        if ($request->hasFile('uploads')) {
            foreach ($request->file('uploads') as $file) {
                $name = $file->getClientOriginalName();
                
                // Define the dynamic folder path using the Storage facade syntax.
                // The 'release' folder is static, and {$reference} is dynamic.
                $dynamicPath = "uploads/release/{$reference}/";

                // Ensure the directory structure exists before writing the file.
                // makeDirectory ensures all parent directories are created recursively.
                Storage::makeDirectory($dynamicPath);

                // Store the file on the disk (public) using the dynamic path
                $file->move($dynamicPath, $name); 
            }
        }
    }
}

Why the Storage Facade Wins

Using Storage::makeDirectory() and subsequent file operations provides several benefits:

  1. Abstraction: It abstracts away platform-specific differences, making your code portable.
  2. Error Handling: Laravel’s storage methods often integrate better with its error handling mechanisms.
  3. Clarity: It clearly communicates the intent—you are interacting with a file system managed by Laravel, which aligns perfectly with modern application architecture principles like those promoted by Laravel Company.

Conclusion

To dynamically create folders based on database references during file uploads in Laravel, the key is to prioritize robust directory creation before moving the files. While manually managing mkdir() works, leveraging the built-in Storage facade—specifically using methods like makeDirectory()—is the superior, more maintainable, and scalable approach. By adopting these patterns, you ensure that your application remains clean, efficient, and adheres to modern Laravel development standards.