Laravel 12 CRUD Application with Resource Controller

Laravel 12 CRUD Application with Resource Controller

Laravel 12 CRUD Application with Resource Controller

In this tutorial, you will build a fully functional CRUD (Create, Read, Update, Delete) application in Laravel 12 using a resource controller, form request validation, and Blade templates.

Step 1: Install Laravel 12

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

Configure your database in the .env file:

DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=laravel_crud
DB_USERNAME=root
DB_PASSWORD=

Step 2: Create Model, Migration, Factory, and Controller

php artisan make:model Post -mfc --resource

Open the generated migration and define the columns:

Schema::create('posts', function (Blueprint $table) {
    $table->id();
    $table->foreignId('user_id')->constrained()->cascadeOnDelete();
    $table->string('title');
    $table->string('slug')->unique();
    $table->text('body');
    $table->boolean('published')->default(false);
    $table->timestamps();
});
php artisan migrate

Step 3: Add Sluggable to the Model

// app/Models/Post.php
class Post extends Model
{
    protected $fillable = ['user_id', 'title', 'slug', 'body', 'published'];

    protected function casts(): array
    {
        return ['published' => 'boolean'];
    }
}

Step 4: Create Form Requests

php artisan make:request StorePostRequest
php artisan make:request UpdatePostRequest
// StorePostRequest
public function rules(): array
{
    return [
        'title'     => ['required', 'string', 'max:255', 'unique:posts,title'],
        'body'      => ['required', 'string'],
        'published' => ['boolean'],
    ];
}

// UpdatePostRequest
public function rules(): array
{
    return [
        'title'     => ['required', 'string', 'max:255', 'unique:posts,title,'.$this->post->id],
        'body'      => ['required', 'string'],
        'published' => ['boolean'],
    ];
}

Step 5: Write the Resource Controller

// app/Http/Controllers/PostController.php
use Illuminate\Support\Str;

class PostController extends Controller
{
    public function index(): View
    {
        $posts = Post::latest()->paginate(10);
        return view('posts.index', compact('posts'));
    }

    public function create(): View
    {
        return view('posts.create');
    }

    public function store(StorePostRequest $request): RedirectResponse
    {
        $data = $request->validated();
        $data['user_id'] = auth()->id();
        $data['slug']    = Str::slug($data['title']);

        Post::create($data);

        return redirect()->route('posts.index')->with('success', 'Post created!');
    }

    public function show(Post $post): View
    {
        return view('posts.show', compact('post'));
    }

    public function edit(Post $post): View
    {
        return view('posts.edit', compact('post'));
    }

    public function update(UpdatePostRequest $request, Post $post): RedirectResponse
    {
        $data = $request->validated();
        $data['slug'] = Str::slug($data['title']);

        $post->update($data);

        return redirect()->route('posts.index')->with('success', 'Post updated!');
    }

    public function destroy(Post $post): RedirectResponse
    {
        $post->delete();

        return redirect()->route('posts.index')->with('success', 'Post deleted!');
    }
}

Step 6: Register the Resource Route

// routes/web.php
Route::middleware('auth')->resource('posts', PostController::class);

Step 7: Create Blade Views

Create the directory resources/views/posts/ and add these files:

{{-- index.blade.php --}}
@extends('layouts.app')
@section('content')
    <h1>Posts</h1>
    <a href="{{ route('posts.create') }}">New Post</a>

    @if (session('success')) <p>{{ session('success') }}</p> @endif

    @foreach ($posts as $post)
        <div>
            <h2>{{ $post->title }}</h2>
            <a href="{{ route('posts.edit', $post) }}">Edit</a>
            <form method="POST" action="{{ route('posts.destroy', $post) }}">
                @csrf @method('DELETE')
                <button onclick="return confirm('Delete?')">Delete</button>
            </form>
        </div>
    @endforeach

    {{ $posts->links() }}
@endsection
{{-- create.blade.php --}}
@extends('layouts.app')
@section('content')
    <h1>Create Post</h1>
    <form method="POST" action="{{ route('posts.store') }}">
        @csrf
        <div>
            <label>Title</label>
            <input type="text" name="title" value="{{ old('title') }}">
            @error('title') <p>{{ $message }}</p> @enderror
        </div>
        <div>
            <label>Body</label>
            <textarea name="body">{{ old('body') }}</textarea>
            @error('body') <p>{{ $message }}</p> @enderror
        </div>
        <label>
            <input type="checkbox" name="published" value="1" {{ old('published') ? 'checked' : '' }}> Publish
        </label>
        <button type="submit">Save</button>
    </form>
@endsection

Step 8: Test the Application

php artisan serve

Visit http://127.0.0.1:8000/posts to see your full CRUD application. You can create, read, update, and delete posts with validation and flash messages working out of the box.

All Comments