© Khmer Angkor Academy - sophearithput168

Firebase

Firebase Integration

Firebase គឺជា Backend-as-a-Service (BaaS) platform ពី Google។ វាផ្តល់សេវា backend ដែលអ្នកអាចប្រើភ្លាមៗ ដោយមិនចាំបាច់សរសេរ backend code ផ្ទាល់ខ្លួន។

📚 Firebase Architecture Theory

Firebase Ecosystem:

Firebase Platform (15+ Services)

📱 AUTHENTICATION
   - Email/Password
   - Google Sign-In
   - Phone Auth
   - Anonymous Auth

�️ DATABASES
   - Firestore (NoSQL, real-time)
   - Realtime Database (JSON tree)

💾 STORAGE
   - Cloud Storage (files, images, videos)

☁️ CLOUD FUNCTIONS
   - Serverless backend code

📨 MESSAGING
   - Cloud Messaging (Push notifications)
   - In-App Messaging

📊 ANALYTICS
   - User behavior tracking
   - Crash reporting (Crashlytics)

Firebase vs Traditional Backend:

Feature Firebase Traditional Backend
Setup Time ✅ Minutes ❌ Days/Weeks
Server Management ✅ No servers needed ❌ Must manage servers
Scaling ✅ Auto-scales ❌ Manual scaling
Real-time Data ✅ Built-in ❌ Need WebSockets
Cost (Small Apps) ✅ Free tier generous ❌ Server costs from start
Customization ⚠️ Limited ✅ Full control

🔥 Firebase Services Overview

1. Firebase Authentication

Secure user authentication ជាមួយ methods ជាច្រើន៖

  • 📧 Email & Password
  • 🔍 Google Sign-In
  • 📱 Phone Number (OTP)
  • 👤 Anonymous Login
  • 📘 Facebook, Twitter, GitHub, etc.

2. Cloud Firestore (Recommended Database)

NoSQL database ដែល support real-time syncing:

  • ✅ Real-time listeners (data updates instantly)
  • ✅ Offline support (works without internet)
  • ✅ Powerful querying & filtering
  • ✅ Auto-scaling
  • ✅ Security rules for access control

3. Firebase Storage

Store and serve user-generated content:

  • 🖼️ Images, Videos
  • 📄 Documents, PDFs
  • 🎵 Audio files

4. Cloud Messaging (FCM)

Send push notifications ទៅ users:

  • 📨 Promotional messages
  • 🔔 Real-time alerts
  • 💬 Chat notifications

� Firebase Authentication Example

# pubspec.yaml
dependencies:
  firebase_core: ^2.24.0
  firebase_auth: ^4.15.0
import 'package:firebase_auth/firebase_auth.dart';

class AuthService {
  final FirebaseAuth _auth = FirebaseAuth.instance;
  
  // Get current user
  User? get currentUser => _auth.currentUser;
  
  // Auth state changes stream
  Stream<User?> get authStateChanges => _auth.authStateChanges();
  
  // Sign up with email & password
  Future<UserCredential?> signUp({
    required String email,
    required String password,
  }) async {
    try {
      final credential = await _auth.createUserWithEmailAndPassword(
        email: email,
        password: password,
      );
      return credential;
    } on FirebaseAuthException catch (e) {
      if (e.code == 'weak-password') {
        throw Exception('Password ខ្សោយពេក');
      } else if (e.code == 'email-already-in-use') {
        throw Exception('Email នេះត្រូវបានប្រើរួចហើយ');
      }
      throw Exception(e.message);
    }
  }
  
  // Sign in with email & password
  Future<UserCredential?> signIn({
    required String email,
    required String password,
  }) async {
    try {
      return await _auth.signInWithEmailAndPassword(
        email: email,
        password: password,
      );
    } on FirebaseAuthException catch (e) {
      if (e.code == 'user-not-found') {
        throw Exception('រកមិនឃើញ user');
      } else if (e.code == 'wrong-password') {
        throw Exception('Password មិនត្រឹមត្រូវ');
      }
      throw Exception(e.message);
    }
  }
  
  // Sign out
  Future<void> signOut() async {
    await _auth.signOut();
  }
  
  // Reset password
  Future<void> resetPassword(String email) async {
    await _auth.sendPasswordResetEmail(email: email);
  }
}

📝 Cloud Firestore Examples

# pubspec.yaml
dependencies:
  cloud_firestore: ^4.13.0
