摘要
关于这部分,我的实际体会是这样的:本文详细介绍Laravel游戏论坛API的完整开发过程,包括1000y项目回顾、89个API端点设计、64张表数据库架构、核心功能实现代码示例。
项目回顾
1000y游戏论坛
- 域名:https://1000y.chencunli.com
- 框架:Laravel 11.48.0 + PHP 8.4.18
- API数量:89个端点(已上线)
- 数据库:1000y(64张表)
- 读写分离:HyperDB + MySQL主从复制
路由设计
路由文件结构
routes/
├── api.php # API路由
├── web.php # Web路由
└── channels.php # 广播通道
API路由分组
// routes/api.php
// ========================================
// 公开API
// ========================================
Route::prefix("v1")->group(function () {
// 游戏相关
Route::apiResource("games", GameController::class);
Route::get("games/{slug}/articles", [GameArticleController::class, "byGame"]);
Route::get("games/{slug}/primary", [GameController::class, "getPrimary"]);
// 文章相关
Route::apiResource("articles", ArticleController::class);
Route::get("articles/{id}/comments", [CommentController::class, "index"]);
// 分类和标签
Route::apiResource("categories", CategoryController::class);
Route::apiResource("tags", TagController::class);
});
// ========================================
// 需要认证的API
// ========================================
Route::middleware("auth:sanctum")->group(function () {
// 用户文章管理
Route::post("articles", [ArticleController::class, "store"]);
Route::put("articles/{id}", [ArticleController::class, "update"]);
Route::delete("articles/{id}", [ArticleController::class, "destroy"]);
// 评论管理
Route::post("articles/{id}/comments", [CommentController::class, "store"]);
Route::put("comments/{id}", [CommentController::class, "update"]);
Route::delete("comments/{id}", [CommentController::class, "destroy"]);
});
重要:路由排序规则
具体路由必须在通配符路由之前!
// ✅ 正确顺序
Route::get("games/{slug}/primary", [GameController::class, "getPrimary"]);
Route::get("games/{slug}", [GameController::class, "show"]);
// ❌ 错误顺序
Route::get("games/{slug}", [GameController::class, "show"]);
Route::get("games/{slug}/primary", [GameController::class, "getPrimary"]);
// /games/primary 会被第一条路由捕获,slug值为"primary"
数据库架构
核心表结构
| 表名 | 说明 | 关联 |
|---|---|---|
| games | 游戏表 | hasMany(articles) |
| game_articles | 游戏文章 | belongsTo(Game) |
| categories | 分类表 | belongsToMany(Articles) |
| tags | 标签表 | belongsToMany(Articles) |
| comments | 评论表 | belongsTo(Article) |
| users | 用户表 | hasMany(Articles) |
游戏表迁移示例
// database/migrations/2024_01_01_create_games_table.php
Schema::create("games", function (Blueprint $table) {
$table->id();
$table->string("name");
$table->string("slug")->unique();
$table->text("description")->nullable();
$table->string("cover_image")->nullable();
$table->string("theme_color")->default("#00f0ff");
$table->boolean("is_primary")->default(false);
$table->integer("sort_order")->default(0);
$table->timestamps();
$table->softDeletes();
});
核心功能实现
1. 游戏列表API
// app/Http/Controllers/GameController.php
class GameController extends Controller
{
/**
* 获取游戏列表
* GET /api/v1/games
*/
public function index(Request $request)
{
$games = Game::query()
->withCount("articles as articles_count")
->orderBy("sort_order")
->orderBy("id", "desc")
->paginate($request->get("per_page", 12));
return response()->json([
"success" => true,
"data" => $games->items(),
"pagination" => [
"total" => $games->total(),
"per_page" => $games->perPage(),
"current_page" => $games->currentPage(),
"last_page" => $games->lastPage(),
]
]);
}
/**
* 获取单个游戏详情
* GET /api/v1/games/{slug}
*/
public function show($slug)
{
$game = Game::where("slug", $slug)
->with(["articles" => function ($query) {
$query->limit(10);
}])
->firstOrFail();
return response()->json([
"success" => true,
"data" => $game
]);
}
/**
* 获取主推游戏
* GET /api/v1/games/primary
*/
public function getPrimary()
{
$game = Game::where("is_primary", true)
->firstOrFail();
return response()->json([
"success" => true,
"data" => $game
]);
}
}
2. 文章详情API
// app/Http/Controllers/ArticleController.php
class ArticleController extends Controller
{
/**
* 获取文章详情
* GET /api/v1/articles/{id}
*/
public function show($id)
{
$article = Article::with(["game", "category", "tags", "author"])
->findOrFail($id);
// 增加浏览次数
$article->increment("views");
return response()->json([
"success" => true,
"data" => $article
]);
}
/**
* 按游戏获取文章
* GET /api/v1/games/{slug}/articles
*/
public function byGame($slug, Request $request)
{
$game = Game::where("slug", $slug)->firstOrFail();
$articles = $game->articles()
->with(["category", "tags"])
->orderBy("id", "desc")
->paginate($request->get("per_page", 10));
return response()->json([
"success" => true,
"data" => $articles->items(),
"pagination" => [
"total" => $articles->total(),
"per_page" => $articles->perPage(),
]
]);
}
}
3. 评论系统API
// app/Http/Controllers/CommentController.php
class CommentController extends Controller
{
/**
* 获取文章评论列表
* GET /api/v1/articles/{id}/comments
*/
public function index($articleId)
{
$comments = Comment::where("article_id", $articleId)
->whereNull("parent_id") // 只获取顶级评论
->with(["user", "replies.user"])
->orderBy("id", "desc")
->paginate(20);
return response()->json([
"success" => true,
"data" => $comments->items()
]);
}
/**
* 创建评论
* POST /api/v1/articles/{id}/comments
*/
public function store(Request $request, $articleId)
{
$validated = $request->validate([
"content" => "required|string|max:5000",
"parent_id" => "nullable|exists:comments,id"
]);
$comment = Comment::create([
"article_id" => $articleId,
"user_id" => auth()->id(),
"content" => $validated["content"],
"parent_id" => $validated["parent_id"] ?? null
]);
return response()->json([
"success" => true,
"data" => $comment->load("user")
], 201);
}
}
API测试方法
1. 使用curl测试
# 获取游戏列表
curl https://1000y.chencunli.com/api/v1/games
# 获取游戏详情
curl https://1000y.chencunli.com/api/v1/games/qian-mmorpg
# 获取主推游戏
curl https://1000y.chencunli.com/api/v1/games/primary
2. 使用Postman
- 导入API集合
- 配置环境变量
- 设置认证Token(如需要)
3. 验证端点
访问:https://1000y.chencunli.com/test-db-read-write
读写分离配置
database.php配置
// config/database.php
"mysql" => [
"read" => [
"host" => [
"101.201.48.221", // 主库2
"8.130.67.202" // 主库3
]
],
"write" => [
"host" => [
"localhost" // 主库1
]
],
"sticky" => true, // 写后读从主库
"driver" => "mysql",
"database" => "1000y",
"username" => "root",
"password" => "Zwk)k_p7E0Jh",
// ... 其他配置
]
常见问题FAQ
Q1: 路由不生效?
回过头看,A: 清除路由缓存:
php artisan route:clear
php artisan route:cache
Q2: API返回404?
A: 检查路由顺序和slug参数:
// 查看所有路由
php artisan route:list --path=games
Q3: Eloquent查询返回null?
A: 使用Eloquent模型而非DB查询:
// ✅ 使用Eloquent
$game = Game::where("slug", $slug)->first();
// ❌ 避免使用DB(特别是在EIP环境下)
$game = DB::table("games")->where("slug", $slug)->first();
最后一些建议
说说我自己的经历和看法:本文介绍了Laravel游戏论坛API的完整开发过程:
- 路由设计:89个API端点,路由排序规则
- 数据库架构:64张表,关联关系设计
- 核心功能:游戏列表、文章详情、评论系统
- 读写分离:HyperDB + MySQL主从复制