Simple CRUD tutorial in Laravel 13

Below is a simple CRUD tutorial in Laravel 13 using a Product example with Blade views. This is a classic Resource Controller + Blade Templates CRUD application. Laravel follows the MVC (Model-View-Controller) pattern:

  • Routes → Connect URLs to controller methods
  • Model (Product) → Handles database interaction
  • Controller (ProductController) → Handles logic and HTTP requests
  • Views (Blade templates) → Handle the user interface

1. Create Laravel Project

composer create-project laravel/laravel crud-app
cd crud-app

Configure your database in .env:

DB_DATABASE=crud_app
DB_USERNAME=root
DB_PASSWORD=

Then run:

php artisan migrate

2. Create Model, Migration, and Controller

php artisan make:model Product -m
php artisan make:controller ProductController --resource

3. Create Products Table

Open the migration file in:

database/migrations/xxxx_xx_xx_create_products_table.php

Update it:

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('products', function (Blueprint $table) {
            $table->id();
            $table->string('name');
            $table->text('description')->nullable();
            $table->decimal('price', 10, 2);
            $table->integer('stock')->default(0);
            $table->timestamps();
        });
    }

    public function down(): void
    {
        Schema::dropIfExists('products');
    }
};

Run migration:

php artisan migrate

4. Update Product Model

Open:

app/Models/Product.php

Add fillable fields:

namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class Product extends Model
{
    protected $fillable = [
        'name',
        'description',
        'price',
        'stock',
    ];
}

5. Add Resource Route

Open:

routes/web.php

Add:

use Illuminate\Support\Facades\Route;
use App\Http\Controllers\ProductController;

Route::get('/', function () {
    return redirect()->route('products.index');
});

Route::resource('products', ProductController::class);

This single line “Route::resource(‘products’, ProductController::class);” automatically creates 7 RESTful routes:

HTTP MethodURLController MethodPurpose
GET/productsindex()List all
GET/products/createcreate()Show create form
POST/productsstore()Save new product
GET/products/{product}show()View one
GET/products/{product}/editedit()Show edit form
PUT/PATCH/products/{product}update()Update product
DELETE/products/{product}destroy()Delete product

There’s also a root redirect to the products index.


6. Create CRUD Controller Logic

Open:

app/Http/Controllers/ProductController.php

Replace with:

namespace App\Http\Controllers;

use App\Models\Product;
use Illuminate\Http\Request;

class ProductController extends Controller
{
    public function index()
    {
        $products = Product::latest()->paginate(10);

        return view('products.index', compact('products'));
    }

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

    public function store(Request $request)
    {
        $request->validate([
            'name' => 'required|string|max:255',
            'description' => 'nullable|string',
            'price' => 'required|numeric|min:0',
            'stock' => 'required|integer|min:0',
        ]);

        Product::create($request->all());

        return redirect()
            ->route('products.index')
            ->with('success', 'Product created successfully.');
    }

    public function show(Product $product)
    {
        return view('products.show', compact('product'));
    }

    public function edit(Product $product)
    {
        return view('products.edit', compact('product'));
    }

    public function update(Request $request, Product $product)
    {
        $request->validate([
            'name' => 'required|string|max:255',
            'description' => 'nullable|string',
            'price' => 'required|numeric|min:0',
            'stock' => 'required|integer|min:0',
        ]);

        $product->update($request->all());

        return redirect()
            ->route('products.index')
            ->with('success', 'Product updated successfully.');
    }

    public function destroy(Product $product)
    {
        $product->delete();

        return redirect()
            ->route('products.index')
            ->with('success', 'Product deleted successfully.');
    }
}

7. Create Blade Views

Create this folder:

resources/views/products

8. Create Layout File

Create:

resources/views/layouts/app.blade.php

Add:

<!DOCTYPE html>
<html>
<head>
    <title>Laravel CRUD</title>
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
</head>

<body>
<div class="container mt-5">

    <h2 class="mb-4">Laravel Product CRUD</h2>

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

    @if($errors->any())
        <div class="alert alert-danger">
            <ul class="mb-0">
                @foreach($errors->all() as $error)
                    <li>{{ $error }}</li>
                @endforeach
            </ul>
        </div>
    @endif

    @yield('content')