import 'package:cloud_firestore/cloud_firestore.dart';

// បន្ថែមទិន្នន័យ
Future<void> addUser() async {
  await FirebaseFirestore.instance.collection('users').add({
    'name': 'សុខា',
    'age': 20,
  });
}

// អានទិន្នន័យ
Stream<QuerySnapshot> getUsers() {
  return FirebaseFirestore.instance.collection('users').snapshots();
}

Complete Firestore CRUD Operations:

import 'package:cloud_firestore/cloud_firestore.dart';

class FirestoreService {
  final FirebaseFirestore _db = FirebaseFirestore.instance;
  
  // CREATE - បង្កើតទិន្នន័យថ្មី
  Future<String> addProduct(Map<String, dynamic> data) async {
    // Auto-generated ID
    final docRef = await _db.collection('products').add(data);
    return docRef.id;
    
    // Or custom ID
    // await _db.collection('products').doc('product_123').set(data);
  }
  
  // READ - អានទិន្នន័យ (Real-time listener)
  Stream<List<Map<String, dynamic>>> getProducts() {
    return _db.collection('products').snapshots().map(
      (snapshot) => snapshot.docs.map((doc) {
        return {...doc.data(), 'id': doc.id};
      }).toList(),
    );
  }
  
  // READ - អានមួយ document
  Future<Map<String, dynamic>?> getProduct(String id) async {
    final doc = await _db.collection('products').doc(id).get();
    if (doc.exists) {
      return {...doc.data()!, 'id': doc.id};
    }
    return null;
  }
  
  // UPDATE - ធ្វើបច្ចុប្បន្នភាព
  Future<void> updateProduct(String id, Map<String, dynamic> data) async {
    await _db.collection('products').doc(id).update(data);
  }
  
  // DELETE - លុប
  Future<void> deleteProduct(String id) async {
    await _db.collection('products').doc(id).delete();
  }
  
  // QUERY - ស្វែងរក & Filter
  Stream<List<Map<String, dynamic>>> getProductsByCategory(String category) {
    return _db
      .collection('products')
      .where('category', isEqualTo: category)
      .orderBy('price', descending: false)
      .limit(10)
      .snapshots()
      .map((snapshot) => snapshot.docs.map((doc) {
        return {...doc.data(), 'id': doc.id};
      }).toList());
  }
  
  // Complex Query
  Future<List<Map<String, dynamic>>> searchProducts({
    required String category,
    required double minPrice,
    required double maxPrice,
  }) async {
    final snapshot = await _db
      .collection('products')
      .where('category', isEqualTo: category)
      .where('price', isGreaterThanOrEqualTo: minPrice)
      .where('price', isLessThanOrEqualTo: maxPrice)
      .get();
    
    return snapshot.docs.map((doc) {
      return {...doc.data(), 'id': doc.id};
    }).toList();
  }
}

📸 Firebase Storage Example

# pubspec.yaml
dependencies:
  firebase_storage: ^11.5.0
  image_picker: ^1.0.4
import 'package:firebase_storage/firebase_storage.dart';
import 'package:image_picker/image_picker.dart';
import 'dart:io';

class StorageService {
  final FirebaseStorage _storage = FirebaseStorage.instance;
  
  // Upload image
  Future<String> uploadImage(File imageFile, String userId) async {
    try {
      // Create unique filename
      final fileName = DateTime.now().millisecondsSinceEpoch.toString();
      final ref = _storage.ref().child('users/' + userId + '/' + fileName);
      
      // Upload file
      final uploadTask = ref.putFile(imageFile);
      
      // Wait for upload to complete
      final snapshot = await uploadTask;
      
      // Get download URL
      final downloadUrl = await snapshot.ref.getDownloadURL();
      return downloadUrl;
    } catch (e) {
      throw Exception('Upload failed: ' + e.toString());
    }
  }
  
  // Pick and upload image
  Future<String?> pickAndUploadImage(String userId) async {
    final picker = ImagePicker();
    final pickedFile = await picker.pickImage(source: ImageSource.gallery);
    
    if (pickedFile != null) {
      final file = File(pickedFile.path);
      return await uploadImage(file, userId);
    }
    return null;
  }
  
  // Delete image
  Future<void> deleteImage(String imageUrl) async {
    try {
      final ref = _storage.refFromURL(imageUrl);
      await ref.delete();
    } catch (e) {
      throw Exception('Delete failed: ' + e.toString());
    }
  }
  
