owned this note
owned this note
以下範例是使用 Laravel 8/9/10 皆可通用的示範程式碼,示範如何建立一個包含「會員系統」與「討論區」功能的網路討論區專案。程式碼側重在 關鍵功能與流程 的展示,實際專案可再根據需求做更多細節優化。整個回答將包含:
1. 資料庫設計(Migrations)
2. Model (Eloquent) 設計
3. 中介層 (Middleware) 設計
4. 路由 (Routes)
5. Controller 與部分 Request 驗證
6. Blade 模板 (前端)
7. 程式碼與功能說明
1. 資料庫設計(Migrations)
1.1 users 資料表
此處包含會員基本資料,如帳號(email)、顯示名稱(name)、個人頭像(avatar)、角色(role)等。
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateUsersTable extends Migration
{
public function up()
{
Schema::create('users', function (Blueprint $table) {
$table->id();
$table->string('name'); // 使用者名稱
$table->string('email')->unique(); // 使用者 Email (唯一)
$table->string('password'); // 使用者密碼
$table->string('avatar')->nullable(); // 頭像路徑 (可選)
$table->string('role')->default('member');
// 角色 (可為 'admin', 'moderator', 'member' 等)
$table->text('bio')->nullable(); // 自我介紹
$table->rememberToken();
$table->timestamps();
});
}
public function down()
{
Schema::dropIfExists('users');
}
}
1.2 forums 資料表
此處放置討論區(板塊)的資訊。
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateForumsTable extends Migration
{
public function up()
{
Schema::create('forums', function (Blueprint $table) {
$table->id();
$table->string('name'); // 討論區名稱
$table->text('description'); // 討論區描述
$table->timestamps();
});
}
public function down()
{
Schema::dropIfExists('forums');
}
}
1.3 posts 資料表
用來儲存文章資訊,並跟所屬討論區和發文者做關聯。
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreatePostsTable extends Migration
{
public function up()
{
Schema::create('posts', function (Blueprint $table) {
$table->id();
$table->string('title'); // 文章標題
$table->text('content'); // 文章內容
$table->unsignedBigInteger('forum_id'); // 所屬討論區
$table->unsignedBigInteger('user_id'); // 發文者 (會員)
$table->string('image_path')->nullable(); // 文章上傳圖片的路徑 (可選)
$table->timestamps();
// 外鍵關聯
$table->foreign('forum_id')
->references('id')
->on('forums')
->onDelete('cascade');
$table->foreign('user_id')
->references('id')
->on('users')
->onDelete('cascade');
});
}
public function down()
{
Schema::dropIfExists('posts');
}
}
2. Model (Eloquent) 設計
2.1 User Model
namespace App\Models;
use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
class User extends Authenticatable
{
use HasFactory, Notifiable;
protected $fillable = [
'name',
'email',
'password',
'avatar',
'role',
'bio'
];
protected $hidden = [
'password',
'remember_token',
];
// 一位使用者可以發多篇文章 (一對多)
public function posts()
{
return $this->hasMany(Post::class);
}
}
2.2 Forum Model
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Forum extends Model
{
use HasFactory;
protected $fillable = [
'name',
'description'
];
// 一個討論區可以有多篇文章 (一對多)
public function posts()
{
return $this->hasMany(Post::class);
}
}
2.3 Post Model
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Post extends Model
{
use HasFactory;
protected $fillable = [
'title',
'content',
'forum_id',
'user_id',
'image_path'
];
// 一篇文章屬於一個討論區 (多對一)
public function forum()
{
return $this->belongsTo(Forum::class);
}
// 一篇文章由一個使用者發文 (多對一)
public function user()
{
return $this->belongsTo(User::class);
}
}
3. 中介層 (Middleware) 設計
3.1 角色檢查範例:CheckRole Middleware
假設我們需要判斷使用者是否為「網站管理員」或「討論區管理員」,可在 app/Http/Middleware/CheckRole.php 中設定邏輯:
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
class CheckRole
{
/**
* Handle an incoming request.
*/
public function handle(Request $request, Closure $next, ...$roles)
{
$user = Auth::user();
if (!$user) {
// 尚未登入
return redirect()->route('login')->with('error', '請先登入');
}
// 檢查使用者的角色是否在允許清單中
if (!in_array($user->role, $roles)) {
return redirect()->route('home')->with('error', '您沒有存取此頁面的權限');
}
return $next($request);
}
}
在 kernel.php 中註冊:
使用方式(範例):
Route::group(['middleware' => ['auth', 'checkRole:admin,moderator']], function() {
// 只有 admin 或 moderator 可以進入的路由
});
4. 路由 (Routes)
假設在 routes/web.php,設置以下基本路由:
use App\Http\Controllers\AuthController;
use App\Http\Controllers\UserController;
use App\Http\Controllers\ForumController;
use App\Http\Controllers\PostController;
// 首頁路由
Route::get('/', function () {
return view('welcome');
})->name('home');
// 會員相關 (登入、登出、註冊、修改資料)
Route::get('/register', [AuthController::class, 'showRegistrationForm'])->name('register.show');
Route::post('/register', [AuthController::class, 'register'])->name('register');
Route::get('/login', [AuthController::class, 'showLoginForm'])->name('login.show');
Route::post('/login', [AuthController::class, 'login'])->name('login');
Route::post('/logout', [AuthController::class, 'logout'])->name('logout');
Route::middleware('auth')->group(function() {
// 會員資料修改
Route::get('/user/profile', [UserController::class, 'edit'])->name('user.profile');
Route::post('/user/profile', [UserController::class, 'update'])->name('user.update');
});
// 討論區列表 & 文章列表
Route::get('/forums', [ForumController::class, 'index'])->name('forums.index');
Route::get('/forums/{forum}', [ForumController::class, 'show'])->name('forums.show');
// 文章相關
Route::middleware('auth')->group(function() {
Route::get('/posts/create', [PostController::class, 'create'])->name('posts.create');
Route::post('/posts', [PostController::class, 'store'])->name('posts.store');
Route::get('/posts/{post}/edit', [PostController::class, 'edit'])->name('posts.edit');
Route::put('/posts/{post}', [PostController::class, 'update'])->name('posts.update');
});
5. Controller 與 Request 驗證
5.1 AuthController (會員註冊與登入)
namespace App\Http\Controllers;
use App\Models\User;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Hash;
use Illuminate\Validation\Rules;
class AuthController extends Controller
{
// 顯示註冊表單
public function showRegistrationForm()
{
return view('auth.register');
}
// 處理註冊邏輯
public function register(Request $request)
{
// 表單驗證
$request->validate([
'name' => ['required', 'string', 'max:255'],
'email' => ['required', 'string', 'email', 'max:255', 'unique:users'],
'password' => ['required', 'confirmed', Rules\Password::defaults()],
]);
// 建立使用者
$user = User::create([
'name' => $request->name,
'email' => $request->email,
'password' => Hash::make($request->password),
]);
// 自動登入
Auth::login($user);
return redirect()->route('home')->with('success', '註冊成功,您已登入!');
}
// 顯示登入表單
public function showLoginForm()
{
return view('auth.login');
}
// 處理登入邏輯
public function login(Request $request)
{
// 表單驗證
$credentials = $request->validate([
'email' => ['required', 'email'],
'password' => ['required'],
]);
// 嘗試登入
if (Auth::attempt($credentials, $request->remember)) {
$request->session()->regenerate();
return redirect()->route('home')->with('success', '登入成功');
}
return back()->withErrors([
'email' => '帳號或密碼錯誤',
]);
}
// 登出
public function logout(Request $request)
{
Auth::logout();
$request->session()->invalidate();
$request->session()->regenerateToken();
return redirect()->route('home')->with('success', '您已登出');
}
}
5.2 UserController (會員資料修改)
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Hash;
class UserController extends Controller
{
public function edit()
{
$user = Auth::user();
return view('user.edit', compact('user'));
}
public function update(Request $request)
{
$user = Auth::user();
// 簡易驗證
$request->validate([
'name' => 'required|string|max:255',
'password' => 'nullable|confirmed|min:8',
'avatar' => 'nullable|image|max:2048',
'bio' => 'nullable|string'
]);
$user->name = $request->name;
// 若有修改密碼
if ($request->filled('password')) {
$user->password = Hash::make($request->password);
}
// 處理頭像 (avatar) 上傳
if ($request->hasFile('avatar')) {
// 存到 storage/app/public/avatars
$path = $request->file('avatar')->store('public/avatars');
// 可以自行處理檔案名稱、路徑
$user->avatar = $path;
}
$user->bio = $request->bio;
$user->save();
return redirect()->back()->with('success', '會員資料已更新');
}
}
5.3 ForumController (討論區列表、單一討論區顯示)
namespace App\Http\Controllers;
use App\Models\Forum;
use Illuminate\Http\Request;
class ForumController extends Controller
{
// 討論區總列表
public function index()
{
$forums = Forum::all();
return view('forums.index', compact('forums'));
}
// 顯示單一討論區以及其文章列表(分頁)
public function show(Forum $forum)
{
// 每頁顯示 20 篇文章
$posts = $forum->posts()->paginate(20);
return view('forums.show', compact('forum', 'posts'));
}
}
5.4 PostController (文章新增、編輯)
namespace App\Http\Controllers;
use App\Models\Post;
use App\Models\Forum;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
class PostController extends Controller
{
// 顯示新增文章表單
public function create()
{
$forums = Forum::all(); // 用來在前端選擇要發在哪個討論區
return view('posts.create', compact('forums'));
}
// 新增文章
public function store(Request $request)
{
// 驗證
$request->validate([
'title' => 'required|string|max:255',
'content' => 'required|string',
'forum_id' => 'required|exists:forums,id',
'image' => 'nullable|image|max:2048'
]);
$post = new Post();
$post->title = $request->title;
$post->content = $request->content;
$post->forum_id = $request->forum_id;
$post->user_id = Auth::id();
// 處理圖片上傳
if ($request->hasFile('image')) {
// 這裡示範存到本機 storage 路徑
$path = $request->file('image')->store('public/posts');
$post->image_path = $path;
}
$post->save();
return redirect()->route('forums.show', $post->forum_id)
->with('success', '文章發表成功');
}
// 顯示編輯文章表單
public function edit(Post $post)
{
// 只有文章作者或管理者可以編輯 (自行在此檢查)
$this->authorize('update', $post); // 也可用 Policy
$forums = Forum::all();
return view('posts.edit', compact('post', 'forums'));
}
// 更新文章
public function update(Request $request, Post $post)
{
// 只有文章作者或管理者可以編輯 (自行在此檢查)
$this->authorize('update', $post);
// 驗證
$request->validate([
'title' => 'required|string|max:255',
'content' => 'required|string',
'forum_id' => 'required|exists:forums,id',
'image' => 'nullable|image|max:2048'
]);
$post->title = $request->title;
$post->content = $request->content;
$post->forum_id = $request->forum_id;
// 處理圖片上傳
if ($request->hasFile('image')) {
$path = $request->file('image')->store('public/posts');
$post->image_path = $path;
}
$post->save();
return redirect()->route('forums.show', $post->forum_id)
->with('success', '文章更新成功');
}
}
6. Blade 模板 (前端)
下列僅示範主要 Blade 檔案的結構與表單,實際專案可根據需求加上 CSS、JS、Layout 等。
6.1 resources/views/auth/register.blade.php (註冊)
@extends('layouts.app')
@section('content')
<h2>會員註冊</h2>
<form action="{{ route('register') }}" method="POST">
@csrf
<div>
<label for="name">名稱:</label>
<input type="text" name="name" value="{{ old('name') }}" required>
@error('name')
<div>{{ $message }}</div>
@enderror
</div>
<div>
<label for="email">Email:</label>
<input type="email" name="email" value="{{ old('email') }}" required>
@error('email')
<div>{{ $message }}</div>
@enderror
</div>
<div>
<label for="password">密碼:</label>
<input type="password" name="password" required>
@error('password')
<div>{{ $message }}</div>
@enderror
</div>
<div>
<label for="password_confirmation">確認密碼:</label>
<input type="password" name="password_confirmation" required>
</div>
<button type="submit">註冊</button>
</form>
@endsection
6.2 resources/views/auth/login.blade.php (登入)
@extends('layouts.app')
@section('content')
<h2>會員登入</h2>
<form action="{{ route('login') }}" method="POST">
@csrf
<div>
<label for="email">Email:</label>
<input type="email" name="email" value="{{ old('email') }}" required>
@error('email')
<div>{{ $message }}</div>
@enderror
</div>
<div>
<label for="password">密碼:</label>
<input type="password" name="password" required>
</div>
<div>
<label for="remember">記住我</label>
<input type="checkbox" name="remember">
</div>
<button type="submit">登入</button>
</form>
@endsection
6.3 resources/views/posts/create.blade.php (文章新增)
@extends('layouts.app')
@section('content')
<h2>發表新文章</h2>
<form action="{{ route('posts.store') }}" method="POST" enctype="multipart/form-data">
@csrf
<div>
<label for="title">標題:</label>
<input type="text" name="title" value="{{ old('title') }}" required>
@error('title')
<div>{{ $message }}</div>
@enderror
</div>
<div>
<label for="content">內容:</label>
<textarea name="content" rows="5" required>{{ old('content') }}</textarea>
@error('content')
<div>{{ $message }}</div>
@enderror
</div>
<div>
<label for="forum_id">討論區:</label>
<select name="forum_id" required>
<option value="">--請選擇--</option>
@foreach($forums as $forum)
<option value="{{ $forum->id }}" {{ old('forum_id') == $forum->id ? 'selected' : '' }}>
{{ $forum->name }}
</option>
@endforeach
</select>
@error('forum_id')
<div>{{ $message }}</div>
@enderror
</div>
<div>
<label for="image">圖片上傳:</label>
<input type="file" name="image" accept="image/*">
@error('image')
<div>{{ $message }}</div>
@enderror
</div>
<button type="submit">發表文章</button>
</form>
@endsection
6.4 resources/views/layouts/app.blade.php (範例主版型)
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Laravel Forum</title>
</head>
<body>
<header>
<nav>
<a href="{{ route('home') }}">首頁</a>
<a href="{{ route('forums.index') }}">討論區列表</a>
@guest
<a href="{{ route('login.show') }}">登入</a>
<a href="{{ route('register.show') }}">註冊</a>
@else
<form action="{{ route('logout') }}" method="POST" style="display:inline;">
@csrf
<button type="submit">登出</button>
</form>
@endguest
</nav>
</header>
<main>
@if(session('success'))
<div style="color: green;">{{ session('success') }}</div>
@endif
@if(session('error'))
<div style="color: red;">{{ session('error') }}</div>
@endif
@yield('content')
</main>
</body>
</html>
7. 程式碼與功能說明
1. 資料庫遷移 (Migrations)
• users:儲存會員資訊,包括名稱、email、密碼、頭像路徑、角色、簡介等。
• forums:討論區(板塊)資訊,如名稱和描述。
• posts:文章資訊,包括標題、內容、發文者 ID、對應討論區 ID、圖片路徑等。
2. Eloquent 關聯
• User 與 Post 是一對多 (One to Many):User 可以擁有多個 posts()。
• Forum 與 Post 也是一對多 (One to Many):Forum 可以有多個 posts()。
• Post 分別屬於 User 和 Forum (多對一):$post->user()、$post->forum()。
3. Middleware
• 建立了 CheckRole 用於檢查使用者角色,若不符合要求則導回首頁。
• 可在路由或控制器中使用 checkRole:admin,moderator 等。
• 或者使用 Laravel 的 授權策略 (Policies) 做更細緻的權限控管。
4. 會員註冊與登入
• AuthController::register 先驗證表單,接著建立 User,最後自動登入。
• AuthController::login 使用 Auth::attempt 嘗試驗證並登入。
• AuthController::logout 執行 Auth::logout 並重設 session token。
5. 會員資料修改 (UserController)
• 在 edit() 取得目前登入者資訊,並返回編輯表單。
• 在 update() 做表單驗證後,更新使用者名稱、密碼、頭像、簡介等欄位。
• 頭像可透過 $request->file('avatar')->store('public/avatars') 進行本機儲存。
6. 討論區功能 (ForumController)
• index():顯示所有 Forum。
• show():顯示單一討論區內文,並以 paginate(20) 顯示該討論區的文章列表。
7. 文章功能 (PostController)
• create():顯示新增文章表單,並讓使用者選擇所屬的討論區。
• store():驗證表單後,建立 Post,並可上傳圖片(儲存到 storage/app/public/posts)。
• edit():顯示編輯文章表單。
• update():更新文章標題、內容、所屬討論區,以及圖片檔案等。
總結
以上範例提供了 Laravel 中實作會員系統與討論區核心功能的邏輯,包括:
• 會員註冊/登入/登出
• 會員角色與權限檢查 (Middleware)
• 文章新增、編輯、上傳圖片
• 討論區板塊列表與文章分頁
對照需求,包含了:
1. 使用 Blade 模板構建頁面。
2. 註冊、登入頁面有基礎驗證(Email 格式、密碼長度/確認)。
3. 文章編輯支援文字輸入、圖片上傳,以及在 Blade 中可以加入簡單的文字樣式(可自行整合 WYSIWYG 編輯器)。
4. 後端會員功能包括註冊、登入、會員資料修改、中介層(Middleware)權限檢查與 API(可把 update 改為 API)。
5. 討論區功能:分類(Forums)、文章列表20篇分頁(Posts)、支援圖片上傳。
6. 完整遷移檔(Migrations)、Eloquent Model 關聯說明(一對多關係)。
此專案僅是最基礎的範例,可進一步整合更多功能(例如:留言功能、通知、站內信、SEO 設計、文章搜尋、投票、編輯器套件等),也可將圖片上傳改用第三方儲存(如 S3、阿里雲 OSS、Google Cloud Storage),只需調整 filesystems.php 以及上傳邏輯即可。
希望能幫助您快速了解並上手 Laravel 如何實作完整的討論區與會員管理流程。祝專案開發順利!