Build a Personal Developer Portfolio Blog in Laravel – Full Tutorial with Free Source Code
1759950633_build-a-personal-developer-portfolio-blog-in-laravel-full-tutorial-with-free-source-code.jpg

Build a Personal Developer Portfolio Blog in Laravel – Full Tutorial with Free Source Code

Build a Developer Portfolio Blog with Laravel – Step-by-Step Guide

🛠️ Step 1: Install Laravel Project

a. Create Laravel Project  

bash

composer create-project laravel/laravel developer

b. Serve the App  

bash

cd developer

php artisan serve

Visit: http://localhost:8000

🧩 Step 2: Set Up Database

a. Create Database

Use phpMyAdmin or MySQL CLI:

sql

CREATE DATABASE developer_blog;

b. Configure .env

DB_DATABASE=developer_blog

DB_USERNAME=root

DB_PASSWORD=

🧱 Step 3: Create Post Model and Migration

bash

php artisan make:model Post -m

c. Edit Model File

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class Post extends Model

{

     protected $guarded = [];

}

 

d. Edit Migration File

database/migrations/xxxx_create_posts_table.php:  

php

public function up()

{ Schema::create('posts', function (Blueprint $table) {

$table->id();

$table->string('title');

$table->string('slug')->unique();

$table->text('content');

$table->timestamps();

});

}

e. Run Migration

bash

php artisan migrate

🧠 Step 4: Create PostController and Routes

a. Create Controller

bash

php artisan make:controller PostController --resource

b. Add Route

In routes/web.php:

php

use App\Http\Controllers\PostController;

Route::get('/', [PostController::class, 'index'])->name('home');

Route::resource('posts', PostController::class);

 

✏️ Step 5: Controller Logic (CRUD)

In PostController.php:

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;

use App\Models\Post;

use Illuminate\Support\Str;

class PostController extends Controller

{

public function index() {

    $posts = Post::latest()->paginate(5);

    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([

        'title' => $request->title,

        'slug' => Str::slug($request->title),

        'content' => $request->content,

    ]);

 

    return redirect()->route('home')->with('success', 'Post Created');

}

 

public function show($slug) {

    $post = Post::where('slug', $slug)->firstOrFail();

    return view('posts.show', compact('post'));

}

 

public function edit($id) {

    $post = Post::findOrFail($id);

    return view('posts.edit', compact('post'));

}

 

public function update(Request $request, $id) {

    $post = Post::findOrFail($id);

    $post->update([

        'title' => $request->title,

        'slug' => Str::slug($request->title),

        'content' => $request->content,

    ]);

 

    return redirect()->route('home')->with('success', 'Post Updated');

}

 

public function destroy($id) {

    Post::findOrFail($id)->delete();

    return redirect()->route('home')->with('success', 'Post Deleted');

}

}

📁 Step 6: Views (Blade Templates)

a. resources/views/layout.blade.php  

blade

<!DOCTYPE html>

<html lang="en">

<head>

<meta charset="UTF-8">

<meta name="description" content="@yield('meta_description', 'Developer Blog Built with Laravel')">

<meta name="keywords" content="@yield('meta_keywords', 'Laravel, Developer Blog, Portfolio, PHP')">

<meta name="viewport" content="width=device-width, initial-scale=1.0">

<title>@yield('title', 'Developer Blog')</title>

<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet">

</head>

<body>

<div class="container mt-4">

<h1 class="mb-4"><a href="{{ route('home') }}">Developer Blog</a></h1>

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

@endif @yield('content')

</div>

</body>

</html>

b. resources/views/posts/index.blade.php

@extends('layout')

@section('title', 'All Blog Posts')

@section('meta_description', 'Read the latest blog posts from a Laravel developer portfolio.')

