Tutorial, we will learn how to upload multiple files in Laravel 12, store file information in the MySQL database, and use Laravel's logging system with session-based file group naming.
Step 1: Create a Migration for the uploads
Table
Run the migration command:
php artisan make:migration create_uploads_table
Then define your table structure in the generated migration:
// database/migrations/xxxx_xx_xx_create_uploads_table.php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration {
public function up(): void
{
Schema::create('uploads', function (Blueprint $table) {
$table->id();
$table->string('filename');
$table->string('upload_name')->nullable();
$table->timestamp('uploaded_at')->nullable();
$table->timestamps();
});
}
public function down(): void
{
Schema::dropIfExists('uploads');
}
};
Then run:
php artisan migrate
Step 2: Create the Upload Model
php artisan make:model Upload
Edit the model to allow mass assignment:
// app/Models/Upload.php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Upload extends Model
{
protected $fillable = ['filename', 'upload_name', 'uploaded_at'];
}
Step 3: Create the Upload Controller
php artisan make:controller FormController
Step 4: Define Routes
In routes/web.php
:
// ------------------------- Form Upload ----------------------------// Route::controller(FormController::class)->group(function () { Route::middleware('auth')->group(function () { Route::get('form/upload/page', 'index')->name('form.upload.page'); Route::post('form/upload/save', 'storeFileUpload')->name('form.upload.save'); }); });
Step 5: Create the Blade Upload Form
<!-- resources/views/upload.blade.php --> @extends('layouts.master') @section('style') <style> .file-row { display: flex; justify-content: space-between; align-items: center; margin-bottom: 8px; padding-bottom: 6px; border-bottom: 1px solid #eee; } .file-name { flex: 1; color: #444; font-size: 14px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } .icon { display: inline-block; font-size: 1.2em; line-height: 1; margin-right: 0.5em; } .icon.success { color: green; padding-left: 8px; } .icon.remove { color: red; cursor: pointer; } </style> @endsection @section('content') <div class="row"> <div class="col-lg-12 col-md-8 col-sm-12 mx-auto"> <div class="d-flex justify-content-between align-items-center mb-3"> <div class="fw-bold py-2">Page Form</div> <div class="p-2"> Page Form - <a href="{{ route('home') }}" class="text-decoration-none fw-semibold">Dashboard</a> </div> </div> <div class="card p-3 shadow-sm"> <div class="container py-3"> <form id="uploadForm" action="{{ route('form/upload/save') }}" method="POST" enctype="multipart/form-data"> @csrf <div class="mb-3 row"> <label for="fileupload" class="form-label">Upload Files</label> <div class="col-sm-3"> <input type="file" class="form-control" id="fileupload" name="fileupload[]" multiple> </div> </div> <div class="mb-3 row"> <div class="col-12"> <div id="file-list"></div> </div> </div> <div class="mb-3 row"> <div class="col-sm-3"> <button type="submit" class="btn btn-primary">Submit</button> </div> </div> </form> </div> </div> </div> </div> @endsection @section('script') <script> const $input = $('#fileupload'); const $list = $('#file-list'); const files = []; const formatSize = bytes => bytes < 1024 ? `${bytes} B` : `${(bytes / 1024).toFixed(1)} Kb`; $input.on('change', function () { Array.from(this.files).forEach(file => { const exists = files.some(f => f.name === file.name && f.size === file.size); if (!exists) { files.push(file); } }); this.value = null; renderFileList(); }); $list.on('click', '.remove', e => { const index = $(e.target).data('idx'); files.splice(index, 1); renderFileList(); }); function renderFileList() { const dataTransfer = new DataTransfer(); $list.empty(); files.forEach((file, index) => { dataTransfer.items.add(file); $list.append(` <div class="file-row"> <span class="file-name">${file.name}</span> <span class="file-size">${formatSize(file.size)}</span> <span class="icon success">✓</span> <span class="icon remove" data-idx="${index}">✕</span> </div> `); }); $input[0].files = dataTransfer.files; } </script> @endsection
Step 6: Implement the Store Logic
// app/Http/Controllers/FormController.php
<?php
namespace App\Http\Controllers;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Log;
use Illuminate\Http\Request;
use App\Models\Upload;
use Carbon\Carbon;
class FormController extends Controller
{
/**
* Display the file upload form.
*
* @return \Illuminate\View\View
*/
public function index()
{
return view('form.upload');
}
/**
* Handle file upload and save to the database.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\RedirectResponse
*/
public function storeFileUpload(Request $request)
{
// Validate the uploaded files
$request->validate([
'fileupload.*' => 'required|file|mimes:jpg,jpeg,png,pdf,docx|max:2048'
]);
try {
// Check if files are present in the request
if ($request->hasFile('fileupload')) {
foreach ($request->file('fileupload') as $file) {
// Create a unique filename with timestamp to avoid filename conflicts
$filename = time() . '_' . $file->getClientOriginalName();
// Store the file in the 'uploads' directory within the public disk
$path = $file->storeAs('uploads', $filename, 'public');
// Save file info to the database
Upload::create([
'filename' => $filename,
'upload_name' => Auth::user()->name,
'uploaded_at' => Carbon::now()
]);
}
}
return back()->with('success', 'Files uploaded and saved successfully.');
} catch (\Exception $e) {
// Log any exception that occurs during the upload process
Log::error('File upload failed: ' . $e->getMessage());
return back()->with('error', 'Something went wrong while uploading the files.');
}
}
}
Step 7: Create the uploads
Directory
Make sure this directory exists in your project:
mkdir public/uploads
chmod -R 775 public/uploads
Step 8: Test Your Application
-
Visit
/set-upload-name
to set the session upload name. -
Go to
/upload
and upload multiple files. -
Check the a
uploads
table in MySQL to confirm the records. -
Review
storage/logs/laravel.log
for success/error logs.
Conclusion
In this tutorial, you learned how to:
-
Upload multiple files in Laravel 12.
-
Store file names, upload batch name (from session), and timestamps in MySQL.
-
Use
try-catch
blocks for error handling. -
Write logs using Laravel's
Log
facade.