Simple Laravel 12 CRUD Application Tutorial

In this tutorial, I will show you how to create a Laravel 12 CRUD application by developing a complete system with a step-by-step guide.
CRUD stands for Create, Read, Update, and Delete — the four basic operations used in managing data in persistent storage.
I will create a simple products
table and store product details in five columns: code
(VARCHAR), name
(VARCHAR), quantity
(INT), price
(DECIMAL), and description
(TEXT).
The following screenshots show the Laravel 12 CRUD application, which can store, view, update, and delete products from the products
table.
Product List Page

Add New Product Page

Edit Product Page

Show Product Page

Readers Also Read: Laravel 12 Custom User Registration and Login Tutorial
Now, let’s begin building a Laravel 12 CRUD application with a straightforward example using a products
table.
Steps to Create Laravel 12 CRUD Application
Follow the step-by-step guide below to create a Laravel 12 CRUD application.
- Install and Set Up Laravel 12 App
- Create a Model with Migration, Resource Controller and Requests for Validation
- Update Product Migration & Migrate Tables to Database
- Define Product Resource Routes
- Update Code in Product Model
- Update Code in Product Controller
- Update Code in Product Store and Update Requests
- Enable Bootstrap 5 in AppServiceProvider
- Create Layout and Product Resource Blade View Files
- Run Laravel Development Server
Step 1. Install and Set Up Laravel 12 App
If you don’t have Laravel 12 installed, start by creating a new Laravel 12 application named crud_app
using the following command:
composer create-project --prefer-dist laravel/laravel crud_app
Now, navigate to the crud_app directory using the following command.
cd crud_app
By default, Laravel 12 uses SQLite. However, in this tutorial, we will use MySQL.
First, create a database named crud_app
, and then configure your database credentials in the .env
file as shown below:
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=crud_app
DB_USERNAME=your_db_username
DB_PASSWORD=your_db_password
Step 2. Create a Model with Migration, Resource Controller and Requests for Validation
In this step, we need to create a Model, Migration, Resource Controller, and Form Request for validating the products
table.
Although we can create each of them individually, Laravel also allows us to generate them all at once using a single Artisan command.
Simply run the following command in your terminal:
php artisan make:model Product -mcr --requests
In the above command, the -m
flag generates a migration for the model, the -cr
flag creates a resource controller, and the --requests
flag generates custom form request classes for the resource controller.
Step 3. Update Product Migration & Migrate Tables to Database
Now, we need to update the product migration file.
Navigate to the crud_app/database/migrations
directory, where you will find the product migration file named something like:
YYYY_MM_DD_TIMESTAMP_create_products_table.php
Open this file and replace its content with the following code:
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('products', function (Blueprint $table) {
$table->id();
$table->string('code')->unique();
$table->string('name');
$table->integer('quantity');
$table->decimal('price', 8, 2);
$table->text('description')->nullable();
$table->timestamps();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('products');
}
};
Once the product migration file is updated, we need to migrate all tables into our database.
Run the following Artisan command in the terminal to perform the migration:
php artisan migrate
Step 4. Define Product Resource Routes
In this step, we need to define the product resource routes for our application in the web.php
file.
Simply copy and paste the following code into your routes/web.php
file:
<?php
use Illuminate\Support\Facades\Route;
use App\Http\Controllers\ProductController;
Route::get('/', function () {
return view('welcome');
});
Route::resource('products', ProductController::class);
After defining the application routes, you can verify them by running the following Artisan command in the terminal:
php artisan route:list
Step 5. Update Code in Product Model
Now, we need to allow mass assignment in the Product
model.
Navigate to app/Models/Product.php
and update the file with the following code:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Product extends Model
{
protected $fillable = [
'code',
'name',
'quantity',
'price',
'description'
];
}
Step 6. Update Code in Product Controller
In this step, we need to update our ProductController
, which includes seven different methods to perform Laravel Eloquent CRUD operations in our Laravel 12 CRUD application.
By default, a resource controller comes with the following methods, and we will use all of them in our Laravel 12 CRUD app:
index()
– Displays a list of all products from theproducts
table.create()
– Shows the form to add a new product.store()
– Handles the submission of the new product form and stores the data in theproducts
table.show()
– Displays the details of a single product.edit()
– Shows the form to edit an existing product.update()
– Updates the product data in theproducts
table.destroy()
– Deletes a product from the database.
Now, copy and paste the following code into the app/Http/Controllers/ProductController.php
file.
<?php
namespace App\Http\Controllers;
use App\Models\Product;
use App\Http\Requests\StoreProductRequest;
use App\Http\Requests\UpdateProductRequest;
use Illuminate\View\View;
use Illuminate\Http\RedirectResponse;
class ProductController extends Controller
{
/**
* Display a listing of the resource.
*/
public function index() : View
{
return view('products.index', [
'products' => Product::latest()->paginate(3)
]);
}
/**
* Show the form for creating a new resource.
*/
public function create() : View
{
return view('products.create');
}
/**
* Store a newly created resource in storage.
*/
public function store(StoreProductRequest $request) : RedirectResponse
{
Product::create($request->validated());
return redirect()->route('products.index')
->withSuccess('New product is added successfully.');
}
/**
* Display the specified resource.
*/
public function show(Product $product) : View
{
return view('products.show', compact('product'));
}
/**
* Show the form for editing the specified resource.
*/
public function edit(Product $product) : View
{
return view('products.edit', compact('product'));
}
/**
* Update the specified resource in storage.
*/
public function update(UpdateProductRequest $request, Product $product) : RedirectResponse
{
$product->update($request->validated());
return redirect()->back()
->withSuccess('Product is updated successfully.');
}
/**
* Remove the specified resource from storage.
*/
public function destroy(Product $product) : RedirectResponse
{
$product->delete();
return redirect()->route('products.index')
->withSuccess('Product is deleted successfully.');
}
}
Step 7. Update Code in Product Store and Update Requests
In this step, we will update the code in our product store and update request classes.
First, copy and paste the following code into the app/Http/Requests/StoreProductRequest.php
file.
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class StoreProductRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*/
public function authorize(): bool
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* @return array<string, \Illuminate\Contracts\Validation\ValidationRule|array<mixed>|string>
*/
public function rules(): array
{
return [
'code' => 'required|string|max:50|unique:products,code',
'name' => 'required|string|max:250',
'quantity' => 'required|integer|min:1|max:10000',
'price' => 'required',
'description' => 'nullable|string'
];
}
}
After that, copy and paste the following code into the app/Http/Requests/UpdateProductRequest.php
file.
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class UpdateProductRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*/
public function authorize(): bool
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* @return array<string, \Illuminate\Contracts\Validation\ValidationRule|array<mixed>|string>
*/
public function rules(): array
{
return [
'code' => 'required|string|max:50|unique:products,code,'.$this->product->id,
'name' => 'required|string|max:250',
'quantity' => 'required|integer|min:1|max:10000',
'price' => 'required',
'description' => 'nullable|string'
];
}
}
Both of these form request files are responsible for validating the data before adding or updating records in the products
table of our database.
Step 8. Enable Bootstrap 5 in AppServiceProvider
Since we are using Bootstrap v5.3.7 via CDN, some of its features—such as pagination—won’t work properly unless explicitly enabled in the App\Providers\AppServiceProvider.php
file.
To fix this, we need to call Paginator::useBootstrapFive();
inside the boot()
method.
Simply copy and paste the following code into your AppServiceProvider.php
file:
<?php
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
use Illuminate\Pagination\Paginator;
class AppServiceProvider extends ServiceProvider
{
/**
* Register any application services.
*/
public function register(): void
{
//
}
/**
* Bootstrap any application services.
*/
public function boot(): void
{
Paginator::useBootstrapFive();
}
}
Step 9. Create Layout and Product Resource Blade View Files
In this step, we need to create /layouts
and /products
directories inside the resources/views/
directory, and then create the necessary Blade view files within them.
These files can be created manually or by using Artisan commands. In this tutorial, we’ll use Artisan to generate them.
Simply run the following Artisan commands, and all five Blade view files will be created in their respective directories:
php artisan make:view layouts.app
php artisan make:view products.index
php artisan make:view products.create
php artisan make:view products.edit
php artisan make:view products.show
The above commands will generate the following Blade view files:
- app.blade.php
- index.blade.php
- create.blade.php
- edit.blade.php
- show.blade.php
Now, we need to update each of these view files with the appropriate code.
Starting with app.blade.php
, which serves as the main layout view file for our Laravel 12 CRUD application:
Copy and paste the following code into resources/views/layouts/app.blade.php
:
<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Simple Laravel 12 CRUD Application Tutorial - AllPHPTricks.com</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" crossorigin="anonymous">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/font/bootstrap-icons.css" crossorigin="anonymous">
</head>
<body>
<div class="container">
<h3 class=" mt-3">Simple Laravel 12 CRUD Application Tutorial - <a href="https://www.allphptricks.com/">AllPHPTricks.com</a></h3>
@yield('content')
<div class="row justify-content-center text-center mt-3">
<div class="col-md-12">
<p>Back to Tutorial:
<a href="https://www.allphptricks.com/simple-laravel-12-crud-application-tutorial/"><strong>Tutorial Link</strong></a>
</p>
<p>
For More Web Development Tutorials Visit: <a href="https://www.allphptricks.com/"><strong>AllPHPTricks.com</strong></a>
</p>
</div>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js" crossorigin="anonymous"></script>
</body>
</html>
index.blade.php
is the main landing page of the Laravel 12 CRUD application. It displays a list of all products from the database along with pagination.
Copy and paste the following code into the resources/views/products/index.blade.php
file:
@extends('layouts.app')
@section('content')
<div class="row justify-content-center mt-3">
<div class="col-md-12">
@session('success')
<div class="alert alert-success" role="alert">
{{ $value }}
</div>
@endsession
<div class="card">
<div class="card-header">Product List</div>
<div class="card-body">
<a href="{{ route('products.create') }}" class="btn btn-success btn-sm my-2"><i class="bi bi-plus-circle"></i> Add New Product</a>
<table class="table table-striped table-bordered">
<thead>
<tr>
<th scope="col">S#</th>
<th scope="col">Code</th>
<th scope="col">Name</th>
<th scope="col">Quantity</th>
<th scope="col">Price</th>
<th scope="col">Action</th>
</tr>
</thead>
<tbody>
@forelse ($products as $product)
<tr>
<th scope="row">{{ $loop->iteration }}</th>
<td>{{ $product->code }}</td>
<td>{{ $product->name }}</td>
<td>{{ $product->quantity }}</td>
<td>{{ $product->price }}</td>
<td>
<form action="{{ route('products.destroy', $product->id) }}" method="post">
@csrf
@method('DELETE')
<a href="{{ route('products.show', $product->id) }}" class="btn btn-warning btn-sm"><i class="bi bi-eye"></i> Show</a>
<a href="{{ route('products.edit', $product->id) }}" class="btn btn-primary btn-sm"><i class="bi bi-pencil-square"></i> Edit</a>
<button type="submit" class="btn btn-danger btn-sm" onclick="return confirm('Do you want to delete this product?');"><i class="bi bi-trash"></i> Delete</button>
</form>
</td>
</tr>
@empty
<td colspan="6">
<span class="text-danger">
<strong>No Product Found!</strong>
</span>
</td>
@endforelse
</tbody>
</table>
{{ $products->links() }}
</div>
</div>
</div>
</div>
@endsection
create.blade.php
is the Blade view file used to add a new product.
Simply copy and paste the following code into resources/views/products/create.blade.php
:
@extends('layouts.app')
@section('content')
<div class="row justify-content-center mt-3">
<div class="col-md-8">
<div class="card">
<div class="card-header">
<div class="float-start">
Add New Product
</div>
<div class="float-end">
<a href="{{ route('products.index') }}" class="btn btn-primary btn-sm">← Back</a>
</div>
</div>
<div class="card-body">
<form action="{{ route('products.store') }}" method="post">
@csrf
<div class="mb-3 row">
<label for="code" class="col-md-4 col-form-label text-md-end text-start">Code</label>
<div class="col-md-6">
<input type="text" class="form-control @error('code') is-invalid @enderror" id="code" name="code" value="{{ old('code') }}">
@error('code')
<span class="text-danger">{{ $message }}</span>
@enderror
</div>
</div>
<div class="mb-3 row">
<label for="name" class="col-md-4 col-form-label text-md-end text-start">Name</label>
<div class="col-md-6">
<input type="text" class="form-control @error('name') is-invalid @enderror" id="name" name="name" value="{{ old('name') }}">
@error('name')
<span class="text-danger">{{ $message }}</span>
@enderror
</div>
</div>
<div class="mb-3 row">
<label for="quantity" class="col-md-4 col-form-label text-md-end text-start">Quantity</label>
<div class="col-md-6">
<input type="number" class="form-control @error('quantity') is-invalid @enderror" id="quantity" name="quantity" value="{{ old('quantity') }}">
@error('quantity')
<span class="text-danger">{{ $message }}</span>
@enderror
</div>
</div>
<div class="mb-3 row">
<label for="price" class="col-md-4 col-form-label text-md-end text-start">Price</label>
<div class="col-md-6">
<input type="number" step="0.01" class="form-control @error('price') is-invalid @enderror" id="price" name="price" value="{{ old('price') }}">
@error('price')
<span class="text-danger">{{ $message }}</span>
@enderror
</div>
</div>
<div class="mb-3 row">
<label for="description" class="col-md-4 col-form-label text-md-end text-start">Description</label>
<div class="col-md-6">
<textarea class="form-control @error('description') is-invalid @enderror" id="description" name="description">{{ old('description') }}</textarea>
@error('description')
<span class="text-danger">{{ $message }}</span>
@enderror
</div>
</div>
<div class="mb-3 row">
<input type="submit" class="col-md-3 offset-md-5 btn btn-primary" value="Add Product">
</div>
</form>
</div>
</div>
</div>
</div>
@endsection
edit.blade.php
is the Blade view file used for editing a product.
Simply copy and paste the following code into resources/views/products/edit.blade.php
:
@extends('layouts.app')
@section('content')
<div class="row justify-content-center mt-3">
<div class="col-md-8">
@session('success')
<div class="alert alert-success" role="alert">
{{ $value }}
</div>
@endsession
<div class="card">
<div class="card-header">
<div class="float-start">
Edit Product
</div>
<div class="float-end">
<a href="{{ route('products.index') }}" class="btn btn-primary btn-sm">← Back</a>
</div>
</div>
<div class="card-body">
<form action="{{ route('products.update', $product->id) }}" method="post">
@csrf
@method("PUT")
<div class="mb-3 row">
<label for="code" class="col-md-4 col-form-label text-md-end text-start">Code</label>
<div class="col-md-6">
<input type="text" class="form-control @error('code') is-invalid @enderror" id="code" name="code" value="{{ $product->code }}">
@error('code')
<span class="text-danger">{{ $message }}</span>
@enderror
</div>
</div>
<div class="mb-3 row">
<label for="name" class="col-md-4 col-form-label text-md-end text-start">Name</label>
<div class="col-md-6">
<input type="text" class="form-control @error('name') is-invalid @enderror" id="name" name="name" value="{{ $product->name }}">
@error('name')
<span class="text-danger">{{ $message }}</span>
@enderror
</div>
</div>
<div class="mb-3 row">
<label for="quantity" class="col-md-4 col-form-label text-md-end text-start">Quantity</label>
<div class="col-md-6">
<input type="number" class="form-control @error('quantity') is-invalid @enderror" id="quantity" name="quantity" value="{{ $product->quantity }}">
@error('quantity')
<span class="text-danger">{{ $message }}</span>
@enderror
</div>
</div>
<div class="mb-3 row">
<label for="price" class="col-md-4 col-form-label text-md-end text-start">Price</label>
<div class="col-md-6">
<input type="number" step="0.01" class="form-control @error('price') is-invalid @enderror" id="price" name="price" value="{{ $product->price }}">
@error('price')
<span class="text-danger">{{ $message }}</span>
@enderror
</div>
</div>
<div class="mb-3 row">
<label for="description" class="col-md-4 col-form-label text-md-end text-start">Description</label>
<div class="col-md-6">
<textarea class="form-control @error('description') is-invalid @enderror" id="description" name="description">{{ $product->description }}</textarea>
@error('description')
<span class="text-danger">{{ $message }}</span>
@enderror
</div>
</div>
<div class="mb-3 row">
<input type="submit" class="col-md-3 offset-md-5 btn btn-primary" value="Update">
</div>
</form>
</div>
</div>
</div>
</div>
@endsection
show.blade.php
is the Blade view file used to display a single product’s details.
Simply copy and paste the following code into resources/views/products/show.blade.php
:
@extends('layouts.app')
@section('content')
<div class="row justify-content-center mt-3">
<div class="col-md-8">
<div class="card">
<div class="card-header">
<div class="float-start">
Product Information
</div>
<div class="float-end">
<a href="{{ route('products.index') }}" class="btn btn-primary btn-sm">← Back</a>
</div>
</div>
<div class="card-body">
<div class="row">
<label for="code" class="col-md-4 col-form-label text-md-end text-start"><strong>Code:</strong></label>
<div class="col-md-6" style="line-height: 35px;">
{{ $product->code }}
</div>
</div>
<div class="row">
<label for="name" class="col-md-4 col-form-label text-md-end text-start"><strong>Name:</strong></label>
<div class="col-md-6" style="line-height: 35px;">
{{ $product->name }}
</div>
</div>
<div class="row">
<label for="quantity" class="col-md-4 col-form-label text-md-end text-start"><strong>Quantity:</strong></label>
<div class="col-md-6" style="line-height: 35px;">
{{ $product->quantity }}
</div>
</div>
<div class="row">
<label for="price" class="col-md-4 col-form-label text-md-end text-start"><strong>Price:</strong></label>
<div class="col-md-6" style="line-height: 35px;">
{{ $product->price }}
</div>
</div>
<div class="row">
<label for="description" class="col-md-4 col-form-label text-md-end text-start"><strong>Description:</strong></label>
<div class="col-md-6" style="line-height: 35px;">
{{ $product->description }}
</div>
</div>
</div>
</div>
</div>
</div>
@endsection
Step 10. Run Laravel Development Server
Finally, we’ve completed all the steps of our Laravel 12 CRUD application tutorial. Now it’s time to test the application.
Start the Laravel development server by running the following Artisan command:
php artisan serve
After starting the development server, visit the following URL to test your Laravel 12 CRUD application:
http://127.0.0.1:8000/products
Conclusion
We hope that by following the steps above, you have learned how to easily create a simple Laravel 12 CRUD application.
If you found this tutorial helpful, please share it with your friends and developer groups.
I spent several hours creating this tutorial. If you’d like to say thanks, consider liking my pages on Facebook, Twitter, and GitHub — and don’t forget to share it!
Facebook Official Page: All PHP Tricks
Twitter Official Page: All PHP Tricks