Laravel Laravel 12

Laravel 12 CRUD Operations: List, Update, and Delete with Image Upload

Laravel 12 Users Profile CRUD (With Image Upload)

A full step-by-step guide using Laravel, Bootstrap 5, and image handling — styled with your custom class system.

1. Create Laravel Project

composer create-project laravel/laravel users-profile-app
cd users-profile-app

2. Set Up Model & Migration

php artisan make:model Profile -m

Edit the migration file to add the necessary fields:

📂 Migration File: database/migrations/xxxx_xx_xx_create_profiles_table.php

public function up(): void
{
    Schema::create('profiles', function (Blueprint $table) {
        $table->id();
        $table->string('first_name');
        $table->string('last_name');
        $table->string('email')->unique();
        $table->string('password');
        $table->string('image')->nullable(); // store image path
        $table->timestamps();
    });
}

Run the migration using the following command:

php artisan migrate

📄 Model File: app/Models/Profile.php

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable;

class Profile extends Authenticatable
{
    use HasFactory;

    protected $fillable = [
        'first_name',
        'last_name',
        'email',
        'password',
        'image',
    ];

    protected $hidden = [
        'password',
    ];

    // Automatically hash password
    public function setPasswordAttribute($value)
    {
        $this->attributes['password'] = bcrypt($value);
    }
}

3. Run Migration

php artisan migrate

4. Controller with Image Upload

php artisan make:controller UsersProfileController

Implement functions: index, create, store, edit, update, destroy

5. Define Web Routes


Route::get('users', [UsersProfileController::class, 'index']);
Route::get('users/create', [UsersProfileController::class, 'create']);
Route::post('users', [UsersProfileController::class, 'store']);
Route::get('users/{id}/edit', [UsersProfileController::class, 'edit']);
Route::put('users/{id}', [UsersProfileController::class, 'update']);
Route::get('users/{id}', [UsersProfileController::class, 'show']);
Route::delete('users/{id}', [UsersProfileController::class, 'destroy']);

10. Full Controller Code


namespace App\Http\Controllers;

use Illuminate\Http\Request;
use App\Models\Profile;
use Illuminate\Support\Facades\File;

class UsersProfileController extends Controller
{
    public function index()
    {
        $profiles = Profile::all();
        return view('users.index', compact('profiles'));
    }

    public function create()
    {
        return view('users.create');
    }

    public function store(Request $request)
    {
        $request->validate([
            'first_name' => 'required|string|max:100',
            'last_name' => 'required|string|max:100',
            'email' => 'required|email|unique:profiles,email',
            'password' => 'required|min:6',
            'image' => 'nullable|image|mimes:jpeg,png,jpg|max:2048',
        ]);

        $imagePath = null;

        if ($request->hasFile('image')) {
            $fileName = time() . '_' . $request->file('image')->getClientOriginalName();
            $request->file('image')->move(public_path('images/profiles'), $fileName);
            $imagePath = 'images/profiles/' . $fileName;
        }

        Profile::create([
            'first_name' => $request->first_name,
            'last_name'  => $request->last_name,
            'email'      => $request->email,
            'password'   => bcrypt($request->password),
            'image'      => $imagePath,
        ]);

        return redirect()->route('users.index')->with('success', 'Profile created.');
    }

    public function edit($id)
    {
        $profile = Profile::findOrFail($id);
        return view('users.edit', compact('profile'));
    }

    public function update(Request $request, $id)
    {
        $profile = Profile::findOrFail($id);

        $request->validate([
            'first_name' => 'required|string|max:100',
            'last_name'  => 'required|string|max:100',
            'email'      => 'required|email|unique:profiles,email,' . $id,
            'password'   => 'nullable|min:6',
            'image'      => 'nullable|image|mimes:jpeg,png,jpg|max:2048',
        ]);

        if ($request->hasFile('image')) {
            if ($profile->image && File::exists(public_path($profile->image))) {
                File::delete(public_path($profile->image));
            }

            $fileName = time() . '_' . $request->file('image')->getClientOriginalName();
            $request->file('image')->move(public_path('images/profiles'), $fileName);
            $profile->image = 'images/profiles/' . $fileName;
        }

        $profile->first_name = $request->first_name;
        $profile->last_name = $request->last_name;
        $profile->email = $request->email;

        if ($request->filled('password')) {
            $profile->password = bcrypt($request->password);
        }

        $profile->save();

        return redirect()->route('users.index')->with('success', 'Profile updated.');
    }