@section('content')

    <div class="d-flex justify-content-between align-items-center mb-4">

        <h1 class="h3">📘 Developer Blog</h1>

        <a href="{{ route('posts.create') }}" class="btn btn-success">➕ Create New Post</a>

    </div>

    <div class="row">

        @foreach ($posts as $post)

            <div class="col-md-6 col-lg-4 mb-4">

                <div class="card h-100 shadow-sm">

                    <div class="card-body d-flex flex-column">

                        <h5 class="card-title">

                            <a href="{{ route('posts.show', $post->slug) }}" class="text-decoration-none text-primary">

                                {{ $post->title }}

                            </a>

                        </h5>

                        <p class="card-text">{{ Str::limit($post->content, 100) }}</p>

                        <div class="mt-auto">

                            <a href="{{ route('posts.edit', $post->id) }}" class="btn btn-sm btn-outline-warning me-2">✏️ Edit</a>

                            <form action="{{ route('posts.destroy', $post->id) }}" method="POST" class="d-inline">

                                @csrf @method('DELETE')

                                <button class="btn btn-sm btn-outline-danger">🗑️ Delete</button>

                            </form>

                        </div>

                    </div>

                </div>

            </div>

        @endforeach

    </div>

    <div class="d-flex justify-content-center mt-4">

        {{ $posts->links() }}

    </div>

@endsection

Note: Go to your app/Providers/AppServiceProvider.php file. Then add use Illuminate\Pagination\Paginator; and find out the boot() function. Then write this line Paginator::useBootstrap(); inside the function. 

 

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

@extends('layout')

@section('title', isset($post) ? 'Edit Post' : 'Create New Post')

@section('meta_description', isset($post) ? 'Edit an existing blog post.' : 'Write a new blog post.')

@section('content')

    <div class="card shadow-sm">

        <div class="card-body">

            <h2 class="mb-4">

                {{ isset($post) ? '✏️ Edit Post' : '📝 Create New Post' }}

            </h2>

            <form action="{{ isset($post) ? route('posts.update', $post->id) : route('posts.store') }}" method="POST">

                @csrf

                @if(isset($post))

                    @method('PUT')

                @endif

                <div class="mb-3">

                    <label for="title" class="form-label fw-bold">Title</label>

                    <input type="text" name="title" id="title" class="form-control"

                        value="{{ old('title', $post->title ?? '') }}" required>

                </div>

                <div class="mb-3">

                    <label for="content" class="form-label fw-bold">Content</label>

                    <textarea name="content" id="content" rows="6" class="form-control" required>{{ old('content', $post->content ?? '') }}</textarea>

                </div>

                <button type="submit" class="btn btn-success me-2">

                    {{ isset($post) ? '💾 Update Post' : '✅ Publish Post' }}

                </button>

                <a href="{{ route('posts.index') }}" class="btn btn-secondary">↩️ Cancel</a>

            </form>

        </div>

    </div>

@endsection

 

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

@extends('layout')

@section('title', 'Edit Post')

@section('meta_description', 'Edit your blog post content and title easily.')

@section('content')

    <div class="card shadow-sm">

        <div class="card-body">

            <h2 class="mb-4 text-warning">✏️ Edit Post</h2>

            <form action="{{ route('posts.update', $post->id) }}" method="POST">

                @csrf

                @method('PUT')

                <div class="mb-3">

                    <label for="title" class="form-label fw-bold">Title</label>

                    <input type="text" name="title" id="title" class="form-control"

                           value="{{ old('title', $post->title) }}" required>

                </div>

                <div class="mb-3">

                    <label for="content" class="form-label fw-bold">Content</label>

                    <textarea name="content" id="content" rows="6" class="form-control" required>{{ old('content', $post->content) }}</textarea>

                </div>

                <button type="submit" class="btn btn-primary me-2">💾 Update Post</button>

                <a href="{{ route('posts.index') }}" class="btn btn-secondary">↩️ Cancel</a>

            </form>

        </div>

    </div>

@endsection

 

e. resources/views/posts/show.blade.php

@extends('layout')

@section('title', $post->title)

@section('meta_description', Str::limit($post->content, 150))

@section('content')

    <div class="card shadow-sm mb-4">

        <div class="card-body">

            <h1 class="card-title text-primary">{{ $post->title }}</h1>

            <p class="text-muted mb-4">Published: {{ $post->created_at->format('F d, Y') }}</p>

            <p class="card-text fs-5">{{ $post->content }}</p>

        </div>

    </div>

    <a href="{{ route('posts.edit', $post->id) }}" class="btn btn-warning me-2">✏️ Edit</a>

    <form action="{{ route('posts.destroy', $post->id) }}" method="POST" class="d-inline">

        @csrf @method('DELETE')

        <button class="btn btn-danger">🗑️ Delete</button>

    </form>

    <a href="{{ route('posts.index') }}" class="btn btn-secondary ms-2">⬅️ Back to Blog</a>

@endsection