</div>
</body>
</html>

9. Product Index Page

Create:

resources/views/products/index.blade.php

Add:

@extends('layouts.app')

@section('content')

<a href="{{ route('products.create') }}" class="btn btn-primary mb-3">
    Add Product
</a>

<table class="table table-bordered">
    <thead>
        <tr>
            <th>ID</th>
            <th>Name</th>
            <th>Price</th>
            <th>Stock</th>
            <th width="250">Actions</th>
        </tr>
    </thead>

    <tbody>
        @forelse($products as $product)
            <tr>
                <td>{{ $product->id }}</td>
                <td>{{ $product->name }}</td>
                <td>${{ $product->price }}</td>
                <td>{{ $product->stock }}</td>
                <td>
                    <a href="{{ route('products.show', $product) }}" class="btn btn-info btn-sm">
                        View
                    </a>

                    <a href="{{ route('products.edit', $product) }}" class="btn btn-warning btn-sm">
                        Edit
                    </a>

                    <form action="{{ route('products.destroy', $product) }}" method="POST" class="d-inline">
                        @csrf
                        @method('DELETE')

                        <button type="submit" class="btn btn-danger btn-sm"
                            onclick="return confirm('Delete this product?')">
                            Delete
                        </button>
                    </form>
                </td>
            </tr>
        @empty
            <tr>
                <td colspan="5" class="text-center">No products found.</td>
            </tr>
        @endforelse
    </tbody>
</table>

{{ $products->links() }}

@endsection

10. Create Product Page

Create:

resources/views/products/create.blade.php

Add:

@extends('layouts.app')

@section('content')

<form action="{{ route('products.store') }}" method="POST">
    @csrf

    @include('products.form')

    <button type="submit" class="btn btn-success">Save</button>
    <a href="{{ route('products.index') }}" class="btn btn-secondary">Back</a>
</form>

@endsection

11. Edit Product Page

Create:

resources/views/products/edit.blade.php

Add:

@extends('layouts.app')

@section('content')

<form action="{{ route('products.update', $product) }}" method="POST">
    @csrf
    @method('PUT')

    @include('products.form')

    <button type="submit" class="btn btn-success">Update</button>
    <a href="{{ route('products.index') }}" class="btn btn-secondary">Back</a>
</form>

@endsection

12. Product Form Partial

Create:

resources/views/products/form.blade.php

Add:

<div class="mb-3">
    <label class="form-label">Name</label>
    <input 
        type="text" 
        name="name" 
        class="form-control"
        value="{{ old('name', $product->name ?? '') }}"
        required
    >
</div>

<div class="mb-3">
    <label class="form-label">Description</label>
    <textarea 
        name="description" 
        class="form-control"
        rows="4"
    >{{ old('description', $product->description ?? '') }}</textarea>
</div>

<div class="mb-3">
    <label class="form-label">Price</label>
    <input 
        type="number" 
        name="price" 
        step="0.01" 
        class="form-control"
        value="{{ old('price', $product->price ?? '') }}"
        required
    >
</div>

<div class="mb-3">
    <label class="form-label">Stock</label>
    <input 
        type="number" 
        name="stock" 
        class="form-control"
        value="{{ old('stock', $product->stock ?? '') }}"
        required
    >
</div>

13. Show Product Page

Create:

resources/views/products/show.blade.php

Add:

@extends('layouts.app')

@section('content')

<div class="card">
    <div class="card-body">
        <h4>{{ $product->name }}</h4>

        <p><strong>Description:</strong> {{ $product->description }}</p>
        <p><strong>Price:</strong> ${{ $product->price }}</p>
        <p><strong>Stock:</strong> {{ $product->stock }}</p>

        <a href="{{ route('products.edit', $product) }}" class="btn btn-warning">
            Edit
        </a>

        <a href="{{ route('products.index') }}" class="btn btn-secondary">
            Back
        </a>
    </div>
</div>

@endsection

14. Run Laravel App

php artisan serve

Visit:

http://127.0.0.1:8000/products

You now have a complete Laravel CRUD for products.

Leave a Reply

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

This site uses Akismet to reduce spam. Learn how your comment data is processed.