在 Laravel 中實作「文章點閱率統計」

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

  1. 記錄資料:client IP、host、user agent(瀏覽器資訊)、文章 ID、點擊時間。
  2. 同一篇文章、同一 IP、同一天只記一次點閱
  3. 可擴充作為 log 或統計用途。
image 9

在控制器記錄的方式

資料表設計

建立一個 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 方式

Middleware 提供了一個機制,可檢驗與過濾進入應用程式的 HTTP Request。舉例來說,Laravel 中包含了一個可以認證使用者是否已登入的 Middleware。若使用者未登入,該 Middleware 會將使用者重新導向回登入畫面。不過,若使用者已登入,這個 Middleware 就會讓 Request 進一步進入程式中處理。

所以可以使用這個機制來記錄使用者是否有點閱文章。接下來要作的功能大約有下列幾項:

  • 可用於 網頁瀏覽(web controller)
  • 可用於 API 請求(API controller)
  • 使用 Middleware 避免重複記錄
  • 儲存內容:文章 ID、IP、host、user agent、日期(同一天只算一次)

Middleware: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);
    }
}

註冊 Middleware

bootstrap/app.php 加入自定義 middleware:

->withMiddleware(function (Middleware $middleware) {        
        $middleware->alias([            
            'log.blogpost.view' => \App\Http\Middleware\LogBlogPostView::class,
        ]);
    })

Web Controller 範例

// 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'));
}

API Controller 範例

// 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,
    ]);
}

這樣就可以達成相同的結果了

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *


內容索引