Laravel 12 Tutorial for Beginners — Step-by-Step

Laravel 12 Tutorial for Beginners — Step-by-Step

 Below is a clean, copy-ready tutorial you can post on your website. It walks a beginner through building a simple CRUD (Create, Read, Update, Delete) app with Laravel 12 — including commands, code snippets, and helpful tips.

Introduction

In this tutorial, you’ll learn how to build a simple blog-like CRUD application using Laravel 12. We'll create posts with titles and content, list them, edit them, and delete them. This guide assumes you know basic terminal usage and have a development environment set up.

Requirements

  • PHP 8.1 or newer

  • Composer

  • MySQL or MariaDB (or any supported database)

  • Optional: Node.js & NPM (for frontend assets / compiling CSS/JS)

Step 1 — Create a new Laravel project

Run:

composer create-project laravel/laravel:^12.0 my-laravel-app cd my-laravel-app

Start the development server:

php artisan serve

Visit http://localhost:8000 to confirm Laravel is running.

Step 2 — Project structure (quick guide)

  • app/ — Models, Controllers, core logic

  • routes/web.php (web routes), api.php (API routes)

  • resources/views/ — Blade templates (HTML views)

  • database/migrations/ — Migrations (DB schema)

Step 3 — Database Setup

Edit .env:

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

Step 4 — Create Post model + migration

Generate a model with migration:

php artisan make:model Post -m

Open the migration file in database/migrations/ (it will be timestamped) and edit the up() method:

public function up(): void { Schema::create('posts', function (Blueprint $table) { $table->id(); $table->string('title'); $table->text('content'); $table->timestamps(); }); }

Run migrations:

php artisan migrate

Step 5 — Add mass assignment protection to the model

Open app/Models/Post.php and add $fillable So you can use Post::create() safely:

namespace App\Models; use Illuminate\Database\Eloquent\Model; class Post extends Model { protected $fillable = ['title', 'content']; }

Step 6 — Generate Controller (resource)

Create a resource controller to handle CRUD:

php artisan make:controller PostController --resource

This command creates app/Http/Controllers/PostController.php with boilerplate methods (index, create, store, show, edit, update, destroy).

Step 7 — Implement controller methods

Replace the controller methods with the following (concise implementation):

namespace App\Http\Controllers; use App\Models\Post; use Illuminate\Http\Request; class PostController extends Controller { public function index() { // Optionally paginate: Post::latest()->paginate(10); $posts = Post::latest()->get(); return view('posts.index', compact('posts')); } public function create() { return view('posts.create'); } public function store(Request $request) { $request->validate([ 'title' => 'required|max:255', 'content' => 'required', ]); Post::create($request->only('title', 'content')); return redirect()->route('posts.index') ->with('success', 'Post created successfully.'); } public function show(Post $post) { return view('posts.show', compact('post')); } public function edit(Post $post) { return view('posts.edit', compact('post')); } public function update(Request $request, Post $post) { $request->validate([ 'title' => 'required|max:255', 'content' => 'required', ]); $post->update($request->only('title', 'content')); return redirect()->route('posts.index') ->with('success', 'Post updated successfully.'); } public function destroy(Post $post) { $post->delete(); return redirect()->route('posts.index') ->with('success', 'Post deleted successfully.'); } }

Step 8 — Add resource routes

Open routes/web.php and add:

use App\Http\Controllers\PostController; Route::resource('posts', PostController::class); Route::get('/', function () { return redirect()->route('posts.index'); });

php artisan route:list will now show all RESTful routes for posts.

Step 9 — Create Blade views (basic)

Create a resources/views/posts folder and add the following minimal templates.

resources/views/layouts/app.blade.php (master layout)

<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>Laravel 12 CRUD</title> <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet"> </head> <body> <div class="container py-4"> @if(session('success')) <div class="alert alert-success">{{ session('success') }}</div> @endif @yield('content') </div> </body> </html>

resources/views/posts/index.blade.php

@extends('layouts.app') @section('content') <div class="d-flex justify-content-between align-items-center mb-3"> <h1>Posts</h1> <a href="{{ route('posts.create') }}" class="btn btn-primary">Create Post</a> </div> @foreach($posts as $post) <div class="card mb-2"> <div class="card-body"> <h5 class="card-title">{{ $post->title }}</h5> <p class="card-text">{{ Str::limit($post->content, 200) }}</p> <a href="{{ route('posts.show', $post) }}" class="btn btn-sm btn-outline-secondary">View</a> <a href="{{ route('posts.edit', $post) }}" class="btn btn-sm btn-warning">Edit</a> <form action="{{ route('posts.destroy', $post) }}" method="POST" class="d-inline" onsubmit="return confirm('Delete this post?');"> @csrf @method('DELETE') <button class="btn btn-sm btn-danger">Delete</button> </form> </div> </div> @endforeach @endsection

resources/views/posts/create.blade.php and edit.blade.php

You can use the same form partial for create/edit.

resources/views/posts/create.blade.php:

@extends('layouts.app') @section('content') <h1>Create Post</h1> @if ($errors->any()) <div class="alert alert-danger"> <ul class="mb-0"> @foreach ($errors->all() as $error) <li>{{ $error }}</li> @endforeach </ul> </div> @endif <form action="{{ route('posts.store') }}" method="POST"> @csrf <div class="mb-3"> <label class="form-label">Title</label> <input name="title" class="form-control" value="{{ old('title') }}"> </div> <div class="mb-3"> <label class="form-label">Content</label> <textarea name="content" rows="6" class="form-control">{{ old('content') }}</textarea> </div> <button class="btn btn-primary">Save</button> </form> @endsection

resources/views/posts/edit.blade.php:

@extends('layouts.app') @section('content') <h1>Edit Post</h1> @if ($errors->any()) <div class="alert alert-danger"> <ul class="mb-0"> @foreach ($errors->all() as $error) <li>{{ $error }}</li> @endforeach </ul> </div> @endif <form action="{{ route('posts.update', $post) }}" method="POST"> @csrf @method('PUT') <div class="mb-3"> <label class="form-label">Title</label> <input name="title" class="form-control" value="{{ old('title', $post->title) }}"> </div> <div class="mb-3"> <label class="form-label">Content</label> <textarea name="content" rows="6" class="form-control">{{ old('content', $post->content) }}</textarea> </div> <button class="btn btn-primary">Update</button> </form> @endsection

resources/views/posts/show.blade.php

@extends('layouts.app') @section('content') <h1>{{ $post->title }}</h1> <p>{{ $post->content }}</p> <a href="{{ route('posts.index') }}" class="btn btn-secondary">Back</a> @endsection

Step 10 — Test the app

  • Visit /posts — should show list (initially empty)

  • Create a post via /posts/create

  • Edit and delete posts via the UI

If you encounter validation errors, the forms will display them (we have included server-side validation in the controller).

Souy Soeng

Souy Soeng

Hi there 👋, I’m Soeng Souy (StarCode Kh)
-------------------------------------------
🌱 I’m currently creating a sample Laravel and React Vue Livewire
👯 I’m looking to collaborate on open-source PHP & JavaScript projects
💬 Ask me about Laravel, MySQL, or Flutter
⚡ Fun fact: I love turning ☕️ into code!

Post a Comment

CAN FEEDBACK
close