  // Get all user images
  Future<List<String>> getUserImages(String userId) async {
    final ref = _storage.ref().child('users/' + userId);
    final result = await ref.listAll();
    
    List<String> urls = [];
    for (var item in result.items) {
      final url = await item.getDownloadURL();
      urls.add(url);
    }
    return urls;
  }
}

🔔 Firebase Cloud Messaging (Push Notifications)

# pubspec.yaml
dependencies:
  firebase_messaging: ^14.7.0
import 'package:firebase_messaging/firebase_messaging.dart';

class MessagingService {
  final FirebaseMessaging _messaging = FirebaseMessaging.instance;
  
  // Initialize notifications
  Future<void> initialize() async {
    // Request permission (iOS)
    NotificationSettings settings = await _messaging.requestPermission(
      alert: true,
      badge: true,
      sound: true,
    );
    
    if (settings.authorizationStatus == AuthorizationStatus.authorized) {
      print('User granted permission');
      
      // Get FCM token
      String? token = await _messaging.getToken();
      print('FCM Token: ' + (token ?? ''));
      
      // Listen to foreground messages
      FirebaseMessaging.onMessage.listen((RemoteMessage message) {
        print('Got message: ' + (message.notification?.title ?? ''));
        // Show local notification
      });
      
      // Handle notification tap
      FirebaseMessaging.onMessageOpenedApp.listen((RemoteMessage message) {
        print('Notification tapped!');
        // Navigate to specific screen
      });
    }
  }
  
  // Subscribe to topic
  Future<void> subscribeToTopic(String topic) async {
    await _messaging.subscribeToTopic(topic);
  }
}

🔒 Firestore Security Rules

Security Rules ត្រួតពិនិត្យថាអ្នកណាអាច read/write data បាន:

// firestore.rules
rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {
    
    // Public read, authenticated write
    match /products/{productId} {
      allow read: if true;  // Anyone can read
      allow write: if request.auth != null;  // Only logged-in users
    }
    
    // Users can only access their own data
    match /users/{userId} {
      allow read, write: if request.auth != null 
                          && request.auth.uid == userId;
    }
    
    // Admin-only collection
    match /admin/{document=**} {
      allow read, write: if request.auth != null 
                          && get(/databases/$(database)/documents/users/$(request.auth.uid)).data.role == 'admin';
    }
  }
}

🎯 Real-World Example: Chat App

class ChatService {
  final FirebaseFirestore _db = FirebaseFirestore.instance;
  final FirebaseAuth _auth = FirebaseAuth.instance;
  
  // Send message
  Future<void> sendMessage(String chatId, String text) async {
    await _db.collection('chats').doc(chatId).collection('messages').add({
      'text': text,
      'senderId': _auth.currentUser!.uid,
      'senderName': _auth.currentUser!.displayName,
      'timestamp': FieldValue.serverTimestamp(),
      'isRead': false,
    });
  }
  
  // Get messages (real-time)
  Stream<List<Map<String, dynamic>>> getMessages(String chatId) {
    return _db
      .collection('chats')
      .doc(chatId)
      .collection('messages')
      .orderBy('timestamp', descending: true)
      .limit(50)
      .snapshots()
      .map((snapshot) => snapshot.docs.map((doc) {
        return {...doc.data(), 'id': doc.id};
      }).toList());
  }
  
  // Mark message as read
  Future<void> markAsRead(String chatId, String messageId) async {
    await _db
      .collection('chats')
      .doc(chatId)
      .collection('messages')
      .doc(messageId)
      .update({'isRead': true});
  }
}

💡 Best Practices:

  • ✅ Use Security Rules to protect data
  • ✅ Index fields used in queries for better performance
  • ✅ Use transactions for atomic operations
  • ✅ Implement offline persistence for better UX
  • ✅ Use batched writes for multiple operations
  • ✅ Store large files in Storage, not Firestore
  • ✅ Use subcollections for nested data

⚠️ Common Mistakes:

  • ❌ Leaving Security Rules in test mode (public access)
  • ❌ Not handling offline state properly
  • ❌ Making too many small writes (use batches)
  • ❌ Storing arrays for lists (use subcollections instead)
  • ❌ Not monitoring costs (Firestore charges per read/write)

💡 ជំនួយ: Firebase ងាយប្រើ និងមានសេវាច្រើន។ Firestore support real-time data syncing។ Security Rules ចាំបាច់សម្រាប់ការពារទិន្នន័យ។