    public function destroy($id)
    {
        $profile = Profile::findOrFail($id);

        if ($profile->image && File::exists(public_path($profile->image))) {
            File::delete(public_path($profile->image));
        }

        $profile->delete();

        return redirect()->route('users.index')->with('success', 'Profile deleted.');
    }

    public function show($id)
    {
        $profile = Profile::findOrFail($id);
        return view('users.show', compact('profile'));
    }
}

6. Blade Templates

  • layouts/app.blade.php — Layout with Bootstrap 5 + custom styles
// File: resources/views/layouts/app.blade.php

<!DOCTYPE html>
<html>
<head>
    <title>@yield('title', 'Users Profile')</title>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">

    <!-- Bootstrap 5 CDN -->
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">

    <style>
        .profile-image {
            width: 60px;
            height: 60px;
            object-fit: cover;
            border-radius: 50%;
        }
    </style>
</head>
<body>
    <div class="container py-4">
        <h2 class="mb-4 text-center">@yield('title', 'Users Profile')</h2>

        @if(session('success'))
            <div class="alert alert-success">{{ session('success') }}</div>
        @endif

        @yield('content')
    </div>

    <!-- Bootstrap JS -->
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>
  • users/index.blade.php — List profiles

// File: resources/views/users/index.blade.php

<@extends('layouts.app')>

<@section('title', 'All User Profiles')>

<@section('content')>
    <div class="mb-3 text-end">
        <a href="{{ route('users.create') }}" class="btn btn-primary">+ Create New Profile</a>
    </div>

    <table class="table table-bordered table-hover">
        <thead class="table-light">
            <tr>
                <th>#</th>
                <th>Image</th>
                <th>Full Name</th>
                <th>Email</th>
                <th style="width: 150px;">Actions</th>
            </tr>
        </thead>
        <tbody>
            @forelse($profiles as $index => $profile)
                <tr>
                    <td>{{ $index + 1 }}</td>
                    <td>
                        @if($profile->image)
                            <img src="{{ asset($profile->image) }}" alt="image" class="profile-image">
                        @else
                            <span class="text-muted">N/A</span>
                        @endif
                    </td>
                    <td>{{ $profile->first_name }} {{ $profile->last_name }}</td>
                    <td>{{ $profile->email }}</td>
                    <td>
                        <a href="{{ route('users.edit', $profile->id) }}" class="btn btn-sm btn-warning">Edit</a>
                        <form action="{{ route('users.destroy', $profile->id) }}" method="POST" class="d-inline" onsubmit="return confirm('Delete this profile?')">
                            @csrf
                            @method('DELETE')
                            <button class="btn btn-sm btn-danger">Del</button>
                        </form>
                    </td>
                </tr>
            @empty
                <tr><td colspan="5" class="text-center text-muted">No profiles found.</td></tr>
            @endforelse
        </tbody>
    </table>
<@endsection>
  • users/create.blade.php — Add form

 


// File: resources/views/users/create.blade.php

<@extends('layouts.app')>

<@section('title', 'Create User Profile')>

<@section('content')>
<div class="container mt-4">
    <h2 class="mb-4">Create User Profile</h2>

    <form action="{{ route('users.store') }}" method="POST" enctype="multipart/form-data" class="card p-4 shadow-sm">
        @csrf

        <div class="mb-3">
            <label class="form-label">First Name</label>
            <input type="text" name="first_name" class="form-control" value="{{ old('first_name') }}" required>
            @error('first_name') <small class="text-danger">{{ $message }}</small> @enderror
        </div>

        <div class="mb-3">
            <label class="form-label">Last Name</label>
            <input type="text" name="last_name" class="form-control" value="{{ old('last_name') }}" required>
            @error('last_name') <small class="text-danger">{{ $message }}</small> @enderror
        </div>

        <div class="mb-3">
            <label class="form-label">Email</label>
            <input type="email" name="email" class="form-control" value="{{ old('email') }}" required>
            @error('email') <small class="text-danger">{{ $message }}</small> @enderror
        </div>

        <div class="mb-3">
            <label class="form-label">Password</label>
            <input type="password" name="password" class="form-control" required>
            @error('password') <small class="text-danger">{{ $message }}</small> @enderror
        </div>

        <div class="mb-3">
            <label class="form-label">Profile Image</label>
            <input type="file" name="image" class="form-control">
            @error('image') <small class="text-danger">{{ $message }}</small> @enderror
        </div>

        <div class="text-end">
            <button type="submit" class="btn btn-primary">Create Profile</button>
        </div>
    </form>
