Firebase เป็นแพลตฟอร์ม Backend-as-a-Service ที่มีบริการ Authentication, Cloud Firestore, Realtime Database, Cloud Storage, Cloud Messaging และบริการอื่น ๆ ส่วน Laravel เหมาะกับการสร้าง Web Application, REST API, ระบบหลังบ้าน และงานประมวลผลฝั่งเซิร์ฟเวอร์

การใช้ทั้งสองระบบร่วมกันทำให้ Laravel รับผิดชอบ business logic และ API ขณะที่ Firebase ช่วยจัดการ identity, NoSQL database, ไฟล์ และการแจ้งเตือนแบบ realtime

บทความนี้ใช้ PHP package kreait/laravel-firebase ซึ่งเป็น Laravel integration สำหรับ Firebase Admin PHP SDK

#1. สถาปัตยกรรมที่แนะนำ

flowchart LR
    C[Web / Mobile Client] -->|Firebase ID Token| L[Laravel API]
    L -->|Verify Token| A[Firebase Authentication]
    L -->|Admin SDK| F[Firestore / Realtime Database]
    L -->|Admin SDK| S[Cloud Storage]
    L --> D[(Laravel Database)]

แนวทางสำคัญคือ Client ขอ Firebase ID Token หลังจากผู้ใช้เข้าสู่ระบบ แล้วส่ง token ไปยัง Laravel ผ่าน Authorization: Bearer ... จากนั้น Laravel ตรวจสอบ token ด้วย Admin SDK ก่อนให้สิทธิ์เข้าถึงข้อมูล

ส่วนประกอบ หน้าที่หลัก
Firebase Client SDK Login, รับ ID Token และสื่อสารกับ Firebase จากฝั่งผู้ใช้
Laravel API, business rules, validation, authorization และการเชื่อมต่อระบบอื่น
Firebase Admin SDK ตรวจสอบ token และจัดการ Firebase จาก trusted server
Firestore ฐานข้อมูลเอกสารแบบ NoSQL ที่รองรับการ query และการขยายระบบ
Realtime Database ฐานข้อมูล JSON ที่เหมาะกับข้อมูลซึ่งต้องอัปเดตแบบ realtime
Cloud Storage จัดเก็บรูปภาพ เอกสาร และไฟล์สื่อ

ไม่ควรนำ Service Account JSON ไปไว้ใน frontend หรือ commit เข้า Git repository เพราะไฟล์ดังกล่าวมีสิทธิ์ระดับ server

#2. สิ่งที่ต้องเตรียม

  • Firebase Project
  • Laravel application ที่ทำงานได้
  • PHP และ Composer
  • Firebase Authentication ที่เปิด provider ตามต้องการ
  • Firestore หรือ Realtime Database หากต้องการเก็บข้อมูลใน Firebase
  • Cloud Storage หากต้องการอัปโหลดไฟล์
  • Service Account สำหรับการทำงานจาก Laravel

Firebase ระบุว่า Admin SDK ใช้สำหรับสภาพแวดล้อมที่มีสิทธิ์ เช่น server หรือ cloud environment และสามารถตรวจสอบ authentication token รวมถึงเข้าถึง Firestore, Realtime Database และ Cloud Storage ได้

#3. ติดตั้งแพ็กเกจ Laravel Firebase

ติดตั้งแพ็กเกจด้วย Composer:

composer require kreait/laravel-firebase

แพ็กเกจจะลงทะเบียน service provider ให้กับ Laravel และสามารถเรียกใช้งานผ่าน dependency injection, app() helper หรือ Facade ได้

เผยแพร่ไฟล์ configuration หากต้องการปรับแต่ง:

php artisan vendor:publish \
  --provider="Kreait\\Laravel\\Firebase\\ServiceProvider" \
  --tag=config

#4. ตั้งค่า Service Account

ไปที่ Firebase Console → Project settings → Service accounts แล้วสร้าง private key จากนั้นเก็บไฟล์ไว้ในตำแหน่งที่ไม่ถูกเผยแพร่ เช่น storage/app/firebase/ และเพิ่มลงใน .gitignore

ตัวอย่าง .env:

FIREBASE_CREDENTIALS=storage/app/firebase/firebase-service-account.json
FIREBASE_DATABASE_URL=https://your-project-default-rtdb.firebaseio.com

หรือใช้ Application Default Credentials:

GOOGLE_APPLICATION_CREDENTIALS=/absolute/path/to/firebase-service-account.json

ตัวอย่าง .gitignore:

storage/app/firebase/*.json
.env

หลังแก้ .env ให้ล้าง configuration cache:

php artisan config:clear
php artisan cache:clear

ใน production ที่ทำงานบน Google Cloud ควรพิจารณาใช้ Application Default Credentials หรือ workload identity ตามสภาพแวดล้อม แทนการฝัง private key ไว้ใน source code

#5. เรียกใช้ Firebase Service

ตัวอย่าง Controller ที่เรียก Firebase Facade:

<?php

namespace App\\Http\\Controllers;

use Illuminate\\Http\\JsonResponse;
use Kreait\\Laravel\\Firebase\\Facades\\Firebase;

class FirebaseHealthController extends Controller
{
    public function __invoke(): JsonResponse
    {
        $auth = Firebase::auth();

        return response()->json([
            'firebase' => 'connected',
            'auth_service' => $auth::class,
        ]);
    }
}

เพิ่ม route:

use App\\Http\\Controllers\\FirebaseHealthController;

Route::get('/firebase/health', FirebaseHealthController::class);

สำหรับโค้ดที่ต้องทดสอบง่าย แนะนำ dependency injection ผ่าน service class แทนการเรียก Facade ทุกจุด

#6. Firebase Authentication กับ Laravel API

#6.1 ตรวจสอบ Firebase ID Token

สร้าง middleware:

php artisan make:middleware VerifyFirebaseToken

ตัวอย่าง middleware:

<?php

namespace App\\Http\\Middleware;

use Closure;
use Illuminate\\Http\\Request;
use Kreait\\Firebase\\Exception\\Auth\\FailedToVerifyToken;
use Kreait\\Laravel\\Firebase\\Facades\\Firebase;

class VerifyFirebaseToken
{
    public function handle(Request $request, Closure $next)
    {
        $token = $request->bearerToken();

        if (!$token) {
            return response()->json([
                'message' => 'Missing Firebase ID token',
            ], 401);
        }

        try {
            $verifiedIdToken = Firebase::auth()->verifyIdToken($token);
            $request->attributes->set('firebase_token', $verifiedIdToken);
            $request->attributes->set('firebase_uid', $verifiedIdToken->claims()->get('sub'));
        } catch (FailedToVerifyToken $exception) {
            return response()->json([
                'message' => 'Invalid Firebase ID token',
            ], 401);
        }

        return $next($request);
    }
}

ผูก middleware กับ route:

Route::middleware('firebase.auth')->group(function () {
    Route::get('/me', function (Request $request) {
        return response()->json([
            'uid' => $request->attributes->get('firebase_uid'),
        ]);
    });
});

ชื่อ middleware ต้องลงทะเบียนให้ตรงกับ Laravel version ที่ใช้งาน เช่นใน bootstrap/app.php หรือ app/Http/Kernel.php

#6.2 เชื่อม Firebase user กับตาราง users ของ Laravel

ระบบจริงมักเก็บข้อมูลผู้ใช้ที่เกี่ยวกับ business ไว้ในฐานข้อมูล Laravel และใช้ Firebase เป็น identity provider:

$uid = $request->attributes->get('firebase_uid');

$user = User::updateOrCreate(
    ['firebase_uid' => $uid],
    [
        'email' => $verifiedIdToken->claims()->get('email'),
        'name' => $verifiedIdToken->claims()->get('name', 'ผู้ใช้ Firebase'),
    ]
);

ควรเพิ่ม unique index ให้ firebase_uid:

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

ข้อดีของแนวทางนี้คือ Firebase จัดการ identity ส่วน Laravel ยังคงใช้ Eloquent, Policy, Role และ business transaction ได้เต็มที่

#7. Cloud Firestore

#7.1 เขียนเอกสาร

use Illuminate\\Http\\Request;
use Kreait\\Laravel\\Firebase\\Facades\\Firebase;

public function store(Request $request)
{
    $data = $request->validate([
        'title' => ['required', 'string', 'max:255'],
        'content' => ['required', 'string'],
    ]);

    $firestore = Firebase::firestore();
    $database = $firestore->database();

    $document = $database->collection('posts')->add([
        'title' => $data['title'],
        'content' => $data['content'],
        'created_at' => now()->toIso8601String(),
    ]);

    return response()->json([
        'id' => $document->id(),
    ], 201);
}

#7.2 อ่านเอกสาร

public function show(string $id)
{
    $document = Firebase::firestore()
        ->database()
        ->collection('posts')
        ->document($id)
        ->snapshot();

    if (!$document->exists()) {
        return response()->json(['message' => 'Post not found'], 404);
    }

    return response()->json([
        'id' => $document->id(),
        ...$document->data(),
    ]);
}

#7.3 Query เอกสาร

$documents = Firebase::firestore()
    ->database()
    ->collection('posts')
    ->where('status', '=', 'published')
    ->orderBy('created_at', 'DESC')
    ->limit(20)
    ->documents();

$posts = [];

foreach ($documents as $document) {
    if ($document->exists()) {
        $posts[] = [
            'id' => $document->id(),
            ...$document->data(),
        ];
    }
}

ควรวางแผน index ของ Firestore ตั้งแต่ต้น โดยเฉพาะ query ที่ใช้ filter และ order หลาย field

#8. Realtime Database

Realtime Database เหมาะกับโครงสร้างข้อมูล JSON และ use case ที่ต้องอ่านค่าปัจจุบันอย่างรวดเร็ว เช่น presence, สถานะอุปกรณ์ หรือ dashboard แบบ realtime

เขียนข้อมูล:

$database = Firebase::database();

$database
    ->getReference('rooms/room-001/messages/msg-001')
    ->set([
        'text' => 'สวัสดีจาก Laravel',
        'sender_id' => $request->user()->id,
        'created_at' => now()->timestamp,
    ]);

อ่านข้อมูล:

$value = Firebase::database()
    ->getReference('rooms/room-001/messages')
    ->getValue();

return response()->json($value ?? []);

ควรกำหนดโครงสร้าง path ให้สัมพันธ์กับ tenant หรือ user และหลีกเลี่ยงการเก็บข้อมูลทั้งหมดไว้ที่ root เดียว

#9. Cloud Storage

ตัวอย่างอัปโหลดไฟล์จาก Laravel ไปยัง Firebase Storage:

use Illuminate\\Http\\Request;
use Kreait\\Laravel\\Firebase\\Facades\\Firebase;

public function upload(Request $request)
{
    $request->validate([
        'file' => ['required', 'file', 'max:5120', 'mimes:jpg,jpeg,png,pdf'],
    ]);

    $file = $request->file('file');
    $path = 'uploads/' . date('Y/m') . '/' . uniqid() . '-' . $file->getClientOriginalName();

    $bucket = Firebase::storage()->getBucket();
    $bucket->upload(
        fopen($file->getRealPath(), 'r'),
        ['name' => $path]
    );

    return response()->json([
        'path' => $path,
        'message' => 'Upload successful',
    ], 201);
}

ใน production ควรจัดการชื่อไฟล์เอง ไม่ใช้ชื่อไฟล์จากผู้ใช้โดยตรง และควรกำหนด MIME type, ขนาดไฟล์, antivirus scanning และอายุของ signed URL ตามความเหมาะสม

#10. Firebase Cloud Messaging จาก Laravel

สำหรับการแจ้งเตือนแบบ push ให้เก็บ FCM registration token ของอุปกรณ์ แล้วส่งข้อความผ่าน Firebase Messaging:

use Kreait\\Firebase\\Messaging\\CloudMessage;
use Kreait\\Firebase\\Messaging\\Notification;
use Kreait\\Laravel\\Firebase\\Facades\\Firebase;

$message = CloudMessage::withTarget('token', $deviceToken)
    ->withNotification(Notification::create(
        'มีข้อความใหม่',
        'คุณได้รับการแจ้งเตือนจากระบบ'
    ));

Firebase::messaging()->send($message);

งานส่ง notification จำนวนมากควรทำผ่าน Laravel Queue เพื่อไม่ให้ request หลักรอการส่งข้อความ

#11. Security Checklist

#Service Account

  • ห้าม commit JSON private key เข้า Git
  • ห้ามใส่ private key ใน JavaScript หรือ mobile application
  • ใช้ secret manager หรือ environment secret ใน production
  • จำกัดสิทธิ์ของ service account ตามหลัก least privilege
  • หมุนเวียน key และ revoke key ที่ไม่ใช้งาน

#Authentication

  • ตรวจสอบ ID Token ทุกครั้งที่รับ request ที่ต้องยืนยันตัวตน
  • ตรวจสอบ iss, aud, expiration และ project ที่ถูกต้องผ่าน SDK
  • ไม่รับ uid จาก request body มาใช้แทน uid จาก token
  • ใช้ Laravel Policy/Gate ตรวจสอบสิทธิ์ระดับ resource
  • แยก authentication ออกจาก authorization

#Database

  • กำหนด Firebase Security Rules สำหรับการเข้าถึงจาก client
  • สำหรับ Admin SDK ให้ถือว่าเป็น privileged access และตรวจสอบสิทธิ์ใน Laravel เอง
  • Validate input ก่อนเขียน Firestore หรือ Realtime Database
  • หลีกเลี่ยงการใช้ document ID ที่คาดเดาได้เมื่อข้อมูลมีความอ่อนไหว
  • ออกแบบ index และ pagination เพื่อควบคุมค่าใช้จ่ายและ latency

#File Storage

  • ตรวจสอบนามสกุล MIME และขนาดไฟล์
  • สร้าง path จาก server ไม่ใช้ path ที่ผู้ใช้ส่งมาโดยตรง
  • ใช้ signed URL ที่มีอายุจำกัดกับไฟล์ส่วนตัว
  • แยกพื้นที่เก็บไฟล์ public และ private

#12. Testing Strategy

ทดสอบอย่างน้อย 4 ระดับ:

  1. Unit test สำหรับ service ที่แปลงข้อมูลและสร้างคำสั่ง Firebase
  2. Feature test สำหรับ middleware ตรวจสอบ token และ HTTP status code
  3. Integration test กับ Firebase project แยกสำหรับทดสอบ
  4. Security test สำหรับ token หมดอายุ, token ผิด project, user ไม่มีสิทธิ์ และ path ที่ไม่ควรเข้าถึง

ไม่ควรใช้ production Firebase project เป็นฐานข้อมูลสำหรับ automated test ควรแยก project หรือใช้ emulator ตามความเหมาะสม

#13. ปัญหาที่พบบ่อย

#Unable to determine the Google's Application Credentials

ตรวจสอบว่า FIREBASE_CREDENTIALS หรือ GOOGLE_APPLICATION_CREDENTIALS ชี้ไปยังไฟล์จริง และรัน php artisan config:clear

#Permission denied

ตรวจสอบ service account role, project ID, database URL และรูปแบบ path ของ Firestore/Realtime Database รวมถึงสิทธิ์ที่ตรวจสอบใน Laravel

#Token ใช้งานไม่ได้

ตรวจสอบว่า Client ส่ง Firebase ID Token ไม่ใช่ refresh token หรือ access token ของ provider อื่น และส่งผ่าน header รูปแบบนี้:

Authorization: Bearer <FIREBASE_ID_TOKEN>

#ข้อมูลใน Firestore ไม่ตรงชนิด

Firestore มีชนิดข้อมูลเฉพาะ เช่น timestamp, reference และ array ควรกำหนด data contract ให้ชัดเจน และแปลงข้อมูลก่อนส่งออกเป็น JSON

#14. สรุปแนวทางเลือกบริการ

ความต้องการ บริการที่เหมาะสม
Login ด้วย Google, Apple, Email หรือ Phone Firebase Authentication
ข้อมูลเอกสารและ query แบบ NoSQL Cloud Firestore
ข้อมูลสถานะที่ต้องอัปเดตเร็วและโครงสร้าง JSON Realtime Database
รูปภาพ เอกสาร และวิดีโอ Cloud Storage
Push notification Firebase Cloud Messaging
Business transaction, report, relational data Laravel Database เช่น MySQL/PostgreSQL

สถาปัตยกรรมที่ใช้งานได้ดีในหลายโครงการคือให้ Firebase ดูแล identity และความสามารถ realtime ส่วน Laravel เป็นศูนย์กลางของ domain logic, authorization, validation, queue และการเชื่อมต่อระบบภายนอก

#แหล่งอ้างอิง


บทความนี้เป็นแนวทางเริ่มต้น ควรตรวจสอบ compatibility ของ PHP, Laravel, kreait/laravel-firebase และ kreait/firebase-php ให้ตรงกับเวอร์ชันในโครงการก่อนติดตั้งจริง