Laravel 12 Tutorial: File Listing and Delete Functionality
In this tutorial, we’ll walk through building a robust file upload and management system using Laravel, including:
-
Uploading multiple files
-
Storing metadata in the database
-
Listing files using DataTables
-
Supporting download and delete actions
-
Enforcing permissions based on user roles
Route Definitions
Define your upload-related routes in web.php
using a Controller group:
// ------------------------- Form Upload ----------------------------//
Route::controller(FormController::class)->group(function () {
Route::middleware('auth')->group(function () {
Route::get('form/upload/page', 'index')->name('form/upload/page'); // Show upload form
Route::post('form/upload/save', 'storeFileUpload')->name('form/upload/save'); // Handle upload
Route::get('form/upload/listing', 'showFiles')->name('form/upload/listing'); // Show uploaded files
Route::get('form/file/listing', 'getData')->name('get-data-file.listing'); // DataTables JSON
Route::get('/download/{filename}', 'download')->name('file.download'); // Download a file
Route::post('delete-file', 'destroy')->name('delete-file'); // Delete a file
});
});
File Listing Blade View
Inside resources/views/form/listing.blade.php
:
@extends('layouts.master') @section('content') <div class="row"> <div class="col-lg-12 mx-auto"> <div class="d-flex justify-content-between mb-3"> <div class="fw-bold">File Listing</div> <div> <a href="{{ route('home') }}" class="fw-semibold text-decoration-none">Dashboard</a> </div> </div> <div class="card p-3 shadow-sm"> <div class="table-responsive"> <table id="fileListing" class="table table-striped nowrap w-100"> <thead> <tr> <th>Actions</th> <th>No</th> <th>File Name</th> <th>Upload Name</th> <th>Date Uploaded</th> </tr> </thead> <tbody></tbody> </table> </div> </div> </div> </div> <!-- Delete Modal --> <div class="modal fade" id="modalDeleteFile" tabindex="-1"> <div class="modal-dialog modal-dialog-centered"> <div class="modal-content"> <form action="{{ route('delete-file') }}" method="POST"> @csrf <div class="modal-header"> <h5 class="modal-title">Delete File</h5> <button type="button" class="btn-close" data-bs-dismiss="modal"></button> </div> <div class="modal-body text-center"> Are you sure you want to delete <strong id="d_name" class="text-danger"></strong>? </div> <input type="hidden" id="d_filename" name="filename"> <div class="modal-footer justify-content-center"> <button type="button" class="btn btn-outline-primary" data-bs-dismiss="modal">Cancel</button> <button type="submit" class="btn btn-outline-danger">Delete</button> </div> </form> </div> </div> </div> @endsection @section('script') <script> $(function() { const table = $('#fileListing').DataTable({ lengthMenu: [[10, 25, 50, 100], [10, 25, 50, 100]], buttons: ['pageLength'], pageLength: 10, order: [[4, 'desc']], scrollX: true, processing: true, serverSide: true, ajax: "{{ route('get-data-file.listing') }}", columns: [ { data: 'action', orderable: false, searchable: false }, { data: 'id' }, { data: 'filename' }, { data: 'upload_name' }, { data: 'uploaded_at' }, ] }); $(document).on('click', '.fileDelete', function() { const filename = $(this).data('filename'); $('#d_filename').val(filename); $('#d_name').text(filename); $('#modalDeleteFile').modal('show'); }); }); </script> @endsection
Controller Logic
Your FormController.php
handles all the backend logic:
namespace App\Http\Controllers;
use Illuminate\Support\Facades\{Storage, Auth, Log};
use Illuminate\Http\Request;
use App\Models\Upload;
use Carbon\Carbon;
use Exception;
class FormController extends Controller
{
public function showFiles()
{
return view('form.fileslisting');
}
public function getData(Request $request)
{
$query = Upload::query();
$total = $query->count();
$search = $request->get('search')['value'];
if ($search) {
$query->where(function($q) use ($search) {
$q->orWhere('filename', 'like', "%$search%")
->orWhere('upload_name', 'like', "%$search%")
->orWhere('uploaded_at', 'like', "%$search%");
});
}
$filtered = $query->count();
$records = $query->orderBy(
$request->get('columns')[$request->get('order')[0]['column']]['data'],
$request->get('order')[0]['dir']
)
->skip($request->get('start'))
->take($request->get('length'))
->get();
$userRole = Auth::user()->role_name;
$permissions = config('roles')[$userRole] ?? [];
$data = $records->map(function ($item, $index) use ($request, $permissions) {
return [
'action' => in_array('delete', $permissions)
? '<button class="btn btn-danger btn-sm fileDelete" data-filename="'.$item->filename.'"><i class="bi bi-trash"></i></button>'
: '',
'id' => $request->get('start') + $index + 1,
'filename' => '<a href="'.route('file.download', $item->filename).'" class="filename" data-filename="'.$item->filename.'">'.$item->filename.'</a>',
'upload_name' => $item->upload_name,
'uploaded_at' => $item->uploaded_at,
];
});
return response()->json([
"draw" => intval($request->get('draw')),
"iTotalRecords" => $total,
"iTotalDisplayRecords" => $filtered,
"aaData" => $data
]);
}
public function download($filename)
{
$path = "uploads/$filename";
if (!Storage::disk('public')->exists($path)) {
abort(404);
}
return Storage::disk('public')->download($path);
}
public function destroy(Request $request)
{
try {
$file = Upload::where('filename', $request->filename)->firstOrFail();
Storage::disk('public')->delete('uploads/' . $file->filename);
$file->delete();
return back()->with('success', 'File deleted successfully.');
} catch (Exception $e) {
Log::error('Delete error: '.$e->getMessage());
return back()->with('error', 'Failed to delete file.');
}
}
}
Upload Model Example
Ensure you have the Upload
model like so:
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Upload extends Model
{
protected $fillable = [
'filename', 'upload_name', 'uploaded_at'
];
public $timestamps = false;
}
Final Notes
-
Role permissions are assumed to be configured in
config/roles.php
-
Bootstrap 5 and jQuery DataTables are used for UI
-
File validation ensures safe formats and size limits
Demo Features
Feature | Description |
---|---|
Upload | Multiple file upload with validation |
Listing | Paginated & searchable DataTable |
Delete | Soft-confirmation via modal |
Download | Direct secure download from public disk |
Permissions | Role-based button visibility |