</div>
<@endsection>
  • users/edit.blade.php — Edit with image preview

 


// File: resources/views/users/edit.blade.php

<@extends('layouts.app')>

<@section('title', 'Edit User Profile')>

<@section('content')>
    <form action="{{ route('users.update', $profile->id) }}" method="POST" enctype="multipart/form-data" class="card p-4 shadow-sm">
        @csrf
        @method('PUT')

        <div class="mb-3">
            <label class="form-label">First Name</label>
            <input type="text" name="first_name" class="form-control" value="{{ old('first_name', $profile->first_name) }}" required>
            @error('first_name') <small class="text-danger">{{ $message }}</small> @enderror
        </div>

        <div class="mb-3">
            <label class="form-label">Last Name</label>
            <input type="text" name="last_name" class="form-control" value="{{ old('last_name', $profile->last_name) }}" required>
            @error('last_name') <small class="text-danger">{{ $message }}</small> @enderror
        </div>

        <div class="mb-3">
            <label class="form-label">Email</label>
            <input type="email" name="email" class="form-control" value="{{ old('email', $profile->email) }}" required>
            @error('email') <small class="text-danger">{{ $message }}</small> @enderror
        </div>

        <div class="mb-3">
            <label class="form-label">New Password <small class="text-muted">(leave blank to keep existing)</small></label>
            <input type="password" name="password" class="form-control">
            @error('password') <small class="text-danger">{{ $message }}</small> @enderror
        </div>

        <div class="mb-3">
            <label class="form-label">Profile Image</label><br>
            @if ($profile->image)
                <img src="{{ asset($profile->image) }}" class="profile-image mb-2" alt="Profile Image"><br>
            @endif
            <input type="file" name="image" class="form-control">
            @error('image') <small class="text-danger">{{ $message }}</small> @enderror
        </div>

        <div class="text-end">
            <a href="{{ route('users.index') }}" class="btn btn-secondary">Back</a>
            <button type="submit" class="btn btn-primary">Update</button>
        </div>
    </form>
<@endsection>

 

users/show.blade.php — Profile detai


// File: resources/views/users/show.blade.php

<@extends('layouts.app')>

<@section('title', 'User Profile Details')>

<@section('content')>
    <div class="card shadow-sm p-4">
        <div class="row">
            <div class="col-md-3 text-center">
                @if($profile->image)
                    <img src="{{ asset($profile->image) }}" alt="Profile Image" class="img-fluid rounded-circle mb-2" style="max-width: 150px;">
                @else
                    <div class="text-muted">No Image</div>
                @endif
            </div>

            <div class="col-md-9">
                <h4>{{ $profile->first_name }} {{ $profile->last_name }}</h4>
                <p><strong>Email:</strong> {{ $profile->email }}</p>
                <p><strong>Created At:</strong> {{ $profile->created_at->format('d M Y, h:i A') }}</p>
                <p><strong>Updated At:</strong> {{ $profile->updated_at->format('d M Y, h:i A') }}</p>

                <div class="mt-3">
                    <a href="{{ route('users.index') }}" class="btn btn-secondary">Back</a>
                    <a href="{{ route('users.edit', $profile->id) }}" class="btn btn-warning">Edit</a>
                    <form action="{{ route('users.destroy', $profile->id) }}" method="POST" class="d-inline" onsubmit="return confirm('Delete this profile?')">
                        @csrf
                        @method('DELETE')
                        <button class="btn btn-danger">Delete</button>
                    </form>
                </div>
            </div>
        </div>
    </div>
<@endsection>

7. Image Upload Folder

Create this folder inside public/ and make it writable:

public/images/profiles

8. Launch the App

php artisan serve

Visit: http://localhost:8000/users

Leave a Reply

Your email address will not be published. Required fields are marked *