Physical Address
304 North Cardinal St.
Dorchester Center, MA 02124
Physical Address
304 North Cardinal St.
Dorchester Center, MA 02124

在 Laravel 中實作「文章點閱率統計」,要達成以下條件:

建立一個 blog_views 表記錄點閱資訊,先建立 migration table: create_blog_views_table.php
php artisan make:migration create_blog_views_table然後在檔案: xxxx_xx_xx_create_blog_views_table.php 內加入下列程式碼
// database/migrations/xxxx_xx_xx_create_blog_views_table.php
Schema::create('blog_views', function (Blueprint $table) {
$table->id();
$table->foreignId('post_id')->constrained('posts')->onDelete('cascade');
$table->string('ip', 45);
$table->string('host')->nullable();
$table->string('user_agent')->nullable();
$table->date('view_date');
$table->timestamps();
$table->unique(['post_id', 'ip', 'view_date']); // 限制同一天只記一次
});
建立 Model:BlogView
// app/Models/BlogView.php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class BlogView extends Model
{
protected $fillable = ['post_id', 'ip', 'host', 'user_agent', 'view_date'];
public $timestamps = true;
}
在 PostController@show() 或你顯示文章的地方加入:
use Illuminate\Http\Request;
use App\Models\PostView;
use Carbon\Carbon;
public function show(Request $request, $id)
{
$post = Post::findOrFail($id);
$ip = $request->ip();
$host = gethostbyaddr($ip) ?? 'unknown';
$userAgent = $request->header('User-Agent') ?? 'unknown';
$today = Carbon::now()->toDateString();
// 檢查今天是否已經記錄過
$alreadyViewed = BlogView::where('post_id', $id)
->where('ip', $ip)
->whereDate('view_date', $today)
->exists();
if (!$alreadyViewed) {
BlogView::create([
'post_id' => $id,
'ip' => $ip,
'host' => $host,
'user_agent' => $userAgent,
'view_date' => $today,
]);
// 可選:更新文章總點閱數
$post->increment('view_count');
}
return view('posts.show', compact('post'));
}view_count 欄位需預先在 posts 表中建立,若要省略則僅依 post_views 表做統計。
若不想用 posts.view_count 欄位,可以用 SQL 統計:
$totalViews = BlogView::where('post_id', $id)->count();Middleware 提供了一個機制,可檢驗與過濾進入應用程式的 HTTP Request。舉例來說,Laravel 中包含了一個可以認證使用者是否已登入的 Middleware。若使用者未登入,該 Middleware 會將使用者重新導向回登入畫面。不過,若使用者已登入,這個 Middleware 就會讓 Request 進一步進入程式中處理。
所以可以使用這個機制來記錄使用者是否有點閱文章。接下來要作的功能大約有下列幾項:
LogBlobPostView建立 middle 指令
php artisan make:middleware LogBlogPostView在檔案 LogBlobPostView 加入程式碼
// app/Http/Middleware/LogBlogPostView.php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
use App\Models\BlogView;
use Carbon\Carbon;
class LogBlogPostView
{
public function handle(Request $request, Closure $next)
{
$postId = $request->route('id') ?? $request->route('post'); // 支援不同路由命名
if ($postId) {
$ip = $request->ip();
$host = gethostbyaddr($ip) ?? 'unknown';
$userAgent = $request->header('User-Agent') ?? 'unknown';
$today = Carbon::now()->toDateString();
$exists = BlogView::where('post_id', $postId)
->where('ip', $ip)
->whereDate('view_date', $today)
->exists();
if (!$exists) {
BlogView::create([
'post_id' => $postId,
'ip' => $ip,
'host' => $host,
'user_agent' => $userAgent,
'view_date' => $today,
]);
}
}
return $next($request);
}
}
在 bootstrap/app.php 加入自定義 middleware:
->withMiddleware(function (Middleware $middleware) {
$middleware->alias([
'log.blogpost.view' => \App\Http\Middleware\LogBlogPostView::class,
]);
})
// routes/web.php
Route::get('/posts/{id}', [PostController::class, 'show'])
->middleware('log.blogpost.view');
// app/Http/Controllers/PostController.php
public function show($id)
{
$post = Post::findOrFail($id);
return view('posts.show', compact('post'));
}// routes/api.php
Route::get('/posts/{post}', [Api\PostController::class, 'show'])
->middleware('log.blogpost.view');
// app/Http/Controllers/Api/PostController.php
public function show($postId)
{
$post = Post::findOrFail($postId);
return response()->json([
'id' => $post->id,
'title' => $post->title,
'content' => $post->content,
]);
}這樣就可以達成相同的結果了